Age Owner Branch data TLA 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
8210 meskes@postgresql.or 44 :CBC 827 : isvarchar(unsigned char c)
45 : : {
46 [ + + ]: 827 : if (isalnum(c))
47 : 3 : return true;
48 : :
49 [ + - + - : 824 : if (c == '_' || c == '>' || c == '-' || c == '.')
+ - - + ]
8210 meskes@postgresql.or 50 :UBC 0 : return true;
51 : :
8210 meskes@postgresql.or 52 [ - + ]:CBC 824 : if (c >= 128)
8210 meskes@postgresql.or 53 :UBC 0 : return true;
54 : :
2942 peter_e@gmx.net 55 :CBC 824 : return false;
56 : : }
57 : :
58 : : bool
2299 meskes@postgresql.or 59 : 5 : ecpg_register_prepared_stmt(struct statement *stmt)
60 : : {
61 : : struct statement *prep_stmt;
62 : : struct prepared_statement *this;
2178 tgl@sss.pgh.pa.us 63 : 5 : struct connection *con = stmt->connection;
2299 meskes@postgresql.or 64 : 5 : struct prepared_statement *prev = NULL;
tgl@sss.pgh.pa.us 65 : 5 : int lineno = stmt->lineno;
66 : :
67 : : /* check if we already have prepared this statement */
68 : 5 : this = ecpg_find_prepared_statement(stmt->name, con, &prev);
69 [ - + - - ]: 5 : if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
2299 tgl@sss.pgh.pa.us 70 :UBC 0 : return false;
71 : :
72 : : /* allocate new statement */
2299 tgl@sss.pgh.pa.us 73 :CBC 5 : this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
74 [ - + ]: 5 : if (!this)
2299 tgl@sss.pgh.pa.us 75 :UBC 0 : return false;
76 : :
2299 tgl@sss.pgh.pa.us 77 :CBC 5 : prep_stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
2295 78 [ - + ]: 5 : if (!prep_stmt)
79 : : {
2299 tgl@sss.pgh.pa.us 80 :UBC 0 : ecpg_free(this);
81 : 0 : return false;
82 : : }
2299 tgl@sss.pgh.pa.us 83 :CBC 5 : memset(prep_stmt, 0, sizeof(struct statement));
84 : :
85 : : /* create statement */
86 : 5 : prep_stmt->lineno = lineno;
87 : 5 : prep_stmt->connection = con;
45 michael@paquier.xyz 88 :GNC 5 : prep_stmt->command = ecpg_strdup(stmt->command, lineno, NULL);
89 [ - + ]: 5 : if (!prep_stmt->command)
90 : : {
45 michael@paquier.xyz 91 :UNC 0 : ecpg_free(prep_stmt);
92 : 0 : ecpg_free(this);
93 : 0 : return false;
94 : : }
2299 tgl@sss.pgh.pa.us 95 :CBC 5 : prep_stmt->inlist = prep_stmt->outlist = NULL;
45 michael@paquier.xyz 96 :GNC 5 : this->name = ecpg_strdup(stmt->name, lineno, NULL);
97 [ - + ]: 5 : if (!this->name)
98 : : {
45 michael@paquier.xyz 99 :UNC 0 : ecpg_free(prep_stmt->command);
100 : 0 : ecpg_free(prep_stmt);
101 : 0 : ecpg_free(this);
102 : 0 : return false;
103 : : }
2299 tgl@sss.pgh.pa.us 104 :CBC 5 : this->stmt = prep_stmt;
105 : 5 : this->prepared = true;
106 : :
107 [ - + ]: 5 : if (con->prep_stmts == NULL)
2299 tgl@sss.pgh.pa.us 108 :UBC 0 : this->next = NULL;
109 : : else
2299 tgl@sss.pgh.pa.us 110 :CBC 5 : this->next = con->prep_stmts;
111 : :
112 : 5 : con->prep_stmts = this;
113 : 5 : return true;
114 : : }
115 : :
116 : : static bool
5953 meskes@postgresql.or 117 : 855 : replace_variables(char **text, int lineno)
118 : : {
6505 bruce@momjian.us 119 : 855 : bool string = false;
120 : 855 : int counter = 1,
121 : 855 : ptr = 0;
122 : :
6598 meskes@postgresql.or 123 [ + + ]: 24201 : for (; (*text)[ptr] != '\0'; ptr++)
124 : : {
125 [ + + ]: 23346 : if ((*text)[ptr] == '\'')
8210 126 : 2 : string = string ? false : true;
127 : :
6598 128 [ + + + + : 23346 : if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?')))
+ + ]
129 : 22516 : continue;
130 : :
6505 bruce@momjian.us 131 [ + + + + ]: 830 : if (((*text)[ptr] == ':') && ((*text)[ptr + 1] == ':'))
132 : 2 : ptr += 2; /* skip '::' */
133 : : else
134 : : {
135 : : /* a rough guess of the size we need: */
2517 tgl@sss.pgh.pa.us 136 : 828 : int buffersize = sizeof(int) * CHAR_BIT * 10 / 3;
137 : : int len;
138 : : char *buffer,
139 : : *newcopy;
140 : :
6548 meskes@postgresql.or 141 [ - + ]: 828 : if (!(buffer = (char *) ecpg_alloc(buffersize, lineno)))
6598 meskes@postgresql.or 142 :UBC 0 : return false;
143 : :
6598 meskes@postgresql.or 144 :CBC 828 : snprintf(buffer, buffersize, "$%d", counter++);
145 : :
2517 tgl@sss.pgh.pa.us 146 [ + + + + ]: 831 : for (len = 1; (*text)[ptr + len] && isvarchar((*text)[ptr + len]); len++)
147 : : /* skip */ ;
1939 148 [ - + ]: 828 : if (!(newcopy = (char *) ecpg_alloc(strlen(*text) - len + strlen(buffer) + 1, lineno)))
149 : : {
6548 meskes@postgresql.or 150 :UBC 0 : ecpg_free(buffer);
6598 151 : 0 : return false;
152 : : }
153 : :
3878 tgl@sss.pgh.pa.us 154 :CBC 828 : memcpy(newcopy, *text, ptr);
6598 meskes@postgresql.or 155 : 828 : strcpy(newcopy + ptr, buffer);
6505 bruce@momjian.us 156 : 828 : strcat(newcopy, (*text) +ptr + len);
157 : :
6548 meskes@postgresql.or 158 : 828 : ecpg_free(*text);
159 : 828 : ecpg_free(buffer);
160 : :
6598 161 : 828 : *text = newcopy;
162 : :
6505 bruce@momjian.us 163 [ - + ]: 828 : if ((*text)[ptr] == '\0') /* we reached the end */
6505 bruce@momjian.us 164 :UBC 0 : ptr--; /* since we will (*text)[ptr]++ in the top
165 : : * level for loop */
166 : : }
167 : : }
6598 meskes@postgresql.or 168 :CBC 855 : return true;
169 : : }
170 : :
171 : : static bool
2999 tgl@sss.pgh.pa.us 172 : 855 : 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 */
6548 meskes@postgresql.or 179 : 855 : this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
8210 180 [ - + ]: 855 : if (!this)
8210 meskes@postgresql.or 181 :UBC 0 : return false;
182 : :
6548 meskes@postgresql.or 183 :CBC 855 : stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
8210 184 [ - + ]: 855 : if (!stmt)
185 : : {
6548 meskes@postgresql.or 186 :UBC 0 : ecpg_free(this);
8210 187 : 0 : return false;
188 : : }
189 : :
190 : : /* create statement */
8210 meskes@postgresql.or 191 :CBC 855 : stmt->lineno = lineno;
6555 192 : 855 : stmt->connection = con;
45 michael@paquier.xyz 193 :GNC 855 : stmt->command = ecpg_strdup(variable, lineno, NULL);
194 [ - + ]: 855 : if (!stmt->command)
195 : : {
45 michael@paquier.xyz 196 :UNC 0 : ecpg_free(stmt);
197 : 0 : ecpg_free(this);
198 : 0 : return false;
199 : : }
8210 meskes@postgresql.or 200 :CBC 855 : stmt->inlist = stmt->outlist = NULL;
201 : :
202 : : /* if we have C variables in our statement replace them with '?' */
5953 203 : 855 : replace_variables(&(stmt->command), lineno);
204 : :
205 : : /* add prepared statement to our list */
45 michael@paquier.xyz 206 :GNC 855 : this->name = ecpg_strdup(name, lineno, NULL);
207 [ - + ]: 855 : if (!this->name)
208 : : {
45 michael@paquier.xyz 209 :UNC 0 : ecpg_free(stmt->command);
210 : 0 : ecpg_free(stmt);
211 : 0 : ecpg_free(this);
212 : 0 : return false;
213 : : }
8210 meskes@postgresql.or 214 :CBC 855 : this->stmt = stmt;
215 : :
216 : : /* and finally really prepare the statement */
6598 217 : 855 : query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL);
6548 218 [ - + ]: 855 : if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat))
219 : : {
6548 meskes@postgresql.or 220 :UBC 0 : ecpg_free(stmt->command);
5805 221 : 0 : ecpg_free(this->name);
6548 222 : 0 : ecpg_free(this);
223 : 0 : ecpg_free(stmt);
6598 224 : 0 : return false;
225 : : }
226 : :
5648 meskes@postgresql.or 227 :CBC 855 : ecpg_log("prepare_common on line %d: name %s; query: \"%s\"\n", stmt->lineno, name, stmt->command);
6598 228 : 855 : PQclear(query);
229 : 855 : this->prepared = true;
230 : :
6555 231 [ + + ]: 855 : if (con->prep_stmts == NULL)
8210 232 : 829 : this->next = NULL;
233 : : else
6555 234 : 26 : this->next = con->prep_stmts;
235 : :
236 : 855 : con->prep_stmts = this;
8210 237 : 855 : 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
2517 tgl@sss.pgh.pa.us 243 : 852 : 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 : :
2178 252 : 852 : con = ecpg_get_connection(connection_name);
253 [ - + ]: 852 : if (!ecpg_init(con, connection_name, lineno))
5706 meskes@postgresql.or 254 :UBC 0 : return false;
255 : :
256 : : /* check if we already have prepared this statement */
5706 meskes@postgresql.or 257 :CBC 852 : this = ecpg_find_prepared_statement(name, con, &prev);
258 [ + + - + ]: 852 : if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
5706 meskes@postgresql.or 259 :UBC 0 : return false;
260 : :
5422 meskes@postgresql.or 261 :CBC 852 : return prepare_common(lineno, con, name, variable);
262 : : }
263 : :
264 : : struct prepared_statement *
5713 265 : 1789 : ecpg_find_prepared_statement(const char *name,
266 : : struct connection *con, struct prepared_statement **prev_)
267 : : {
268 : : struct prepared_statement *this,
269 : : *prev;
270 : :
2517 tgl@sss.pgh.pa.us 271 : 1789 : for (this = con->prep_stmts, prev = NULL;
272 [ + + ]: 1856 : this != NULL;
273 : 67 : prev = this, this = this->next)
274 : : {
6555 meskes@postgresql.or 275 [ + + ]: 1782 : if (strcmp(this->name, name) == 0)
276 : : {
277 [ + + ]: 1715 : if (prev_)
278 : 839 : *prev_ = prev;
279 : 1715 : return this;
280 : : }
281 : : }
282 : 74 : return NULL;
283 : : }
284 : :
285 : : static bool
2517 tgl@sss.pgh.pa.us 286 : 858 : deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
287 : : struct prepared_statement *prev, struct prepared_statement *this)
288 : : {
6505 bruce@momjian.us 289 : 858 : bool r = false;
290 : :
5648 meskes@postgresql.or 291 : 858 : ecpg_log("deallocate_one on line %d: name %s\n", lineno, this->name);
292 : :
293 : : /* first deallocate the statement in the backend */
6555 294 [ + - ]: 858 : if (this->prepared)
295 : : {
296 : : char *text;
297 : : PGresult *query;
298 : :
6548 299 : 858 : text = (char *) ecpg_alloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);
300 : :
6555 301 [ + - ]: 858 : if (text)
302 : : {
303 : 858 : sprintf(text, "deallocate \"%s\"", this->name);
304 : 858 : query = PQexec(this->stmt->connection->connection, text);
6548 305 : 858 : ecpg_free(text);
2517 tgl@sss.pgh.pa.us 306 [ + - ]: 858 : if (ecpg_check_PQresult(query, lineno,
307 : 858 : this->stmt->connection->connection,
308 : 858 : this->stmt->compat))
309 : : {
6598 meskes@postgresql.or 310 : 858 : PQclear(query);
6555 311 : 858 : 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 [ - + - - : 858 : if (!r && !INFORMIX_MODE(c))
- - ]
321 : : {
6548 meskes@postgresql.or 322 :UBC 0 : ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name);
6555 323 : 0 : return false;
324 : : }
325 : :
326 : : /* okay, free all the resources */
6548 meskes@postgresql.or 327 :CBC 858 : ecpg_free(this->stmt->command);
328 : 858 : ecpg_free(this->stmt);
5805 329 : 858 : ecpg_free(this->name);
6555 330 [ + + ]: 858 : if (prev != NULL)
331 : 3 : prev->next = this->next;
332 : : else
333 : 855 : con->prep_stmts = this->next;
334 : :
6548 335 : 858 : ecpg_free(this);
6555 336 : 858 : return true;
337 : : }
338 : :
339 : : /* handle the EXEC SQL DEALLOCATE PREPARE statement */
340 : : bool
341 : 53 : 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 : :
2178 tgl@sss.pgh.pa.us 347 : 53 : con = ecpg_get_connection(connection_name);
348 [ - + ]: 53 : if (!ecpg_init(con, connection_name, lineno))
6326 meskes@postgresql.or 349 :UBC 0 : return false;
350 : :
5713 meskes@postgresql.or 351 :CBC 53 : this = ecpg_find_prepared_statement(name, con, &prev);
6555 352 [ + + ]: 53 : if (this)
353 : 52 : return deallocate_one(lineno, c, con, prev, this);
354 : :
355 : : /* prepared statement is not found */
356 [ + - - + ]: 1 : if (INFORMIX_MODE(c))
6555 meskes@postgresql.or 357 :UBC 0 : return true;
6548 meskes@postgresql.or 358 :CBC 1 : ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
6555 359 : 1 : return false;
360 : : }
361 : :
362 : : bool
2999 tgl@sss.pgh.pa.us 363 : 124 : ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *con)
364 : : {
365 : : /* deallocate all prepared statements */
6555 meskes@postgresql.or 366 [ + + ]: 143 : while (con->prep_stmts)
367 : : {
6551 368 [ - + ]: 19 : if (!deallocate_one(lineno, c, con, NULL, con->prep_stmts))
8210 meskes@postgresql.or 369 :UBC 0 : return false;
370 : : }
371 : :
8210 meskes@postgresql.or 372 :CBC 124 : return true;
373 : : }
374 : :
375 : : bool
6551 376 : 1 : ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
377 : : {
2517 tgl@sss.pgh.pa.us 378 : 1 : return ecpg_deallocate_all_conn(lineno, compat,
379 : : ecpg_get_connection(connection_name));
380 : : }
381 : :
382 : : char *
2999 383 : 853 : ecpg_prepared(const char *name, struct connection *con)
384 : : {
385 : : struct prepared_statement *this;
386 : :
5713 meskes@postgresql.or 387 : 853 : this = ecpg_find_prepared_statement(name, con, NULL);
6555 388 [ + - ]: 853 : 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 : 21 : ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
395 : : {
396 : : (void) lineno; /* keep the compiler quiet */
397 : :
2178 tgl@sss.pgh.pa.us 398 : 21 : 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
6598 meskes@postgresql.or 405 : 10 : HashStmt(const char *ecpgQuery)
406 : : {
407 : : int stmtIx,
408 : : bucketNo,
409 : : hashLeng,
410 : : stmtLeng;
411 : : uint64 hashVal,
412 : : rotVal;
413 : :
6505 bruce@momjian.us 414 : 10 : stmtLeng = strlen(ecpgQuery);
2517 tgl@sss.pgh.pa.us 415 : 10 : hashLeng = 50; /* use 1st 50 characters of statement */
416 [ + - ]: 10 : if (hashLeng > stmtLeng) /* if the statement isn't that long */
417 : 10 : hashLeng = stmtLeng; /* use its actual length */
418 : :
6505 bruce@momjian.us 419 : 10 : hashVal = 0;
420 [ + + ]: 371 : for (stmtIx = 0; stmtIx < hashLeng; ++stmtIx)
421 : : {
2516 tgl@sss.pgh.pa.us 422 : 361 : hashVal = hashVal + (unsigned char) ecpgQuery[stmtIx];
423 : : /* rotate 32-bit hash value left 13 bits */
6505 bruce@momjian.us 424 : 361 : hashVal = hashVal << 13;
2516 tgl@sss.pgh.pa.us 425 : 361 : rotVal = (hashVal & UINT64CONST(0x1fff00000000)) >> 32;
426 : 361 : hashVal = (hashVal & UINT64CONST(0xffffffff)) | rotVal;
427 : : }
428 : :
6505 bruce@momjian.us 429 : 10 : bucketNo = hashVal % stmtCacheNBuckets;
430 : :
431 : : /* Add 1 so that array entry 0 is never used */
2516 tgl@sss.pgh.pa.us 432 : 10 : 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
6598 meskes@postgresql.or 441 : 8 : SearchStmtCache(const char *ecpgQuery)
442 : : {
443 : : int entNo,
444 : : entIx;
445 : :
446 : : /* quick failure if cache not set up */
2516 tgl@sss.pgh.pa.us 447 [ + + ]: 8 : if (stmtCacheEntries == NULL)
448 : 1 : return 0;
449 : :
450 : : /* hash the statement */
6505 bruce@momjian.us 451 : 7 : entNo = HashStmt(ecpgQuery);
452 : :
453 : : /* search the cache */
454 [ + + ]: 23 : for (entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx)
455 : : {
2517 tgl@sss.pgh.pa.us 456 [ + + ]: 21 : if (stmtCacheEntries[entNo].stmtID[0]) /* check if entry is in use */
457 : : {
5002 peter_e@gmx.net 458 [ + - ]: 5 : if (strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery) == 0)
2517 tgl@sss.pgh.pa.us 459 : 5 : break; /* found it */
460 : : }
461 : 16 : ++entNo; /* incr entry # */
462 : : }
463 : :
464 : : /* if entry wasn't found - set entry # to zero */
6505 bruce@momjian.us 465 [ + + ]: 7 : if (entIx >= stmtCacheEntPerBucket)
466 : 2 : entNo = 0;
467 : :
2942 peter_e@gmx.net 468 : 7 : 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
2517 tgl@sss.pgh.pa.us 477 : 3 : 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 */
2516 486 [ - + ]: 3 : if (stmtCacheEntries == NULL)
2516 tgl@sss.pgh.pa.us 487 :UBC 0 : return -1;
488 : :
6505 bruce@momjian.us 489 :CBC 3 : entry = &stmtCacheEntries[entNo];
2517 tgl@sss.pgh.pa.us 490 [ + - ]: 3 : if (!entry->stmtID[0]) /* return if the entry isn't in use */
2942 peter_e@gmx.net 491 : 3 : return 0;
492 : :
6505 bruce@momjian.us 493 :UBC 0 : con = ecpg_get_connection(entry->connection);
494 : :
495 : : /* free the 'prepared_statement' list entry */
5713 meskes@postgresql.or 496 : 0 : this = ecpg_find_prepared_statement(entry->stmtID, con, &prev);
6421 497 [ # # # # ]: 0 : if (this && !deallocate_one(lineno, compat, con, prev, this))
2942 peter_e@gmx.net 498 : 0 : return -1;
499 : :
6505 bruce@momjian.us 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 : :
2942 peter_e@gmx.net 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
2517 tgl@sss.pgh.pa.us 517 :CBC 3 : 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 */
2516 530 [ + + ]: 3 : if (stmtCacheEntries == NULL)
531 : : {
532 : 1 : stmtCacheEntries = (stmtCacheEntry *)
533 : 1 : ecpg_alloc(sizeof(stmtCacheEntry) * stmtCacheArraySize, lineno);
534 [ - + ]: 1 : if (stmtCacheEntries == NULL)
2516 tgl@sss.pgh.pa.us 535 :UBC 0 : return -1;
536 : : }
537 : :
538 : : /* hash the statement */
6505 bruce@momjian.us 539 :CBC 3 : initEntNo = HashStmt(ecpgQuery);
540 : :
541 : : /* search for an unused entry */
542 : 3 : entNo = initEntNo; /* start with the initial entry # for the
543 : : * bucket */
2517 tgl@sss.pgh.pa.us 544 : 3 : luEntNo = initEntNo; /* use it as the initial 'least used' entry */
6505 bruce@momjian.us 545 [ + - ]: 3 : for (ix = 0; ix < stmtCacheEntPerBucket; ++ix)
546 : : {
547 : 3 : entry = &stmtCacheEntries[entNo];
2517 tgl@sss.pgh.pa.us 548 [ + - ]: 3 : if (!entry->stmtID[0]) /* unused entry - use it */
6505 bruce@momjian.us 549 : 3 : break;
6505 bruce@momjian.us 550 [ # # ]:UBC 0 : if (entry->execs < stmtCacheEntries[luEntNo].execs)
2517 tgl@sss.pgh.pa.us 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 : : */
2517 tgl@sss.pgh.pa.us 559 [ - + ]:CBC 3 : if (ix >= stmtCacheEntPerBucket)
2517 tgl@sss.pgh.pa.us 560 :UBC 0 : entNo = luEntNo;
561 : :
562 : : /* 'entNo' is the entry to use - make sure its free */
6421 meskes@postgresql.or 563 [ - + ]:CBC 3 : if (ecpg_freeStmtCacheEntry(lineno, compat, entNo) < 0)
2942 peter_e@gmx.net 564 :UBC 0 : return -1;
565 : :
566 : : /* add the query to the entry */
6505 bruce@momjian.us 567 :CBC 3 : entry = &stmtCacheEntries[entNo];
568 : 3 : entry->lineno = lineno;
45 michael@paquier.xyz 569 :GNC 3 : entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno, NULL);
570 [ - + ]: 3 : if (!entry->ecpgQuery)
45 michael@paquier.xyz 571 :UNC 0 : return -1;
4936 peter_e@gmx.net 572 :CBC 3 : entry->connection = connection;
6505 bruce@momjian.us 573 : 3 : entry->execs = 0;
574 : 3 : memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID));
575 : :
2942 peter_e@gmx.net 576 : 3 : return entNo;
577 : : }
578 : :
579 : : /* handle cache and preparation of statements in auto-prepare mode */
580 : : bool
5890 magnus@hagander.net 581 : 8 : 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 */
6598 meskes@postgresql.or 586 : 8 : entNo = SearchStmtCache(query);
587 : :
588 : : /* if not found - add the statement to the cache */
6505 bruce@momjian.us 589 [ + + ]: 8 : if (entNo)
590 : : {
591 : : char *stmtID;
592 : : struct connection *con;
593 : : struct prepared_statement *prep;
594 : :
6322 peter_e@gmx.net 595 : 5 : ecpg_log("ecpg_auto_prepare on line %d: statement found in cache; entry %d\n", lineno, entNo);
596 : :
5706 meskes@postgresql.or 597 : 5 : stmtID = stmtCacheEntries[entNo].stmtID;
45 michael@paquier.xyz 598 :GNC 5 : *name = ecpg_strdup(stmtID, lineno, NULL);
599 [ - + ]: 5 : if (*name == NULL)
45 michael@paquier.xyz 600 :UNC 0 : return false;
601 : :
5706 meskes@postgresql.or 602 :CBC 5 : con = ecpg_get_connection(connection_name);
603 : 5 : prep = ecpg_find_prepared_statement(stmtID, con, NULL);
604 : : /* This prepared name doesn't exist on this connection. */
5422 605 [ + + - + ]: 5 : if (!prep && !prepare_common(lineno, con, stmtID, query))
606 : : {
40 michael@paquier.xyz 607 :UNC 0 : ecpg_free(*name);
2942 peter_e@gmx.net 608 :UBC 0 : return false;
609 : : }
610 : :
611 : : }
612 : : else
613 : : {
614 : : char stmtID[STMTID_SIZE];
615 : :
6322 peter_e@gmx.net 616 :CBC 3 : ecpg_log("ecpg_auto_prepare on line %d: statement not in cache; inserting\n", lineno);
617 : :
618 : : /* generate a statement ID */
5706 meskes@postgresql.or 619 : 3 : sprintf(stmtID, "ecpg%d", nextStmtID++);
45 michael@paquier.xyz 620 :GNC 3 : *name = ecpg_strdup(stmtID, lineno, NULL);
621 [ - + ]: 3 : if (*name == NULL)
45 michael@paquier.xyz 622 :UNC 0 : return false;
623 : :
5706 meskes@postgresql.or 624 [ - + ]:CBC 3 : if (!ECPGprepare(lineno, connection_name, 0, stmtID, query))
625 : : {
40 michael@paquier.xyz 626 :UNC 0 : ecpg_free(*name);
2942 peter_e@gmx.net 627 :UBC 0 : return false;
628 : : }
629 : :
2516 tgl@sss.pgh.pa.us 630 :CBC 3 : entNo = AddStmtToCache(lineno, stmtID, connection_name, compat, query);
631 [ - + ]: 3 : if (entNo < 0)
632 : : {
40 michael@paquier.xyz 633 :UNC 0 : ecpg_free(*name);
2942 peter_e@gmx.net 634 :UBC 0 : return false;
635 : : }
636 : : }
637 : :
638 : : /* increase usage counter */
6598 meskes@postgresql.or 639 :CBC 8 : stmtCacheEntries[entNo].execs++;
640 : :
2942 peter_e@gmx.net 641 : 8 : return true;
642 : : }
|