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