diff options
Diffstat (limited to 'src/interfaces/ecpg/ecpglib/prepare.c')
-rw-r--r-- | src/interfaces/ecpg/ecpglib/prepare.c | 363 |
1 files changed, 189 insertions, 174 deletions
diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c index 2433478a05b..930bb4f64e6 100644 --- a/src/interfaces/ecpg/ecpglib/prepare.c +++ b/src/interfaces/ecpg/ecpglib/prepare.c @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.23 2007/11/05 20:57:24 tgl Exp $ */ +/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.24 2007/11/15 21:14:45 momjian Exp $ */ #define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" @@ -13,32 +13,32 @@ struct prepared_statement { - char *name; - bool prepared; - struct statement *stmt; - struct prepared_statement *next; + char *name; + bool prepared; + struct statement *stmt; + struct prepared_statement *next; }; #define STMTID_SIZE 32 -typedef struct +typedef struct { - int lineno; - char stmtID[STMTID_SIZE]; - char *ecpgQuery; - long execs; /* # of executions */ - char *connection; /* connection for the statement */ -} stmtCacheEntry; - -static int nextStmtID = 1; -static const int stmtCacheNBuckets = 2039; /* # buckets - a prime # */ -static const int stmtCacheEntPerBucket = 8; /* # entries/bucket */ -static stmtCacheEntry stmtCacheEntries[16384] = {{0,{0},0,0,0}}; + int lineno; + char stmtID[STMTID_SIZE]; + char *ecpgQuery; + long execs; /* # of executions */ + char *connection; /* connection for the statement */ +} stmtCacheEntry; + +static int nextStmtID = 1; +static const int stmtCacheNBuckets = 2039; /* # buckets - a prime # */ +static const int stmtCacheEntPerBucket = 8; /* # entries/bucket */ +static stmtCacheEntry stmtCacheEntries[16384] = {{0, {0}, 0, 0, 0}}; static struct prepared_statement *find_prepared_statement(const char *name, - struct connection *con, struct prepared_statement **prev); -static bool deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con, - struct prepared_statement *prev, struct prepared_statement *this); + struct connection * con, struct prepared_statement ** prev); +static bool deallocate_one(int lineno, enum COMPAT_MODE c, struct connection * con, + struct prepared_statement * prev, struct prepared_statement * this); static bool isvarchar(unsigned char c) @@ -58,8 +58,9 @@ isvarchar(unsigned char c) static bool replace_variables(char **text, int lineno, bool questionmarks) { - bool string = false; - int counter = 1, ptr = 0; + bool string = false; + int counter = 1, + ptr = 0; for (; (*text)[ptr] != '\0'; ptr++) { @@ -69,21 +70,23 @@ replace_variables(char **text, int lineno, bool questionmarks) if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?'))) continue; - if (((*text)[ptr] == ':') && ((*text)[ptr+1] == ':')) - ptr += 2; /* skip '::' */ + if (((*text)[ptr] == ':') && ((*text)[ptr + 1] == ':')) + ptr += 2; /* skip '::' */ else { - int len; - int buffersize = sizeof(int) * CHAR_BIT * 10 / 3; /* a rough guess of the size we need */ - char *buffer, *newcopy; + int len; + int buffersize = sizeof(int) * CHAR_BIT * 10 / 3; /* a rough guess of the + * size we need */ + char *buffer, + *newcopy; if (!(buffer = (char *) ecpg_alloc(buffersize, lineno))) return false; snprintf(buffer, buffersize, "$%d", counter++); - for (len=1; (*text)[ptr+len] && isvarchar((*text)[ptr+len]); len++); - if (!(newcopy = (char *) ecpg_alloc(strlen(*text) - len + strlen(buffer) + 1, lineno))) + for (len = 1; (*text)[ptr + len] && isvarchar((*text)[ptr + len]); len++); + if (!(newcopy = (char *) ecpg_alloc(strlen(*text) -len + strlen(buffer) + 1, lineno))) { ecpg_free(buffer); return false; @@ -91,16 +94,16 @@ replace_variables(char **text, int lineno, bool questionmarks) strncpy(newcopy, *text, ptr); strcpy(newcopy + ptr, buffer); - strcat(newcopy, (*text) + ptr + len); + strcat(newcopy, (*text) +ptr + len); ecpg_free(*text); ecpg_free(buffer); *text = newcopy; - if ((*text)[ptr] == '\0') /* we reached the end */ - ptr--; /* since we will (*text)[ptr]++ in the top level for - * loop */ + if ((*text)[ptr] == '\0') /* we reached the end */ + ptr--; /* since we will (*text)[ptr]++ in the top + * level for loop */ } } return true; @@ -110,10 +113,10 @@ replace_variables(char **text, int lineno, bool questionmarks) bool ECPGprepare(int lineno, const char *connection_name, const int questionmarks, const char *name, const char *variable) { - struct connection *con; - struct statement *stmt; - struct prepared_statement *this, - *prev; + struct connection *con; + struct statement *stmt; + struct prepared_statement *this, + *prev; struct sqlca_t *sqlca = ECPGget_sqlca(); PGresult *query; @@ -174,11 +177,12 @@ ECPGprepare(int lineno, const char *connection_name, const int questionmarks, co return true; } -static struct prepared_statement *find_prepared_statement(const char *name, - struct connection *con, struct prepared_statement **prev_) +static struct prepared_statement * +find_prepared_statement(const char *name, + struct connection * con, struct prepared_statement ** prev_) { - struct prepared_statement *this, - *prev; + struct prepared_statement *this, + *prev; for (this = con->prep_stmts, prev = NULL; this != NULL; prev = this, this = this->next) { @@ -193,19 +197,20 @@ static struct prepared_statement *find_prepared_statement(const char *name, } static bool -deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con, struct prepared_statement *prev, struct prepared_statement *this) +deallocate_one(int lineno, enum COMPAT_MODE c, struct connection * con, struct prepared_statement * prev, struct prepared_statement * this) { - bool r = false; + bool r = false; ecpg_log("ECPGdeallocate line %d: NAME: %s\n", lineno, this->name); /* first deallocate the statement in the backend */ if (this->prepared) { - char *text; - PGresult *query; - + char *text; + PGresult *query; + text = (char *) ecpg_alloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno); + if (text) { sprintf(text, "deallocate \"%s\"", this->name); @@ -220,15 +225,15 @@ deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con, struct pr } /* - * Just ignore all errors since we do not know the list of cursors we - * are allowed to free. We have to trust the software. + * Just ignore all errors since we do not know the list of cursors we are + * allowed to free. We have to trust the software. */ if (!r && !INFORMIX_MODE(c)) { ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name); return false; } - + /* okay, free all the resources */ ecpg_free(this->stmt->command); ecpg_free(this->stmt); @@ -245,9 +250,9 @@ deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con, struct pr bool ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name) { - struct connection *con; - struct prepared_statement *this, - *prev; + struct connection *con; + struct prepared_statement *this, + *prev; con = ecpg_get_connection(connection_name); @@ -263,7 +268,7 @@ ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name) } bool -ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *con) +ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection * con) { /* deallocate all prepared statements */ while (con->prep_stmts) @@ -282,9 +287,10 @@ ECPGdeallocate_all(int lineno, int compat, const char *connection_name) } char * -ecpg_prepared(const char *name, struct connection *con, int lineno) +ecpg_prepared(const char *name, struct connection * con, int lineno) { - struct prepared_statement *this; + struct prepared_statement *this; + this = find_prepared_statement(name, con, NULL); return this ? this->stmt->command : NULL; } @@ -302,97 +308,102 @@ ECPGprepared_statement(const char *connection_name, const char *name, int lineno static int HashStmt(const char *ecpgQuery) { - int stmtIx, bucketNo, hashLeng, stmtLeng; - long long hashVal, rotVal; - - stmtLeng = strlen(ecpgQuery); - hashLeng = 50; /* use 1st 50 characters of statement */ - if(hashLeng > stmtLeng) /* if the statement isn't that long */ - hashLeng = stmtLeng; /* use its actual length */ - - hashVal = 0; - for(stmtIx = 0; stmtIx < hashLeng; ++stmtIx) - { - hashVal = hashVal + (int) ecpgQuery[stmtIx]; - hashVal = hashVal << 13; - rotVal = (hashVal & 0x1fff00000000LL) >> 32; - hashVal = (hashVal & 0xffffffffLL) | rotVal; - } - - bucketNo = hashVal % stmtCacheNBuckets; - bucketNo += 1; /* don't use bucket # 0 */ - - return (bucketNo * stmtCacheEntPerBucket); + int stmtIx, + bucketNo, + hashLeng, + stmtLeng; + long long hashVal, + rotVal; + + stmtLeng = strlen(ecpgQuery); + hashLeng = 50; /* use 1st 50 characters of statement */ + if (hashLeng > stmtLeng) /* if the statement isn't that long */ + hashLeng = stmtLeng; /* use its actual length */ + + hashVal = 0; + for (stmtIx = 0; stmtIx < hashLeng; ++stmtIx) + { + hashVal = hashVal + (int) ecpgQuery[stmtIx]; + hashVal = hashVal << 13; + rotVal = (hashVal & 0x1fff00000000LL) >> 32; + hashVal = (hashVal & 0xffffffffLL) | rotVal; + } + + bucketNo = hashVal % stmtCacheNBuckets; + bucketNo += 1; /* don't use bucket # 0 */ + + return (bucketNo * stmtCacheEntPerBucket); } /* * search the statement cache - search for entry with matching ECPG-format query * Returns entry # in cache if found - * OR zero if not present (zero'th entry isn't used) + * OR zero if not present (zero'th entry isn't used) */ static int SearchStmtCache(const char *ecpgQuery) { - int entNo, entIx; - -/* hash the statement */ - entNo = HashStmt(ecpgQuery); - -/* search the cache */ - for(entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx) - { - if(stmtCacheEntries[entNo].stmtID[0]) /* check if entry is in use */ - { - if(!strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery)) - break; /* found it */ - } - ++entNo; /* incr entry # */ - } + int entNo, + entIx; + +/* hash the statement */ + entNo = HashStmt(ecpgQuery); + +/* search the cache */ + for (entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx) + { + if (stmtCacheEntries[entNo].stmtID[0]) /* check if entry is in use */ + { + if (!strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery)) + break; /* found it */ + } + ++entNo; /* incr entry # */ + } /* if entry wasn't found - set entry # to zero */ - if(entIx >= stmtCacheEntPerBucket) - entNo = 0; + if (entIx >= stmtCacheEntPerBucket) + entNo = 0; - return(entNo); + return (entNo); } /* * free an entry in the statement cache * Returns entry # in cache used - * OR negative error code + * OR negative error code */ static int -ecpg_freeStmtCacheEntry(int entNo) /* entry # to free */ +ecpg_freeStmtCacheEntry(int entNo) /* entry # to free */ { - stmtCacheEntry *entry; - PGresult *results; - char deallocText[100]; - struct connection *con; - - entry = &stmtCacheEntries[entNo]; - if(!entry->stmtID[0]) /* return if the entry isn't in use */ - return(0); - - con = ecpg_get_connection(entry->connection); -/* free the server resources for the statement */ - ecpg_log("ecpg_freeStmtCacheEntry line %d: deallocate %s, cache entry #%d\n", entry->lineno, entry->stmtID, entNo); - sprintf(deallocText, "DEALLOCATE PREPARE %s", entry->stmtID); - results = PQexec(con->connection, deallocText); - - if (!ecpg_check_PQresult(results, entry->lineno, con->connection, ECPG_COMPAT_PGSQL)) - return(-1); - PQclear(results); - - entry->stmtID[0] = '\0'; - -/* free the memory used by the cache entry */ - if(entry->ecpgQuery) - { - ecpg_free(entry->ecpgQuery); - entry->ecpgQuery = 0; - } - - return(entNo); + stmtCacheEntry *entry; + PGresult *results; + char deallocText[100]; + struct connection *con; + + entry = &stmtCacheEntries[entNo]; + if (!entry->stmtID[0]) /* return if the entry isn't in use */ + return (0); + + con = ecpg_get_connection(entry->connection); +/* free the server resources for the statement */ + ecpg_log("ecpg_freeStmtCacheEntry line %d: deallocate %s, cache entry #%d\n", entry->lineno, entry->stmtID, entNo); + sprintf(deallocText, "DEALLOCATE PREPARE %s", entry->stmtID); + results = PQexec(con->connection, deallocText); + + if (!ecpg_check_PQresult(results, entry->lineno, con->connection, ECPG_COMPAT_PGSQL)) + return (-1); + PQclear(results); + + entry->stmtID[0] = '\0'; + +/* free the memory used by the cache entry */ + if (entry->ecpgQuery) + { + ecpg_free(entry->ecpgQuery); + entry->ecpgQuery = 0; + } + + return (entNo); } /* @@ -400,63 +411,67 @@ ecpg_freeStmtCacheEntry(int entNo) /* entry # to free */ * returns entry # in cache used OR negative error code */ static int -AddStmtToCache(int lineno, /* line # of statement */ - char *stmtID, /* statement ID */ - const char *connection, /* connection */ - const char *ecpgQuery) /* query */ +AddStmtToCache(int lineno, /* line # of statement */ + char *stmtID, /* statement ID */ + const char *connection, /* connection */ + const char *ecpgQuery) /* query */ { - int ix, initEntNo, luEntNo, entNo; - stmtCacheEntry *entry; - -/* hash the statement */ - initEntNo = HashStmt(ecpgQuery); - -/* search for an unused entry */ - entNo = initEntNo; /* start with the initial entry # for the bucket */ - luEntNo = initEntNo; /* use it as the initial 'least used' entry */ - for(ix = 0; ix < stmtCacheEntPerBucket; ++ix) - { - entry = &stmtCacheEntries[entNo]; - if(!entry->stmtID[0]) /* unused entry - use it */ - break; - if(entry->execs < stmtCacheEntries[luEntNo].execs) - luEntNo = entNo; /* save new 'least used' entry */ - ++entNo; /* increment entry # */ - } - -/* if no unused entries were found - use the 'least used' entry found in the bucket */ - if(ix >= stmtCacheEntPerBucket) /* if no unused entries were found */ - entNo = luEntNo; /* re-use the 'least used' entry */ - -/* 'entNo' is the entry to use - make sure its free */ - if (ecpg_freeStmtCacheEntry(entNo) < 0) - return (-1); - -/* add the query to the entry */ - entry = &stmtCacheEntries[entNo]; - entry->lineno = lineno; - entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno); - entry->connection = (char *)connection; - entry->execs = 0; - memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID)); - - return(entNo); + int ix, + initEntNo, + luEntNo, + entNo; + stmtCacheEntry *entry; + +/* hash the statement */ + initEntNo = HashStmt(ecpgQuery); + +/* search for an unused entry */ + entNo = initEntNo; /* start with the initial entry # for the + * bucket */ + luEntNo = initEntNo; /* use it as the initial 'least used' entry */ + for (ix = 0; ix < stmtCacheEntPerBucket; ++ix) + { + entry = &stmtCacheEntries[entNo]; + if (!entry->stmtID[0]) /* unused entry - use it */ + break; + if (entry->execs < stmtCacheEntries[luEntNo].execs) + luEntNo = entNo; /* save new 'least used' entry */ + ++entNo; /* increment entry # */ + } + +/* if no unused entries were found - use the 'least used' entry found in the bucket */ + if (ix >= stmtCacheEntPerBucket) /* if no unused entries were found */ + entNo = luEntNo; /* re-use the 'least used' entry */ + +/* 'entNo' is the entry to use - make sure its free */ + if (ecpg_freeStmtCacheEntry(entNo) < 0) + return (-1); + +/* add the query to the entry */ + entry = &stmtCacheEntries[entNo]; + entry->lineno = lineno; + entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno); + entry->connection = (char *) connection; + entry->execs = 0; + memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID)); + + return (entNo); } /* handle cache and preparation of statments in auto-prepare mode */ bool ecpg_auto_prepare(int lineno, const char *connection_name, const int questionmarks, char **name, const char *query) { - int entNo; + int entNo; - /* search the statement cache for this statement */ + /* search the statement cache for this statement */ entNo = SearchStmtCache(query); - /* if not found - add the statement to the cache */ - if(entNo) + /* if not found - add the statement to the cache */ + if (entNo) { ecpg_log("ecpg_auto_prepare line %d: stmt found in cache, entry %d\n", lineno, entNo); - *name = ecpg_strdup(stmtCacheEntries[entNo].stmtID, lineno); + *name = ecpg_strdup(stmtCacheEntries[entNo].stmtID, lineno); } else { @@ -467,13 +482,13 @@ ecpg_auto_prepare(int lineno, const char *connection_name, const int questionmar sprintf(*name, "ecpg%d", nextStmtID++); if (!ECPGprepare(lineno, connection_name, questionmarks, ecpg_strdup(*name, lineno), query)) - return(false); + return (false); if (AddStmtToCache(lineno, *name, connection_name, query) < 0) - return(false); + return (false); } /* increase usage counter */ stmtCacheEntries[entNo].execs++; - return(true); + return (true); } |