Age Owner 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
7329 meskes 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 == '.')
7329 meskes 50 UBC 0 : return true;
51 :
7329 meskes 52 CBC 824 : if (c >= 128)
7329 meskes 53 UBC 0 : return true;
54 :
2061 peter_e 55 CBC 824 : return false;
56 : }
57 :
58 : bool
1418 meskes 59 5 : ecpg_register_prepared_stmt(struct statement *stmt)
60 : {
61 : struct statement *prep_stmt;
62 : struct prepared_statement *this;
1297 tgl 63 5 : struct connection *con = stmt->connection;
1418 meskes 64 5 : struct prepared_statement *prev = NULL;
tgl 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))
1418 tgl 70 UBC 0 : return false;
71 :
72 : /* allocate new statement */
1418 tgl 73 CBC 5 : this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
74 5 : if (!this)
1418 tgl 75 UBC 0 : return false;
76 :
1418 tgl 77 CBC 5 : prep_stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
1414 78 5 : if (!prep_stmt)
79 : {
1418 tgl 80 UBC 0 : ecpg_free(this);
81 0 : return false;
82 : }
1418 tgl 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;
88 5 : prep_stmt->command = ecpg_strdup(stmt->command, lineno);
89 5 : prep_stmt->inlist = prep_stmt->outlist = NULL;
90 5 : this->name = ecpg_strdup(stmt->name, lineno);
91 5 : this->stmt = prep_stmt;
92 5 : this->prepared = true;
93 :
94 5 : if (con->prep_stmts == NULL)
1418 tgl 95 UBC 0 : this->next = NULL;
96 : else
1418 tgl 97 CBC 5 : this->next = con->prep_stmts;
98 :
99 5 : con->prep_stmts = this;
100 5 : return true;
101 : }
102 :
103 : static bool
5072 meskes 104 854 : replace_variables(char **text, int lineno)
105 : {
5624 bruce 106 854 : bool string = false;
107 854 : int counter = 1,
108 854 : ptr = 0;
109 :
5717 meskes 110 24163 : for (; (*text)[ptr] != '\0'; ptr++)
111 : {
112 23309 : if ((*text)[ptr] == '\'')
7329 meskes 113 UBC 0 : string = string ? false : true;
114 :
5717 meskes 115 CBC 23309 : if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?')))
116 22481 : continue;
117 :
5624 bruce 118 828 : if (((*text)[ptr] == ':') && ((*text)[ptr + 1] == ':'))
5624 bruce 119 UBC 0 : ptr += 2; /* skip '::' */
120 : else
121 : {
122 : /* a rough guess of the size we need: */
1636 tgl 123 CBC 828 : int buffersize = sizeof(int) * CHAR_BIT * 10 / 3;
124 : int len;
125 : char *buffer,
126 : *newcopy;
127 :
5667 meskes 128 828 : if (!(buffer = (char *) ecpg_alloc(buffersize, lineno)))
5717 meskes 129 UBC 0 : return false;
130 :
5717 meskes 131 CBC 828 : snprintf(buffer, buffersize, "$%d", counter++);
132 :
1636 tgl 133 831 : for (len = 1; (*text)[ptr + len] && isvarchar((*text)[ptr + len]); len++)
134 : /* skip */ ;
1058 135 828 : if (!(newcopy = (char *) ecpg_alloc(strlen(*text) - len + strlen(buffer) + 1, lineno)))
136 : {
5667 meskes 137 UBC 0 : ecpg_free(buffer);
5717 138 0 : return false;
139 : }
140 :
2997 tgl 141 CBC 828 : memcpy(newcopy, *text, ptr);
5717 meskes 142 828 : strcpy(newcopy + ptr, buffer);
5624 bruce 143 828 : strcat(newcopy, (*text) +ptr + len);
144 :
5667 meskes 145 828 : ecpg_free(*text);
146 828 : ecpg_free(buffer);
147 :
5717 148 828 : *text = newcopy;
149 :
5624 bruce 150 828 : if ((*text)[ptr] == '\0') /* we reached the end */
5624 bruce 151 UBC 0 : ptr--; /* since we will (*text)[ptr]++ in the top
152 : * level for loop */
153 : }
154 : }
5717 meskes 155 CBC 854 : return true;
156 : }
157 :
158 : static bool
2118 tgl 159 854 : 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 */
5667 meskes 166 854 : this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
7329 167 854 : if (!this)
7329 meskes 168 UBC 0 : return false;
169 :
5667 meskes 170 CBC 854 : stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
7329 171 854 : if (!stmt)
172 : {
5667 meskes 173 UBC 0 : ecpg_free(this);
7329 174 0 : return false;
175 : }
176 :
177 : /* create statement */
7329 meskes 178 CBC 854 : stmt->lineno = lineno;
5674 179 854 : stmt->connection = con;
5667 180 854 : stmt->command = ecpg_strdup(variable, lineno);
7329 181 854 : stmt->inlist = stmt->outlist = NULL;
182 :
183 : /* if we have C variables in our statement replace them with '?' */
5072 184 854 : replace_variables(&(stmt->command), lineno);
185 :
186 : /* add prepared statement to our list */
4924 187 854 : this->name = ecpg_strdup(name, lineno);
7329 188 854 : this->stmt = stmt;
189 :
190 : /* and finally really prepare the statement */
5717 191 854 : query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL);
5667 192 854 : if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat))
193 : {
5667 meskes 194 UBC 0 : ecpg_free(stmt->command);
4924 195 0 : ecpg_free(this->name);
5667 196 0 : ecpg_free(this);
197 0 : ecpg_free(stmt);
5717 198 0 : return false;
199 : }
200 :
4767 meskes 201 CBC 854 : ecpg_log("prepare_common on line %d: name %s; query: \"%s\"\n", stmt->lineno, name, stmt->command);
5717 202 854 : PQclear(query);
203 854 : this->prepared = true;
204 :
5674 205 854 : if (con->prep_stmts == NULL)
7329 206 828 : this->next = NULL;
207 : else
5674 208 26 : this->next = con->prep_stmts;
209 :
210 854 : con->prep_stmts = this;
7329 211 854 : 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
1636 tgl 217 851 : 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 :
1297 226 851 : con = ecpg_get_connection(connection_name);
227 851 : if (!ecpg_init(con, connection_name, lineno))
4825 meskes 228 UBC 0 : return false;
229 :
230 : /* check if we already have prepared this statement */
4825 meskes 231 CBC 851 : this = ecpg_find_prepared_statement(name, con, &prev);
232 851 : if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
4825 meskes 233 UBC 0 : return false;
234 :
4541 meskes 235 CBC 851 : return prepare_common(lineno, con, name, variable);
236 : }
237 :
238 : struct prepared_statement *
4832 239 1787 : ecpg_find_prepared_statement(const char *name,
240 : struct connection *con, struct prepared_statement **prev_)
241 : {
242 : struct prepared_statement *this,
243 : *prev;
244 :
1636 tgl 245 1787 : for (this = con->prep_stmts, prev = NULL;
246 1854 : this != NULL;
247 67 : prev = this, this = this->next)
248 : {
5674 meskes 249 1781 : if (strcmp(this->name, name) == 0)
250 : {
251 1714 : if (prev_)
252 839 : *prev_ = prev;
253 1714 : return this;
254 : }
255 : }
256 73 : return NULL;
257 : }
258 :
259 : static bool
1636 tgl 260 857 : deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
261 : struct prepared_statement *prev, struct prepared_statement *this)
262 : {
5624 bruce 263 857 : bool r = false;
264 :
4767 meskes 265 857 : ecpg_log("deallocate_one on line %d: name %s\n", lineno, this->name);
266 :
267 : /* first deallocate the statement in the backend */
5674 268 857 : if (this->prepared)
269 : {
270 : char *text;
271 : PGresult *query;
272 :
5667 273 857 : text = (char *) ecpg_alloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);
274 :
5674 275 857 : if (text)
276 : {
277 857 : sprintf(text, "deallocate \"%s\"", this->name);
278 857 : query = PQexec(this->stmt->connection->connection, text);
5667 279 857 : ecpg_free(text);
1636 tgl 280 857 : if (ecpg_check_PQresult(query, lineno,
281 857 : this->stmt->connection->connection,
282 857 : this->stmt->compat))
283 : {
5717 meskes 284 857 : PQclear(query);
5674 285 857 : 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 857 : if (!r && !INFORMIX_MODE(c))
295 : {
5667 meskes 296 UBC 0 : ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name);
5674 297 0 : return false;
298 : }
299 :
300 : /* okay, free all the resources */
5667 meskes 301 CBC 857 : ecpg_free(this->stmt->command);
302 857 : ecpg_free(this->stmt);
4924 303 857 : ecpg_free(this->name);
5674 304 857 : if (prev != NULL)
305 3 : prev->next = this->next;
306 : else
307 854 : con->prep_stmts = this->next;
308 :
5667 309 857 : ecpg_free(this);
5674 310 857 : return true;
311 : }
312 :
313 : /* handle the EXEC SQL DEALLOCATE PREPARE statement */
314 : bool
315 53 : 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 :
1297 tgl 321 53 : con = ecpg_get_connection(connection_name);
322 53 : if (!ecpg_init(con, connection_name, lineno))
5445 meskes 323 UBC 0 : return false;
324 :
4832 meskes 325 CBC 53 : this = ecpg_find_prepared_statement(name, con, &prev);
5674 326 53 : if (this)
327 52 : return deallocate_one(lineno, c, con, prev, this);
328 :
329 : /* prepared statement is not found */
330 1 : if (INFORMIX_MODE(c))
5674 meskes 331 UBC 0 : return true;
5667 meskes 332 CBC 1 : ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
5674 333 1 : return false;
334 : }
335 :
336 : bool
2118 tgl 337 123 : ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *con)
338 : {
339 : /* deallocate all prepared statements */
5674 meskes 340 141 : while (con->prep_stmts)
341 : {
5670 342 18 : if (!deallocate_one(lineno, c, con, NULL, con->prep_stmts))
7329 meskes 343 UBC 0 : return false;
344 : }
345 :
7329 meskes 346 CBC 123 : return true;
347 : }
348 :
349 : bool
5670 350 1 : ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
351 : {
1636 tgl 352 1 : return ecpg_deallocate_all_conn(lineno, compat,
353 : ecpg_get_connection(connection_name));
354 : }
355 :
356 : char *
2118 357 852 : ecpg_prepared(const char *name, struct connection *con)
358 : {
359 : struct prepared_statement *this;
360 :
4832 meskes 361 852 : this = ecpg_find_prepared_statement(name, con, NULL);
5674 362 852 : 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 20 : ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
369 : {
370 : (void) lineno; /* keep the compiler quiet */
371 :
1297 tgl 372 20 : 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
5717 meskes 379 10 : HashStmt(const char *ecpgQuery)
380 : {
381 : int stmtIx,
382 : bucketNo,
383 : hashLeng,
384 : stmtLeng;
385 : uint64 hashVal,
386 : rotVal;
387 :
5624 bruce 388 10 : stmtLeng = strlen(ecpgQuery);
1636 tgl 389 10 : hashLeng = 50; /* use 1st 50 characters of statement */
390 10 : if (hashLeng > stmtLeng) /* if the statement isn't that long */
391 10 : hashLeng = stmtLeng; /* use its actual length */
392 :
5624 bruce 393 10 : hashVal = 0;
394 371 : for (stmtIx = 0; stmtIx < hashLeng; ++stmtIx)
395 : {
1635 tgl 396 361 : hashVal = hashVal + (unsigned char) ecpgQuery[stmtIx];
397 : /* rotate 32-bit hash value left 13 bits */
5624 bruce 398 361 : hashVal = hashVal << 13;
1635 tgl 399 361 : rotVal = (hashVal & UINT64CONST(0x1fff00000000)) >> 32;
400 361 : hashVal = (hashVal & UINT64CONST(0xffffffff)) | rotVal;
401 : }
402 :
5624 bruce 403 10 : bucketNo = hashVal % stmtCacheNBuckets;
404 :
405 : /* Add 1 so that array entry 0 is never used */
1635 tgl 406 10 : 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
5717 meskes 415 8 : SearchStmtCache(const char *ecpgQuery)
416 : {
417 : int entNo,
418 : entIx;
419 :
420 : /* quick failure if cache not set up */
1635 tgl 421 8 : if (stmtCacheEntries == NULL)
422 1 : return 0;
423 :
424 : /* hash the statement */
5624 bruce 425 7 : entNo = HashStmt(ecpgQuery);
426 :
427 : /* search the cache */
428 23 : for (entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx)
429 : {
1636 tgl 430 21 : if (stmtCacheEntries[entNo].stmtID[0]) /* check if entry is in use */
431 : {
4121 peter_e 432 5 : if (strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery) == 0)
1636 tgl 433 5 : break; /* found it */
434 : }
435 16 : ++entNo; /* incr entry # */
436 : }
437 :
438 : /* if entry wasn't found - set entry # to zero */
5624 bruce 439 7 : if (entIx >= stmtCacheEntPerBucket)
440 2 : entNo = 0;
441 :
2061 peter_e 442 7 : 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
1636 tgl 451 3 : 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 */
1635 460 3 : if (stmtCacheEntries == NULL)
1635 tgl 461 UBC 0 : return -1;
462 :
5624 bruce 463 CBC 3 : entry = &stmtCacheEntries[entNo];
1636 tgl 464 3 : if (!entry->stmtID[0]) /* return if the entry isn't in use */
2061 peter_e 465 3 : return 0;
466 :
5624 bruce 467 UBC 0 : con = ecpg_get_connection(entry->connection);
468 :
469 : /* free the 'prepared_statement' list entry */
4832 meskes 470 0 : this = ecpg_find_prepared_statement(entry->stmtID, con, &prev);
5540 471 0 : if (this && !deallocate_one(lineno, compat, con, prev, this))
2061 peter_e 472 0 : return -1;
473 :
5624 bruce 474 0 : entry->stmtID[0] = '\0';
475 :
476 : /* free the memory used by the cache entry */
477 0 : if (entry->ecpgQuery)
478 : {
479 0 : ecpg_free(entry->ecpgQuery);
480 0 : entry->ecpgQuery = 0;
481 : }
482 :
2061 peter_e 483 0 : 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
1636 tgl 491 CBC 3 : 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 */
1635 504 3 : if (stmtCacheEntries == NULL)
505 : {
506 1 : stmtCacheEntries = (stmtCacheEntry *)
507 1 : ecpg_alloc(sizeof(stmtCacheEntry) * stmtCacheArraySize, lineno);
508 1 : if (stmtCacheEntries == NULL)
1635 tgl 509 UBC 0 : return -1;
510 : }
511 :
512 : /* hash the statement */
5624 bruce 513 CBC 3 : initEntNo = HashStmt(ecpgQuery);
514 :
515 : /* search for an unused entry */
516 3 : entNo = initEntNo; /* start with the initial entry # for the
517 : * bucket */
1636 tgl 518 3 : luEntNo = initEntNo; /* use it as the initial 'least used' entry */
5624 bruce 519 3 : for (ix = 0; ix < stmtCacheEntPerBucket; ++ix)
520 : {
521 3 : entry = &stmtCacheEntries[entNo];
1636 tgl 522 3 : if (!entry->stmtID[0]) /* unused entry - use it */
5624 bruce 523 3 : break;
5624 bruce 524 UBC 0 : if (entry->execs < stmtCacheEntries[luEntNo].execs)
1636 tgl 525 0 : luEntNo = entNo; /* save new 'least used' entry */
526 0 : ++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 : */
1636 tgl 533 CBC 3 : if (ix >= stmtCacheEntPerBucket)
1636 tgl 534 UBC 0 : entNo = luEntNo;
535 :
536 : /* 'entNo' is the entry to use - make sure its free */
5540 meskes 537 CBC 3 : if (ecpg_freeStmtCacheEntry(lineno, compat, entNo) < 0)
2061 peter_e 538 UBC 0 : return -1;
539 :
540 : /* add the query to the entry */
5624 bruce 541 CBC 3 : entry = &stmtCacheEntries[entNo];
542 3 : entry->lineno = lineno;
543 3 : entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno);
4055 peter_e 544 3 : entry->connection = connection;
5624 bruce 545 3 : entry->execs = 0;
546 3 : memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID));
547 :
2061 peter_e 548 3 : return entNo;
549 : }
550 :
551 : /* handle cache and preparation of statements in auto-prepare mode */
552 : bool
5009 magnus 553 8 : 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 */
5717 meskes 558 8 : entNo = SearchStmtCache(query);
559 :
560 : /* if not found - add the statement to the cache */
5624 bruce 561 8 : if (entNo)
562 : {
563 : char *stmtID;
564 : struct connection *con;
565 : struct prepared_statement *prep;
566 :
5441 peter_e 567 5 : ecpg_log("ecpg_auto_prepare on line %d: statement found in cache; entry %d\n", lineno, entNo);
568 :
4825 meskes 569 5 : stmtID = stmtCacheEntries[entNo].stmtID;
570 :
571 5 : con = ecpg_get_connection(connection_name);
572 5 : prep = ecpg_find_prepared_statement(stmtID, con, NULL);
573 : /* This prepared name doesn't exist on this connection. */
4541 574 5 : if (!prep && !prepare_common(lineno, con, stmtID, query))
2061 peter_e 575 UBC 0 : return false;
576 :
4825 meskes 577 CBC 5 : *name = ecpg_strdup(stmtID, lineno);
578 : }
579 : else
580 : {
581 : char stmtID[STMTID_SIZE];
582 :
5441 peter_e 583 3 : ecpg_log("ecpg_auto_prepare on line %d: statement not in cache; inserting\n", lineno);
584 :
585 : /* generate a statement ID */
4825 meskes 586 3 : sprintf(stmtID, "ecpg%d", nextStmtID++);
587 :
588 3 : if (!ECPGprepare(lineno, connection_name, 0, stmtID, query))
2061 peter_e 589 UBC 0 : return false;
590 :
1635 tgl 591 CBC 3 : entNo = AddStmtToCache(lineno, stmtID, connection_name, compat, query);
592 3 : if (entNo < 0)
2061 peter_e 593 UBC 0 : return false;
594 :
4825 meskes 595 CBC 3 : *name = ecpg_strdup(stmtID, lineno);
596 : }
597 :
598 : /* increase usage counter */
5717 599 8 : stmtCacheEntries[entNo].execs++;
600 :
2061 peter_e 601 8 : return true;
602 : }
|