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