Fix obsolete comment regarding FSM truncation.
[PostgreSQL.git] / src / interfaces / ecpg / ecpglib / prepare.c
blob59cf34a608077eac6d3ea1053508797e835e4142
1 /* $PostgreSQL$ */
3 #define POSTGRES_ECPG_INTERNAL
4 #include "postgres_fe.h"
6 #include <ctype.h>
8 #include "ecpgtype.h"
9 #include "ecpglib.h"
10 #include "ecpgerrno.h"
11 #include "extern.h"
12 #include "sqlca.h"
14 struct prepared_statement
16 char *name;
17 bool prepared;
18 struct statement *stmt;
19 struct prepared_statement *next;
22 #define STMTID_SIZE 32
24 typedef struct
26 int lineno;
27 char stmtID[STMTID_SIZE];
28 char *ecpgQuery;
29 long execs; /* # of executions */
30 char *connection; /* connection for the statement */
31 } stmtCacheEntry;
33 static int nextStmtID = 1;
34 static const int stmtCacheNBuckets = 2039; /* # buckets - a prime # */
35 static const int stmtCacheEntPerBucket = 8; /* # entries/bucket */
36 static stmtCacheEntry stmtCacheEntries[16384] = {{0, {0}, 0, 0, 0}};
38 static struct prepared_statement *find_prepared_statement(const char *name,
39 struct connection * con, struct prepared_statement ** prev);
40 static bool deallocate_one(int lineno, enum COMPAT_MODE c, struct connection * con,
41 struct prepared_statement * prev, struct prepared_statement * this);
43 static bool
44 isvarchar(unsigned char c)
46 if (isalnum(c))
47 return true;
49 if (c == '_' || c == '>' || c == '-' || c == '.')
50 return true;
52 if (c >= 128)
53 return true;
55 return (false);
58 static bool
59 replace_variables(char **text, int lineno, bool questionmarks)
61 bool string = false;
62 int counter = 1,
63 ptr = 0;
65 for (; (*text)[ptr] != '\0'; ptr++)
67 if ((*text)[ptr] == '\'')
68 string = string ? false : true;
70 if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?')))
71 continue;
73 if (((*text)[ptr] == ':') && ((*text)[ptr + 1] == ':'))
74 ptr += 2; /* skip '::' */
75 else
77 int len;
78 int buffersize = sizeof(int) * CHAR_BIT * 10 / 3; /* a rough guess of the
79 * size we need */
80 char *buffer,
81 *newcopy;
83 if (!(buffer = (char *) ecpg_alloc(buffersize, lineno)))
84 return false;
86 snprintf(buffer, buffersize, "$%d", counter++);
88 for (len = 1; (*text)[ptr + len] && isvarchar((*text)[ptr + len]); len++);
89 if (!(newcopy = (char *) ecpg_alloc(strlen(*text) -len + strlen(buffer) + 1, lineno)))
91 ecpg_free(buffer);
92 return false;
95 strncpy(newcopy, *text, ptr);
96 strcpy(newcopy + ptr, buffer);
97 strcat(newcopy, (*text) +ptr + len);
99 ecpg_free(*text);
100 ecpg_free(buffer);
102 *text = newcopy;
104 if ((*text)[ptr] == '\0') /* we reached the end */
105 ptr--; /* since we will (*text)[ptr]++ in the top
106 * level for loop */
109 return true;
112 /* handle the EXEC SQL PREPARE statement */
113 bool
114 ECPGprepare(int lineno, const char *connection_name, const int questionmarks, const char *name, const char *variable)
116 struct connection *con;
117 struct statement *stmt;
118 struct prepared_statement *this,
119 *prev;
120 PGresult *query;
122 con = ecpg_get_connection(connection_name);
124 if (!ecpg_init(con, connection_name, lineno))
125 return false;
127 /* check if we already have prepared this statement */
128 this = find_prepared_statement(name, con, &prev);
129 if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
130 return false;
132 /* allocate new statement */
133 this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
134 if (!this)
135 return false;
137 stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
138 if (!stmt)
140 ecpg_free(this);
141 return false;
144 /* create statement */
145 stmt->lineno = lineno;
146 stmt->connection = con;
147 stmt->command = ecpg_strdup(variable, lineno);
148 stmt->inlist = stmt->outlist = NULL;
150 /* if we have C variables in our statment replace them with '?' */
151 replace_variables(&(stmt->command), lineno, questionmarks);
153 /* add prepared statement to our list */
154 this->name = (char *) name;
155 this->stmt = stmt;
157 /* and finally really prepare the statement */
158 query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL);
159 if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat))
161 ecpg_free(stmt->command);
162 ecpg_free(this);
163 ecpg_free(stmt);
164 return false;
167 ecpg_log("ECPGprepare on line %d: name %s; query: \"%s\"\n", stmt->lineno, name, stmt->command);
168 PQclear(query);
169 this->prepared = true;
171 if (con->prep_stmts == NULL)
172 this->next = NULL;
173 else
174 this->next = con->prep_stmts;
176 con->prep_stmts = this;
177 return true;
180 static struct prepared_statement *
181 find_prepared_statement(const char *name,
182 struct connection * con, struct prepared_statement ** prev_)
184 struct prepared_statement *this,
185 *prev;
187 for (this = con->prep_stmts, prev = NULL; this != NULL; prev = this, this = this->next)
189 if (strcmp(this->name, name) == 0)
191 if (prev_)
192 *prev_ = prev;
193 return this;
196 return NULL;
199 static bool
200 deallocate_one(int lineno, enum COMPAT_MODE c, struct connection * con, struct prepared_statement * prev, struct prepared_statement * this)
202 bool r = false;
204 ecpg_log("ECPGdeallocate on line %d: name %s\n", lineno, this->name);
206 /* first deallocate the statement in the backend */
207 if (this->prepared)
209 char *text;
210 PGresult *query;
212 text = (char *) ecpg_alloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);
214 if (text)
216 sprintf(text, "deallocate \"%s\"", this->name);
217 query = PQexec(this->stmt->connection->connection, text);
218 ecpg_free(text);
219 if (ecpg_check_PQresult(query, lineno, this->stmt->connection->connection, this->stmt->compat))
221 PQclear(query);
222 r = true;
228 * Just ignore all errors since we do not know the list of cursors we are
229 * allowed to free. We have to trust the software.
231 if (!r && !INFORMIX_MODE(c))
233 ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name);
234 return false;
237 /* okay, free all the resources */
238 ecpg_free(this->stmt->command);
239 ecpg_free(this->stmt);
240 if (prev != NULL)
241 prev->next = this->next;
242 else
243 con->prep_stmts = this->next;
245 ecpg_free(this);
246 return true;
249 /* handle the EXEC SQL DEALLOCATE PREPARE statement */
250 bool
251 ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name)
253 struct connection *con;
254 struct prepared_statement *this,
255 *prev;
257 con = ecpg_get_connection(connection_name);
259 if (!ecpg_init(con, connection_name, lineno))
260 return false;
262 this = find_prepared_statement(name, con, &prev);
263 if (this)
264 return deallocate_one(lineno, c, con, prev, this);
266 /* prepared statement is not found */
267 if (INFORMIX_MODE(c))
268 return true;
269 ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
270 return false;
273 bool
274 ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection * con)
276 /* deallocate all prepared statements */
277 while (con->prep_stmts)
279 if (!deallocate_one(lineno, c, con, NULL, con->prep_stmts))
280 return false;
283 return true;
286 bool
287 ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
289 return ecpg_deallocate_all_conn(lineno, compat, ecpg_get_connection(connection_name));
292 char *
293 ecpg_prepared(const char *name, struct connection * con, int lineno)
295 struct prepared_statement *this;
297 this = find_prepared_statement(name, con, NULL);
298 return this ? this->stmt->command : NULL;
301 /* return the prepared statement */
302 char *
303 ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
305 return ecpg_prepared(name, ecpg_get_connection(connection_name), lineno);
309 * hash a SQL statement - returns entry # of first entry in the bucket
311 static int
312 HashStmt(const char *ecpgQuery)
314 int stmtIx,
315 bucketNo,
316 hashLeng,
317 stmtLeng;
318 long long hashVal,
319 rotVal;
321 stmtLeng = strlen(ecpgQuery);
322 hashLeng = 50; /* use 1st 50 characters of statement */
323 if (hashLeng > stmtLeng) /* if the statement isn't that long */
324 hashLeng = stmtLeng; /* use its actual length */
326 hashVal = 0;
327 for (stmtIx = 0; stmtIx < hashLeng; ++stmtIx)
329 hashVal = hashVal + (int) ecpgQuery[stmtIx];
330 hashVal = hashVal << 13;
331 rotVal = (hashVal & 0x1fff00000000LL) >> 32;
332 hashVal = (hashVal & 0xffffffffLL) | rotVal;
335 bucketNo = hashVal % stmtCacheNBuckets;
336 bucketNo += 1; /* don't use bucket # 0 */
338 return (bucketNo * stmtCacheEntPerBucket);
342 * search the statement cache - search for entry with matching ECPG-format query
343 * Returns entry # in cache if found
344 * OR zero if not present (zero'th entry isn't used)
346 static int
347 SearchStmtCache(const char *ecpgQuery)
349 int entNo,
350 entIx;
352 /* hash the statement */
353 entNo = HashStmt(ecpgQuery);
355 /* search the cache */
356 for (entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx)
358 if (stmtCacheEntries[entNo].stmtID[0]) /* check if entry is in use */
360 if (!strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery))
361 break; /* found it */
363 ++entNo; /* incr entry # */
366 /* if entry wasn't found - set entry # to zero */
367 if (entIx >= stmtCacheEntPerBucket)
368 entNo = 0;
370 return (entNo);
374 * free an entry in the statement cache
375 * Returns entry # in cache used
376 * OR negative error code
378 static int
379 ecpg_freeStmtCacheEntry(int lineno, int compat, int entNo) /* entry # to free */
381 stmtCacheEntry *entry;
382 struct connection *con;
383 struct prepared_statement *this, *prev;
385 entry = &stmtCacheEntries[entNo];
386 if (!entry->stmtID[0]) /* return if the entry isn't in use */
387 return (0);
389 con = ecpg_get_connection(entry->connection);
391 /* free the 'prepared_statement' list entry */
392 this = find_prepared_statement(entry->stmtID, con, &prev);
393 if (this && !deallocate_one(lineno, compat, con, prev, this))
394 return (-1);
396 entry->stmtID[0] = '\0';
398 /* free the memory used by the cache entry */
399 if (entry->ecpgQuery)
401 ecpg_free(entry->ecpgQuery);
402 entry->ecpgQuery = 0;
405 return (entNo);
409 * add an entry to the statement cache
410 * returns entry # in cache used OR negative error code
412 static int
413 AddStmtToCache(int lineno, /* line # of statement */
414 char *stmtID, /* statement ID */
415 const char *connection, /* connection */
416 int compat, /* compatibility level */
417 const char *ecpgQuery) /* query */
419 int ix,
420 initEntNo,
421 luEntNo,
422 entNo;
423 stmtCacheEntry *entry;
425 /* hash the statement */
426 initEntNo = HashStmt(ecpgQuery);
428 /* search for an unused entry */
429 entNo = initEntNo; /* start with the initial entry # for the
430 * bucket */
431 luEntNo = initEntNo; /* use it as the initial 'least used' entry */
432 for (ix = 0; ix < stmtCacheEntPerBucket; ++ix)
434 entry = &stmtCacheEntries[entNo];
435 if (!entry->stmtID[0]) /* unused entry - use it */
436 break;
437 if (entry->execs < stmtCacheEntries[luEntNo].execs)
438 luEntNo = entNo; /* save new 'least used' entry */
439 ++entNo; /* increment entry # */
442 /* if no unused entries were found - use the 'least used' entry found in the bucket */
443 if (ix >= stmtCacheEntPerBucket) /* if no unused entries were found */
444 entNo = luEntNo; /* re-use the 'least used' entry */
446 /* 'entNo' is the entry to use - make sure its free */
447 if (ecpg_freeStmtCacheEntry(lineno, compat, entNo) < 0)
448 return (-1);
450 /* add the query to the entry */
451 entry = &stmtCacheEntries[entNo];
452 entry->lineno = lineno;
453 entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno);
454 entry->connection = (char *) connection;
455 entry->execs = 0;
456 memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID));
458 return (entNo);
461 /* handle cache and preparation of statments in auto-prepare mode */
462 bool
463 ecpg_auto_prepare(int lineno, const char *connection_name, int compat, const int questionmarks, char **name, const char *query)
465 int entNo;
467 /* search the statement cache for this statement */
468 entNo = SearchStmtCache(query);
470 /* if not found - add the statement to the cache */
471 if (entNo)
473 ecpg_log("ecpg_auto_prepare on line %d: statement found in cache; entry %d\n", lineno, entNo);
474 *name = ecpg_strdup(stmtCacheEntries[entNo].stmtID, lineno);
476 else
478 ecpg_log("ecpg_auto_prepare on line %d: statement not in cache; inserting\n", lineno);
480 /* generate a statement ID */
481 *name = (char *) ecpg_alloc(STMTID_SIZE, lineno);
482 sprintf(*name, "ecpg%d", nextStmtID++);
484 if (!ECPGprepare(lineno, connection_name, questionmarks, ecpg_strdup(*name, lineno), query))
485 return (false);
486 if (AddStmtToCache(lineno, *name, connection_name, compat, query) < 0)
487 return (false);
490 /* increase usage counter */
491 stmtCacheEntries[entNo].execs++;
493 return (true);