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