00001
00002
00003 #define POSTGRES_ECPG_INTERNAL
00004 #include "postgres_fe.h"
00005
00006 #include <ctype.h>
00007
00008 #include "ecpgtype.h"
00009 #include "ecpglib.h"
00010 #include "ecpgerrno.h"
00011 #include "extern.h"
00012 #include "sqlca.h"
00013
00014 #define STMTID_SIZE 32
00015
00016 typedef struct
00017 {
00018 int lineno;
00019 char stmtID[STMTID_SIZE];
00020 char *ecpgQuery;
00021 long execs;
00022 const char *connection;
00023 } stmtCacheEntry;
00024
00025 static int nextStmtID = 1;
00026 static const int stmtCacheNBuckets = 2039;
00027 static const int stmtCacheEntPerBucket = 8;
00028 static stmtCacheEntry stmtCacheEntries[16384] = {{0, {0}, 0, 0, 0}};
00029
00030 static bool deallocate_one(int lineno, enum COMPAT_MODE c, struct connection * con,
00031 struct prepared_statement * prev, struct prepared_statement * this);
00032
00033 static bool
00034 isvarchar(unsigned char c)
00035 {
00036 if (isalnum(c))
00037 return true;
00038
00039 if (c == '_' || c == '>' || c == '-' || c == '.')
00040 return true;
00041
00042 if (c >= 128)
00043 return true;
00044
00045 return (false);
00046 }
00047
00048 static bool
00049 replace_variables(char **text, int lineno)
00050 {
00051 bool string = false;
00052 int counter = 1,
00053 ptr = 0;
00054
00055 for (; (*text)[ptr] != '\0'; ptr++)
00056 {
00057 if ((*text)[ptr] == '\'')
00058 string = string ? false : true;
00059
00060 if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?')))
00061 continue;
00062
00063 if (((*text)[ptr] == ':') && ((*text)[ptr + 1] == ':'))
00064 ptr += 2;
00065 else
00066 {
00067 int len;
00068 int buffersize = sizeof(int) * CHAR_BIT * 10 / 3;
00069
00070 char *buffer,
00071 *newcopy;
00072
00073 if (!(buffer = (char *) ecpg_alloc(buffersize, lineno)))
00074 return false;
00075
00076 snprintf(buffer, buffersize, "$%d", counter++);
00077
00078 for (len = 1; (*text)[ptr + len] && isvarchar((*text)[ptr + len]); len++);
00079 if (!(newcopy = (char *) ecpg_alloc(strlen(*text) -len + strlen(buffer) + 1, lineno)))
00080 {
00081 ecpg_free(buffer);
00082 return false;
00083 }
00084
00085 strncpy(newcopy, *text, ptr);
00086 strcpy(newcopy + ptr, buffer);
00087 strcat(newcopy, (*text) +ptr + len);
00088
00089 ecpg_free(*text);
00090 ecpg_free(buffer);
00091
00092 *text = newcopy;
00093
00094 if ((*text)[ptr] == '\0')
00095 ptr--;
00096
00097 }
00098 }
00099 return true;
00100 }
00101
00102 static bool
00103 prepare_common(int lineno, struct connection * con, const char *name, const char *variable)
00104 {
00105 struct statement *stmt;
00106 struct prepared_statement *this;
00107 PGresult *query;
00108
00109
00110 this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
00111 if (!this)
00112 return false;
00113
00114 stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
00115 if (!stmt)
00116 {
00117 ecpg_free(this);
00118 return false;
00119 }
00120
00121
00122 stmt->lineno = lineno;
00123 stmt->connection = con;
00124 stmt->command = ecpg_strdup(variable, lineno);
00125 stmt->inlist = stmt->outlist = NULL;
00126
00127
00128 replace_variables(&(stmt->command), lineno);
00129
00130
00131 this->name = ecpg_strdup(name, lineno);
00132 this->stmt = stmt;
00133
00134
00135 query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL);
00136 if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat))
00137 {
00138 ecpg_free(stmt->command);
00139 ecpg_free(this->name);
00140 ecpg_free(this);
00141 ecpg_free(stmt);
00142 return false;
00143 }
00144
00145 ecpg_log("prepare_common on line %d: name %s; query: \"%s\"\n", stmt->lineno, name, stmt->command);
00146 PQclear(query);
00147 this->prepared = true;
00148
00149 if (con->prep_stmts == NULL)
00150 this->next = NULL;
00151 else
00152 this->next = con->prep_stmts;
00153
00154 con->prep_stmts = this;
00155 return true;
00156 }
00157
00158
00159
00160 bool
00161 ECPGprepare(int lineno, const char *connection_name, const bool questionmarks, const char *name, const char *variable)
00162 {
00163 struct connection *con;
00164 struct prepared_statement *this,
00165 *prev;
00166
00167 (void) questionmarks;
00168 con = ecpg_get_connection(connection_name);
00169
00170 if (!ecpg_init(con, connection_name, lineno))
00171 return false;
00172
00173
00174 this = ecpg_find_prepared_statement(name, con, &prev);
00175 if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
00176 return false;
00177
00178 return prepare_common(lineno, con, name, variable);
00179 }
00180
00181 struct prepared_statement *
00182 ecpg_find_prepared_statement(const char *name,
00183 struct connection * con, struct prepared_statement ** prev_)
00184 {
00185 struct prepared_statement *this,
00186 *prev;
00187
00188 for (this = con->prep_stmts, prev = NULL; this != NULL; prev = this, this = this->next)
00189 {
00190 if (strcmp(this->name, name) == 0)
00191 {
00192 if (prev_)
00193 *prev_ = prev;
00194 return this;
00195 }
00196 }
00197 return NULL;
00198 }
00199
00200 static bool
00201 deallocate_one(int lineno, enum COMPAT_MODE c, struct connection * con, struct prepared_statement * prev, struct prepared_statement * this)
00202 {
00203 bool r = false;
00204
00205 ecpg_log("deallocate_one on line %d: name %s\n", lineno, this->name);
00206
00207
00208 if (this->prepared)
00209 {
00210 char *text;
00211 PGresult *query;
00212
00213 text = (char *) ecpg_alloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);
00214
00215 if (text)
00216 {
00217 sprintf(text, "deallocate \"%s\"", this->name);
00218 query = PQexec(this->stmt->connection->connection, text);
00219 ecpg_free(text);
00220 if (ecpg_check_PQresult(query, lineno, this->stmt->connection->connection, this->stmt->compat))
00221 {
00222 PQclear(query);
00223 r = true;
00224 }
00225 }
00226 }
00227
00228
00229
00230
00231
00232 if (!r && !INFORMIX_MODE(c))
00233 {
00234 ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name);
00235 return false;
00236 }
00237
00238
00239 ecpg_free(this->stmt->command);
00240 ecpg_free(this->stmt);
00241 ecpg_free(this->name);
00242 if (prev != NULL)
00243 prev->next = this->next;
00244 else
00245 con->prep_stmts = this->next;
00246
00247 ecpg_free(this);
00248 return true;
00249 }
00250
00251
00252 bool
00253 ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name)
00254 {
00255 struct connection *con;
00256 struct prepared_statement *this,
00257 *prev;
00258
00259 con = ecpg_get_connection(connection_name);
00260
00261 if (!ecpg_init(con, connection_name, lineno))
00262 return false;
00263
00264 this = ecpg_find_prepared_statement(name, con, &prev);
00265 if (this)
00266 return deallocate_one(lineno, c, con, prev, this);
00267
00268
00269 if (INFORMIX_MODE(c))
00270 return true;
00271 ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
00272 return false;
00273 }
00274
00275 bool
00276 ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection * con)
00277 {
00278
00279 while (con->prep_stmts)
00280 {
00281 if (!deallocate_one(lineno, c, con, NULL, con->prep_stmts))
00282 return false;
00283 }
00284
00285 return true;
00286 }
00287
00288 bool
00289 ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
00290 {
00291 return ecpg_deallocate_all_conn(lineno, compat, ecpg_get_connection(connection_name));
00292 }
00293
00294 char *
00295 ecpg_prepared(const char *name, struct connection * con)
00296 {
00297 struct prepared_statement *this;
00298
00299 this = ecpg_find_prepared_statement(name, con, NULL);
00300 return this ? this->stmt->command : NULL;
00301 }
00302
00303
00304
00305 char *
00306 ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
00307 {
00308 (void) lineno;
00309 return ecpg_prepared(name, ecpg_get_connection(connection_name));
00310 }
00311
00312
00313
00314
00315 static int
00316 HashStmt(const char *ecpgQuery)
00317 {
00318 int stmtIx,
00319 bucketNo,
00320 hashLeng,
00321 stmtLeng;
00322 long long hashVal,
00323 rotVal;
00324
00325 stmtLeng = strlen(ecpgQuery);
00326 hashLeng = 50;
00327 if (hashLeng > stmtLeng)
00328 hashLeng = stmtLeng;
00329
00330 hashVal = 0;
00331 for (stmtIx = 0; stmtIx < hashLeng; ++stmtIx)
00332 {
00333 hashVal = hashVal + (int) ecpgQuery[stmtIx];
00334 hashVal = hashVal << 13;
00335 rotVal = (hashVal & 0x1fff00000000LL) >> 32;
00336 hashVal = (hashVal & 0xffffffffLL) | rotVal;
00337 }
00338
00339 bucketNo = hashVal % stmtCacheNBuckets;
00340 bucketNo += 1;
00341
00342 return (bucketNo * stmtCacheEntPerBucket);
00343 }
00344
00345
00346
00347
00348
00349
00350 static int
00351 SearchStmtCache(const char *ecpgQuery)
00352 {
00353 int entNo,
00354 entIx;
00355
00356
00357 entNo = HashStmt(ecpgQuery);
00358
00359
00360 for (entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx)
00361 {
00362 if (stmtCacheEntries[entNo].stmtID[0])
00363 {
00364 if (strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery) == 0)
00365 break;
00366 }
00367 ++entNo;
00368 }
00369
00370
00371 if (entIx >= stmtCacheEntPerBucket)
00372 entNo = 0;
00373
00374 return (entNo);
00375 }
00376
00377
00378
00379
00380
00381
00382 static int
00383 ecpg_freeStmtCacheEntry(int lineno, int compat, int entNo)
00384 {
00385 stmtCacheEntry *entry;
00386 struct connection *con;
00387 struct prepared_statement *this,
00388 *prev;
00389
00390 entry = &stmtCacheEntries[entNo];
00391 if (!entry->stmtID[0])
00392 return (0);
00393
00394 con = ecpg_get_connection(entry->connection);
00395
00396
00397 this = ecpg_find_prepared_statement(entry->stmtID, con, &prev);
00398 if (this && !deallocate_one(lineno, compat, con, prev, this))
00399 return (-1);
00400
00401 entry->stmtID[0] = '\0';
00402
00403
00404 if (entry->ecpgQuery)
00405 {
00406 ecpg_free(entry->ecpgQuery);
00407 entry->ecpgQuery = 0;
00408 }
00409
00410 return (entNo);
00411 }
00412
00413
00414
00415
00416
00417 static int
00418 AddStmtToCache(int lineno,
00419 const char *stmtID,
00420 const char *connection,
00421 int compat,
00422 const char *ecpgQuery)
00423 {
00424 int ix,
00425 initEntNo,
00426 luEntNo,
00427 entNo;
00428 stmtCacheEntry *entry;
00429
00430
00431 initEntNo = HashStmt(ecpgQuery);
00432
00433
00434 entNo = initEntNo;
00435
00436 luEntNo = initEntNo;
00437 for (ix = 0; ix < stmtCacheEntPerBucket; ++ix)
00438 {
00439 entry = &stmtCacheEntries[entNo];
00440 if (!entry->stmtID[0])
00441 break;
00442 if (entry->execs < stmtCacheEntries[luEntNo].execs)
00443 luEntNo = entNo;
00444 ++entNo;
00445 }
00446
00447
00448 if (ix >= stmtCacheEntPerBucket)
00449 entNo = luEntNo;
00450
00451
00452 if (ecpg_freeStmtCacheEntry(lineno, compat, entNo) < 0)
00453 return (-1);
00454
00455
00456 entry = &stmtCacheEntries[entNo];
00457 entry->lineno = lineno;
00458 entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno);
00459 entry->connection = connection;
00460 entry->execs = 0;
00461 memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID));
00462
00463 return (entNo);
00464 }
00465
00466
00467 bool
00468 ecpg_auto_prepare(int lineno, const char *connection_name, const int compat, char **name, const char *query)
00469 {
00470 int entNo;
00471
00472
00473 entNo = SearchStmtCache(query);
00474
00475
00476 if (entNo)
00477 {
00478 char *stmtID;
00479 struct connection *con;
00480 struct prepared_statement *prep;
00481
00482 ecpg_log("ecpg_auto_prepare on line %d: statement found in cache; entry %d\n", lineno, entNo);
00483
00484 stmtID = stmtCacheEntries[entNo].stmtID;
00485
00486 con = ecpg_get_connection(connection_name);
00487 prep = ecpg_find_prepared_statement(stmtID, con, NULL);
00488
00489 if (!prep && !prepare_common(lineno, con, stmtID, query))
00490 return (false);
00491
00492 *name = ecpg_strdup(stmtID, lineno);
00493 }
00494 else
00495 {
00496 char stmtID[STMTID_SIZE];
00497
00498 ecpg_log("ecpg_auto_prepare on line %d: statement not in cache; inserting\n", lineno);
00499
00500
00501 sprintf(stmtID, "ecpg%d", nextStmtID++);
00502
00503 if (!ECPGprepare(lineno, connection_name, 0, stmtID, query))
00504 return (false);
00505 if (AddStmtToCache(lineno, stmtID, connection_name, compat, query) < 0)
00506 return (false);
00507
00508 *name = ecpg_strdup(stmtID, lineno);
00509 }
00510
00511
00512 stmtCacheEntries[entNo].execs++;
00513
00514 return (true);
00515 }