Header And Logo

PostgreSQL
| The world's most advanced open source database.

prepare.c

Go to the documentation of this file.
00001 /* src/interfaces/ecpg/ecpglib/prepare.c */
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;          /* # of executions      */
00022     const char *connection;     /* connection for the statement     */
00023 } stmtCacheEntry;
00024 
00025 static int  nextStmtID = 1;
00026 static const int stmtCacheNBuckets = 2039;      /* # buckets - a prime # */
00027 static const int stmtCacheEntPerBucket = 8;     /* # entries/bucket     */
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;           /* skip  '::' */
00065         else
00066         {
00067             int         len;
00068             int         buffersize = sizeof(int) * CHAR_BIT * 10 / 3;   /* a rough guess of the
00069                                                                          * size we need */
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')   /* we reached the end */
00095                 ptr--;          /* since we will (*text)[ptr]++ in the top
00096                                  * level for loop */
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     /* allocate new statement */
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     /* create statement */
00122     stmt->lineno = lineno;
00123     stmt->connection = con;
00124     stmt->command = ecpg_strdup(variable, lineno);
00125     stmt->inlist = stmt->outlist = NULL;
00126 
00127     /* if we have C variables in our statement replace them with '?' */
00128     replace_variables(&(stmt->command), lineno);
00129 
00130     /* add prepared statement to our list */
00131     this->name = ecpg_strdup(name, lineno);
00132     this->stmt = stmt;
00133 
00134     /* and finally really prepare the statement */
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 /* handle the EXEC SQL PREPARE statement */
00159 /* questionmarks is not needed but remains in there for the time being to not change the API */
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;       /* quiet the compiler */
00168     con = ecpg_get_connection(connection_name);
00169 
00170     if (!ecpg_init(con, connection_name, lineno))
00171         return false;
00172 
00173     /* check if we already have prepared this statement */
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     /* first deallocate the statement in the backend */
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      * Just ignore all errors since we do not know the list of cursors we are
00230      * allowed to free. We have to trust the software.
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     /* okay, free all the resources */
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 /* handle the EXEC SQL DEALLOCATE PREPARE statement */
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     /* prepared statement is not found */
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     /* deallocate all prepared statements */
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 /* return the prepared statement */
00304 /* lineno is not used here, but kept in to not break API */
00305 char *
00306 ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
00307 {
00308     (void) lineno;              /* keep the compiler quiet */
00309     return ecpg_prepared(name, ecpg_get_connection(connection_name));
00310 }
00311 
00312 /*
00313  * hash a SQL statement -  returns entry # of first entry in the bucket
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;              /* use 1st 50 characters of statement       */
00327     if (hashLeng > stmtLeng)    /* if the statement isn't that long         */
00328         hashLeng = stmtLeng;    /* use its actual length               */
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;              /* don't use bucket # 0         */
00341 
00342     return (bucketNo * stmtCacheEntPerBucket);
00343 }
00344 
00345 /*
00346  * search the statement cache - search for entry with matching ECPG-format query
00347  * Returns entry # in cache if found
00348  *   OR  zero if not present (zero'th entry isn't used)
00349  */
00350 static int
00351 SearchStmtCache(const char *ecpgQuery)
00352 {
00353     int         entNo,
00354                 entIx;
00355 
00356 /* hash the statement           */
00357     entNo = HashStmt(ecpgQuery);
00358 
00359 /* search the cache     */
00360     for (entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx)
00361     {
00362         if (stmtCacheEntries[entNo].stmtID[0])  /* check if entry is in use     */
00363         {
00364             if (strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery) == 0)
00365                 break;          /* found it     */
00366         }
00367         ++entNo;                /* incr entry #     */
00368     }
00369 
00370 /* if entry wasn't found - set entry # to zero  */
00371     if (entIx >= stmtCacheEntPerBucket)
00372         entNo = 0;
00373 
00374     return (entNo);
00375 }
00376 
00377 /*
00378  * free an entry in the statement cache
00379  * Returns entry # in cache used
00380  *   OR  negative error code
00381  */
00382 static int
00383 ecpg_freeStmtCacheEntry(int lineno, int compat, int entNo)      /* entry # to free */
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])      /* return if the entry isn't in use     */
00392         return (0);
00393 
00394     con = ecpg_get_connection(entry->connection);
00395 
00396     /* free the 'prepared_statement' list entry       */
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     /* free the memory used by the cache entry      */
00404     if (entry->ecpgQuery)
00405     {
00406         ecpg_free(entry->ecpgQuery);
00407         entry->ecpgQuery = 0;
00408     }
00409 
00410     return (entNo);
00411 }
00412 
00413 /*
00414  * add an entry to the statement cache
00415  * returns entry # in cache used  OR  negative error code
00416  */
00417 static int
00418 AddStmtToCache(int lineno,      /* line # of statement      */
00419                const char *stmtID,      /* statement ID             */
00420                const char *connection,  /* connection               */
00421                int compat,      /* compatibility level */
00422                const char *ecpgQuery)   /* query                    */
00423 {
00424     int         ix,
00425                 initEntNo,
00426                 luEntNo,
00427                 entNo;
00428     stmtCacheEntry *entry;
00429 
00430 /* hash the statement                                                                   */
00431     initEntNo = HashStmt(ecpgQuery);
00432 
00433 /* search for an unused entry                                                           */
00434     entNo = initEntNo;          /* start with the initial entry # for the
00435                                  * bucket    */
00436     luEntNo = initEntNo;        /* use it as the initial 'least used' entry         */
00437     for (ix = 0; ix < stmtCacheEntPerBucket; ++ix)
00438     {
00439         entry = &stmtCacheEntries[entNo];
00440         if (!entry->stmtID[0])  /* unused entry  -  use it          */
00441             break;
00442         if (entry->execs < stmtCacheEntries[luEntNo].execs)
00443             luEntNo = entNo;    /* save new 'least used' entry      */
00444         ++entNo;                /* increment entry #                */
00445     }
00446 
00447 /* if no unused entries were found - use the 'least used' entry found in the bucket     */
00448     if (ix >= stmtCacheEntPerBucket)    /* if no unused entries were found  */
00449         entNo = luEntNo;        /* re-use the 'least used' entry    */
00450 
00451 /* 'entNo' is the entry to use - make sure its free                                     */
00452     if (ecpg_freeStmtCacheEntry(lineno, compat, entNo) < 0)
00453         return (-1);
00454 
00455 /* add the query to the entry                                                           */
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 /* handle cache and preparation of statements in auto-prepare mode */
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     /* search the statement cache for this statement    */
00473     entNo = SearchStmtCache(query);
00474 
00475     /* if not found - add the statement to the cache    */
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         /* This prepared name doesn't exist on this connection. */
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         /* generate a statement ID */
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     /* increase usage counter */
00512     stmtCacheEntries[entNo].execs++;
00513 
00514     return (true);
00515 }