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