Line data Source code
1 : /* src/interfaces/ecpg/ecpglib/prepare.c */
2 :
3 : #define POSTGRES_ECPG_INTERNAL
4 : #include "postgres_fe.h"
5 :
6 : #include <ctype.h>
7 :
8 : #include "ecpgerrno.h"
9 : #include "ecpglib.h"
10 : #include "ecpglib_extern.h"
11 : #include "ecpgtype.h"
12 : #include "sqlca.h"
13 :
14 : #define STMTID_SIZE 32
15 :
16 : /*
17 : * The statement cache contains stmtCacheNBuckets hash buckets, each
18 : * having stmtCacheEntPerBucket entries, which we recycle as needed,
19 : * giving up the least-executed entry in the bucket.
20 : * stmtCacheEntries[0] is never used, so that zero can be a "not found"
21 : * indicator.
22 : */
23 : #define stmtCacheNBuckets 2039 /* should be a prime number */
24 : #define stmtCacheEntPerBucket 8
25 :
26 : #define stmtCacheArraySize (stmtCacheNBuckets * stmtCacheEntPerBucket + 1)
27 :
28 : typedef struct
29 : {
30 : int lineno;
31 : char stmtID[STMTID_SIZE];
32 : char *ecpgQuery;
33 : long execs; /* # of executions */
34 : const char *connection; /* connection for the statement */
35 : } stmtCacheEntry;
36 :
37 : static int nextStmtID = 1;
38 : static stmtCacheEntry *stmtCacheEntries = NULL;
39 :
40 : static bool deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
41 : struct prepared_statement *prev, struct prepared_statement *this);
42 :
43 : static bool
44 0 : isvarchar(unsigned char c)
45 : {
46 0 : if (isalnum(c))
47 0 : return true;
48 :
49 0 : if (c == '_' || c == '>' || c == '-' || c == '.')
50 0 : return true;
51 :
52 0 : if (c >= 128)
53 0 : return true;
54 :
55 0 : return false;
56 0 : }
57 :
58 : bool
59 0 : ecpg_register_prepared_stmt(struct statement *stmt)
60 : {
61 0 : struct statement *prep_stmt;
62 0 : struct prepared_statement *this;
63 0 : struct connection *con = stmt->connection;
64 0 : struct prepared_statement *prev = NULL;
65 0 : int lineno = stmt->lineno;
66 :
67 : /* check if we already have prepared this statement */
68 0 : this = ecpg_find_prepared_statement(stmt->name, con, &prev);
69 0 : if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
70 0 : return false;
71 :
72 : /* allocate new statement */
73 0 : this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
74 0 : if (!this)
75 0 : return false;
76 :
77 0 : prep_stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
78 0 : if (!prep_stmt)
79 : {
80 0 : ecpg_free(this);
81 0 : return false;
82 : }
83 0 : memset(prep_stmt, 0, sizeof(struct statement));
84 :
85 : /* create statement */
86 0 : prep_stmt->lineno = lineno;
87 0 : prep_stmt->connection = con;
88 0 : prep_stmt->command = ecpg_strdup(stmt->command, lineno, NULL);
89 0 : if (!prep_stmt->command)
90 : {
91 0 : ecpg_free(prep_stmt);
92 0 : ecpg_free(this);
93 0 : return false;
94 : }
95 0 : prep_stmt->inlist = prep_stmt->outlist = NULL;
96 0 : this->name = ecpg_strdup(stmt->name, lineno, NULL);
97 0 : if (!this->name)
98 : {
99 0 : ecpg_free(prep_stmt->command);
100 0 : ecpg_free(prep_stmt);
101 0 : ecpg_free(this);
102 0 : return false;
103 : }
104 0 : this->stmt = prep_stmt;
105 0 : this->prepared = true;
106 :
107 0 : if (con->prep_stmts == NULL)
108 0 : this->next = NULL;
109 : else
110 0 : this->next = con->prep_stmts;
111 :
112 0 : con->prep_stmts = this;
113 0 : return true;
114 0 : }
115 :
116 : static bool
117 0 : replace_variables(char **text, int lineno)
118 : {
119 0 : bool string = false;
120 0 : int counter = 1,
121 0 : ptr = 0;
122 :
123 0 : for (; (*text)[ptr] != '\0'; ptr++)
124 : {
125 0 : if ((*text)[ptr] == '\'')
126 0 : string = string ? false : true;
127 :
128 0 : if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?')))
129 0 : continue;
130 :
131 0 : if (((*text)[ptr] == ':') && ((*text)[ptr + 1] == ':'))
132 0 : ptr += 2; /* skip '::' */
133 : else
134 : {
135 : /* a rough guess of the size we need: */
136 0 : int buffersize = sizeof(int) * CHAR_BIT * 10 / 3;
137 0 : int len;
138 0 : char *buffer,
139 : *newcopy;
140 :
141 0 : if (!(buffer = ecpg_alloc(buffersize, lineno)))
142 0 : return false;
143 :
144 0 : snprintf(buffer, buffersize, "$%d", counter++);
145 :
146 0 : for (len = 1; (*text)[ptr + len] && isvarchar((*text)[ptr + len]); len++)
147 : /* skip */ ;
148 0 : if (!(newcopy = ecpg_alloc(strlen(*text) - len + strlen(buffer) + 1, lineno)))
149 : {
150 0 : ecpg_free(buffer);
151 0 : return false;
152 : }
153 :
154 0 : memcpy(newcopy, *text, ptr);
155 0 : strcpy(newcopy + ptr, buffer);
156 0 : strcat(newcopy, (*text) +ptr + len);
157 :
158 0 : ecpg_free(*text);
159 0 : ecpg_free(buffer);
160 :
161 0 : *text = newcopy;
162 :
163 0 : if ((*text)[ptr] == '\0') /* we reached the end */
164 0 : ptr--; /* since we will (*text)[ptr]++ in the top
165 : * level for loop */
166 0 : }
167 0 : }
168 0 : return true;
169 0 : }
170 :
171 : static bool
172 0 : prepare_common(int lineno, struct connection *con, const char *name, const char *variable)
173 : {
174 0 : struct statement *stmt;
175 0 : struct prepared_statement *this;
176 0 : PGresult *query;
177 :
178 : /* allocate new statement */
179 0 : this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
180 0 : if (!this)
181 0 : return false;
182 :
183 0 : stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
184 0 : if (!stmt)
185 : {
186 0 : ecpg_free(this);
187 0 : return false;
188 : }
189 :
190 : /* create statement */
191 0 : stmt->lineno = lineno;
192 0 : stmt->connection = con;
193 0 : stmt->command = ecpg_strdup(variable, lineno, NULL);
194 0 : if (!stmt->command)
195 : {
196 0 : ecpg_free(stmt);
197 0 : ecpg_free(this);
198 0 : return false;
199 : }
200 0 : stmt->inlist = stmt->outlist = NULL;
201 :
202 : /* if we have C variables in our statement replace them with '?' */
203 0 : if (!replace_variables(&(stmt->command), lineno))
204 : {
205 0 : ecpg_free(stmt->command);
206 0 : ecpg_free(stmt);
207 0 : ecpg_free(this);
208 0 : return false;
209 : }
210 :
211 : /* add prepared statement to our list */
212 0 : this->name = ecpg_strdup(name, lineno, NULL);
213 0 : if (!this->name)
214 : {
215 0 : ecpg_free(stmt->command);
216 0 : ecpg_free(stmt);
217 0 : ecpg_free(this);
218 0 : return false;
219 : }
220 0 : this->stmt = stmt;
221 :
222 : /* and finally really prepare the statement */
223 0 : query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL);
224 0 : if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat))
225 : {
226 0 : ecpg_free(stmt->command);
227 0 : ecpg_free(this->name);
228 0 : ecpg_free(this);
229 0 : ecpg_free(stmt);
230 0 : return false;
231 : }
232 :
233 0 : ecpg_log("prepare_common on line %d: name %s; query: \"%s\"\n", stmt->lineno, name, stmt->command);
234 0 : PQclear(query);
235 0 : this->prepared = true;
236 :
237 0 : if (con->prep_stmts == NULL)
238 0 : this->next = NULL;
239 : else
240 0 : this->next = con->prep_stmts;
241 :
242 0 : con->prep_stmts = this;
243 0 : return true;
244 0 : }
245 :
246 : /* handle the EXEC SQL PREPARE statement */
247 : /* questionmarks is not needed but remains in there for the time being to not change the API */
248 : bool
249 0 : ECPGprepare(int lineno, const char *connection_name, const bool questionmarks,
250 : const char *name, const char *variable)
251 : {
252 0 : struct connection *con;
253 0 : struct prepared_statement *this,
254 : *prev;
255 :
256 0 : (void) questionmarks; /* quiet the compiler */
257 :
258 0 : con = ecpg_get_connection(connection_name);
259 0 : if (!ecpg_init(con, connection_name, lineno))
260 0 : return false;
261 :
262 : /* check if we already have prepared this statement */
263 0 : this = ecpg_find_prepared_statement(name, con, &prev);
264 0 : if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
265 0 : return false;
266 :
267 0 : return prepare_common(lineno, con, name, variable);
268 0 : }
269 :
270 : struct prepared_statement *
271 0 : ecpg_find_prepared_statement(const char *name,
272 : struct connection *con, struct prepared_statement **prev_)
273 : {
274 0 : struct prepared_statement *this,
275 : *prev;
276 :
277 0 : for (this = con->prep_stmts, prev = NULL;
278 0 : this != NULL;
279 0 : prev = this, this = this->next)
280 : {
281 0 : if (strcmp(this->name, name) == 0)
282 : {
283 0 : if (prev_)
284 0 : *prev_ = prev;
285 0 : return this;
286 : }
287 0 : }
288 0 : return NULL;
289 0 : }
290 :
291 : static bool
292 0 : deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
293 : struct prepared_statement *prev, struct prepared_statement *this)
294 : {
295 0 : bool r = false;
296 :
297 0 : ecpg_log("deallocate_one on line %d: name %s\n", lineno, this->name);
298 :
299 : /* first deallocate the statement in the backend */
300 0 : if (this->prepared)
301 : {
302 0 : char *text;
303 0 : PGresult *query;
304 :
305 0 : text = ecpg_alloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);
306 :
307 0 : if (text)
308 : {
309 0 : sprintf(text, "deallocate \"%s\"", this->name);
310 0 : query = PQexec(this->stmt->connection->connection, text);
311 0 : ecpg_free(text);
312 0 : if (ecpg_check_PQresult(query, lineno,
313 0 : this->stmt->connection->connection,
314 0 : this->stmt->compat))
315 : {
316 0 : PQclear(query);
317 0 : r = true;
318 0 : }
319 0 : }
320 0 : }
321 :
322 : /*
323 : * Just ignore all errors since we do not know the list of cursors we are
324 : * allowed to free. We have to trust the software.
325 : */
326 0 : if (!r && !INFORMIX_MODE(c))
327 : {
328 0 : ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name);
329 0 : return false;
330 : }
331 :
332 : /* okay, free all the resources */
333 0 : ecpg_free(this->stmt->command);
334 0 : ecpg_free(this->stmt);
335 0 : ecpg_free(this->name);
336 0 : if (prev != NULL)
337 0 : prev->next = this->next;
338 : else
339 0 : con->prep_stmts = this->next;
340 :
341 0 : ecpg_free(this);
342 0 : return true;
343 0 : }
344 :
345 : /* handle the EXEC SQL DEALLOCATE PREPARE statement */
346 : bool
347 0 : ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name)
348 : {
349 0 : struct connection *con;
350 0 : struct prepared_statement *this,
351 : *prev;
352 :
353 0 : con = ecpg_get_connection(connection_name);
354 0 : if (!ecpg_init(con, connection_name, lineno))
355 0 : return false;
356 :
357 0 : this = ecpg_find_prepared_statement(name, con, &prev);
358 0 : if (this)
359 0 : return deallocate_one(lineno, c, con, prev, this);
360 :
361 : /* prepared statement is not found */
362 0 : if (INFORMIX_MODE(c))
363 0 : return true;
364 0 : ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
365 0 : return false;
366 0 : }
367 :
368 : bool
369 0 : ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *con)
370 : {
371 : /* deallocate all prepared statements */
372 0 : while (con->prep_stmts)
373 : {
374 0 : if (!deallocate_one(lineno, c, con, NULL, con->prep_stmts))
375 0 : return false;
376 : }
377 :
378 0 : return true;
379 0 : }
380 :
381 : bool
382 0 : ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
383 : {
384 0 : return ecpg_deallocate_all_conn(lineno, compat,
385 0 : ecpg_get_connection(connection_name));
386 : }
387 :
388 : char *
389 0 : ecpg_prepared(const char *name, struct connection *con)
390 : {
391 0 : struct prepared_statement *this;
392 :
393 0 : this = ecpg_find_prepared_statement(name, con, NULL);
394 0 : return this ? this->stmt->command : NULL;
395 0 : }
396 :
397 : /* return the prepared statement */
398 : /* lineno is not used here, but kept in to not break API */
399 : char *
400 0 : ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
401 : {
402 0 : (void) lineno; /* keep the compiler quiet */
403 :
404 0 : return ecpg_prepared(name, ecpg_get_connection(connection_name));
405 : }
406 :
407 : /*
408 : * hash a SQL statement - returns entry # of first entry in the bucket
409 : */
410 : static int
411 0 : HashStmt(const char *ecpgQuery)
412 : {
413 0 : int stmtIx,
414 : bucketNo,
415 : hashLeng,
416 : stmtLeng;
417 0 : uint64 hashVal,
418 : rotVal;
419 :
420 0 : stmtLeng = strlen(ecpgQuery);
421 0 : hashLeng = 50; /* use 1st 50 characters of statement */
422 0 : if (hashLeng > stmtLeng) /* if the statement isn't that long */
423 0 : hashLeng = stmtLeng; /* use its actual length */
424 :
425 0 : hashVal = 0;
426 0 : for (stmtIx = 0; stmtIx < hashLeng; ++stmtIx)
427 : {
428 0 : hashVal = hashVal + (unsigned char) ecpgQuery[stmtIx];
429 : /* rotate 32-bit hash value left 13 bits */
430 0 : hashVal = hashVal << 13;
431 0 : rotVal = (hashVal & UINT64CONST(0x1fff00000000)) >> 32;
432 0 : hashVal = (hashVal & UINT64CONST(0xffffffff)) | rotVal;
433 0 : }
434 :
435 0 : bucketNo = hashVal % stmtCacheNBuckets;
436 :
437 : /* Add 1 so that array entry 0 is never used */
438 0 : return bucketNo * stmtCacheEntPerBucket + 1;
439 0 : }
440 :
441 : /*
442 : * search the statement cache - search for entry with matching ECPG-format query
443 : * Returns entry # in cache if found
444 : * OR zero if not present (zero'th entry isn't used)
445 : */
446 : static int
447 0 : SearchStmtCache(const char *ecpgQuery)
448 : {
449 0 : int entNo,
450 : entIx;
451 :
452 : /* quick failure if cache not set up */
453 0 : if (stmtCacheEntries == NULL)
454 0 : return 0;
455 :
456 : /* hash the statement */
457 0 : entNo = HashStmt(ecpgQuery);
458 :
459 : /* search the cache */
460 0 : for (entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx)
461 : {
462 0 : if (stmtCacheEntries[entNo].stmtID[0]) /* check if entry is in use */
463 : {
464 0 : if (strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery) == 0)
465 0 : break; /* found it */
466 0 : }
467 0 : ++entNo; /* incr entry # */
468 0 : }
469 :
470 : /* if entry wasn't found - set entry # to zero */
471 0 : if (entIx >= stmtCacheEntPerBucket)
472 0 : entNo = 0;
473 :
474 0 : return entNo;
475 0 : }
476 :
477 : /*
478 : * free an entry in the statement cache
479 : * Returns entry # in cache used
480 : * OR negative error code
481 : */
482 : static int
483 0 : ecpg_freeStmtCacheEntry(int lineno, int compat,
484 : int entNo) /* entry # to free */
485 : {
486 0 : stmtCacheEntry *entry;
487 0 : struct connection *con;
488 0 : struct prepared_statement *this,
489 : *prev;
490 :
491 : /* fail if cache isn't set up */
492 0 : if (stmtCacheEntries == NULL)
493 0 : return -1;
494 :
495 0 : entry = &stmtCacheEntries[entNo];
496 0 : if (!entry->stmtID[0]) /* return if the entry isn't in use */
497 0 : return 0;
498 :
499 0 : con = ecpg_get_connection(entry->connection);
500 :
501 : /* free the 'prepared_statement' list entry */
502 0 : this = ecpg_find_prepared_statement(entry->stmtID, con, &prev);
503 0 : if (this && !deallocate_one(lineno, compat, con, prev, this))
504 0 : return -1;
505 :
506 0 : entry->stmtID[0] = '\0';
507 :
508 : /* free the memory used by the cache entry */
509 0 : if (entry->ecpgQuery)
510 : {
511 0 : ecpg_free(entry->ecpgQuery);
512 0 : entry->ecpgQuery = NULL;
513 0 : }
514 :
515 0 : return entNo;
516 0 : }
517 :
518 : /*
519 : * add an entry to the statement cache
520 : * returns entry # in cache used OR negative error code
521 : */
522 : static int
523 0 : AddStmtToCache(int lineno, /* line # of statement */
524 : const char *stmtID, /* statement ID */
525 : const char *connection, /* connection */
526 : int compat, /* compatibility level */
527 : const char *ecpgQuery) /* query */
528 : {
529 0 : int ix,
530 : initEntNo,
531 : luEntNo,
532 : entNo;
533 0 : stmtCacheEntry *entry;
534 :
535 : /* allocate and zero cache array if we haven't already */
536 0 : if (stmtCacheEntries == NULL)
537 : {
538 0 : stmtCacheEntries = (stmtCacheEntry *)
539 0 : ecpg_alloc(sizeof(stmtCacheEntry) * stmtCacheArraySize, lineno);
540 0 : if (stmtCacheEntries == NULL)
541 0 : return -1;
542 0 : }
543 :
544 : /* hash the statement */
545 0 : initEntNo = HashStmt(ecpgQuery);
546 :
547 : /* search for an unused entry */
548 0 : entNo = initEntNo; /* start with the initial entry # for the
549 : * bucket */
550 0 : luEntNo = initEntNo; /* use it as the initial 'least used' entry */
551 0 : for (ix = 0; ix < stmtCacheEntPerBucket; ++ix)
552 : {
553 0 : entry = &stmtCacheEntries[entNo];
554 0 : if (!entry->stmtID[0]) /* unused entry - use it */
555 0 : break;
556 0 : if (entry->execs < stmtCacheEntries[luEntNo].execs)
557 0 : luEntNo = entNo; /* save new 'least used' entry */
558 0 : ++entNo; /* increment entry # */
559 0 : }
560 :
561 : /*
562 : * if no unused entries were found, re-use the 'least used' entry found in
563 : * the bucket
564 : */
565 0 : if (ix >= stmtCacheEntPerBucket)
566 0 : entNo = luEntNo;
567 :
568 : /* 'entNo' is the entry to use - make sure its free */
569 0 : if (ecpg_freeStmtCacheEntry(lineno, compat, entNo) < 0)
570 0 : return -1;
571 :
572 : /* add the query to the entry */
573 0 : entry = &stmtCacheEntries[entNo];
574 0 : entry->lineno = lineno;
575 0 : entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno, NULL);
576 0 : if (!entry->ecpgQuery)
577 0 : return -1;
578 0 : entry->connection = connection;
579 0 : entry->execs = 0;
580 0 : memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID));
581 :
582 0 : return entNo;
583 0 : }
584 :
585 : /* handle cache and preparation of statements in auto-prepare mode */
586 : bool
587 0 : ecpg_auto_prepare(int lineno, const char *connection_name, const int compat, char **name, const char *query)
588 : {
589 0 : int entNo;
590 :
591 : /* search the statement cache for this statement */
592 0 : entNo = SearchStmtCache(query);
593 :
594 : /* if not found - add the statement to the cache */
595 0 : if (entNo)
596 : {
597 0 : char *stmtID;
598 0 : struct connection *con;
599 0 : struct prepared_statement *prep;
600 :
601 0 : ecpg_log("ecpg_auto_prepare on line %d: statement found in cache; entry %d\n", lineno, entNo);
602 :
603 0 : stmtID = stmtCacheEntries[entNo].stmtID;
604 0 : *name = ecpg_strdup(stmtID, lineno, NULL);
605 0 : if (*name == NULL)
606 0 : return false;
607 :
608 0 : con = ecpg_get_connection(connection_name);
609 0 : prep = ecpg_find_prepared_statement(stmtID, con, NULL);
610 : /* This prepared name doesn't exist on this connection. */
611 0 : if (!prep && !prepare_common(lineno, con, stmtID, query))
612 : {
613 0 : ecpg_free(*name);
614 0 : return false;
615 : }
616 :
617 0 : }
618 : else
619 : {
620 0 : char stmtID[STMTID_SIZE];
621 :
622 0 : ecpg_log("ecpg_auto_prepare on line %d: statement not in cache; inserting\n", lineno);
623 :
624 : /* generate a statement ID */
625 0 : sprintf(stmtID, "ecpg%d", nextStmtID++);
626 0 : *name = ecpg_strdup(stmtID, lineno, NULL);
627 0 : if (*name == NULL)
628 0 : return false;
629 :
630 0 : if (!ECPGprepare(lineno, connection_name, 0, stmtID, query))
631 : {
632 0 : ecpg_free(*name);
633 0 : return false;
634 : }
635 :
636 0 : entNo = AddStmtToCache(lineno, stmtID, connection_name, compat, query);
637 0 : if (entNo < 0)
638 : {
639 0 : ecpg_free(*name);
640 0 : return false;
641 : }
642 0 : }
643 :
644 : /* increase usage counter */
645 0 : stmtCacheEntries[entNo].execs++;
646 :
647 0 : return true;
648 0 : }
|