#include "postgres_fe.h"#include <ctype.h>#include "ecpgtype.h"#include "ecpglib.h"#include "ecpgerrno.h"#include "extern.h"#include "sqlca.h"
Go to the source code of this file.
Data Structures | |
| struct | stmtCacheEntry |
Defines | |
| #define | POSTGRES_ECPG_INTERNAL |
| #define | STMTID_SIZE 32 |
Functions | |
| 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) |
| static bool | replace_variables (char **text, int lineno) |
| static bool | prepare_common (int lineno, struct connection *con, const char *name, const char *variable) |
| bool | ECPGprepare (int lineno, const char *connection_name, const bool questionmarks, const char *name, const char *variable) |
| struct prepared_statement * | ecpg_find_prepared_statement (const char *name, struct connection *con, struct prepared_statement **prev_) |
| bool | 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) |
| bool | ECPGdeallocate_all (int lineno, int compat, const char *connection_name) |
| char * | ecpg_prepared (const char *name, struct connection *con) |
| char * | ECPGprepared_statement (const char *connection_name, const char *name, int lineno) |
| static int | HashStmt (const char *ecpgQuery) |
| static int | SearchStmtCache (const char *ecpgQuery) |
| static int | ecpg_freeStmtCacheEntry (int lineno, int compat, int entNo) |
| static int | AddStmtToCache (int lineno, const char *stmtID, const char *connection, int compat, const char *ecpgQuery) |
| bool | ecpg_auto_prepare (int lineno, const char *connection_name, const int compat, char **name, const char *query) |
Variables | |
| static int | nextStmtID = 1 |
| static const int | stmtCacheNBuckets = 2039 |
| static const int | stmtCacheEntPerBucket = 8 |
| static stmtCacheEntry | stmtCacheEntries [16384] = {{0, {0}, 0, 0, 0}} |
| static int AddStmtToCache | ( | int | lineno, | |
| const char * | stmtID, | |||
| const char * | connection, | |||
| int | compat, | |||
| const char * | ecpgQuery | |||
| ) | [static] |
Definition at line 418 of file prepare.c.
References stmtCacheEntry::connection, ecpg_freeStmtCacheEntry(), ecpg_strdup(), stmtCacheEntry::ecpgQuery, stmtCacheEntry::execs, HashStmt(), stmtCacheEntry::lineno, stmtCacheEntPerBucket, and stmtCacheEntry::stmtID.
Referenced by ecpg_auto_prepare().
{
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(lineno, compat, entNo) < 0)
return (-1);
/* add the query to the entry */
entry = &stmtCacheEntries[entNo];
entry->lineno = lineno;
entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno);
entry->connection = connection;
entry->execs = 0;
memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID));
return (entNo);
}
| static bool deallocate_one | ( | int | lineno, | |
| enum COMPAT_MODE | c, | |||
| struct connection * | con, | |||
| struct prepared_statement * | prev, | |||
| struct prepared_statement * | this | |||
| ) | [static] |
Definition at line 201 of file prepare.c.
References statement::command, connection::connection, statement::connection, ecpg_alloc(), ecpg_check_PQresult(), ecpg_free(), ECPG_INVALID_STMT, ecpg_log(), ecpg_raise(), ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, INFORMIX_MODE, statement::lineno, name, prepared_statement::next, NULL, PQclear(), PQexec(), connection::prep_stmts, prepared_statement::prepared, and prepared_statement::stmt.
Referenced by ecpg_deallocate_all_conn(), ecpg_freeStmtCacheEntry(), ECPGdeallocate(), and ECPGprepare().
{
bool r = false;
ecpg_log("deallocate_one on line %d: name %s\n", lineno, this->name);
/* first deallocate the statement in the backend */
if (this->prepared)
{
char *text;
PGresult *query;
text = (char *) ecpg_alloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);
if (text)
{
sprintf(text, "deallocate \"%s\"", this->name);
query = PQexec(this->stmt->connection->connection, text);
ecpg_free(text);
if (ecpg_check_PQresult(query, lineno, this->stmt->connection->connection, this->stmt->compat))
{
PQclear(query);
r = true;
}
}
}
/*
* 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);
ecpg_free(this->name);
if (prev != NULL)
prev->next = this->next;
else
con->prep_stmts = this->next;
ecpg_free(this);
return true;
}
| bool ecpg_auto_prepare | ( | int | lineno, | |
| const char * | connection_name, | |||
| const int | compat, | |||
| char ** | name, | |||
| const char * | query | |||
| ) |
Definition at line 468 of file prepare.c.
References AddStmtToCache(), ecpg_find_prepared_statement(), ecpg_get_connection(), ecpg_log(), ecpg_strdup(), ECPGprepare(), stmtCacheEntry::execs, nextStmtID, NULL, prepare_common(), SearchStmtCache(), and stmtCacheEntry::stmtID.
Referenced by ECPGdo().
{
int entNo;
/* search the statement cache for this statement */
entNo = SearchStmtCache(query);
/* if not found - add the statement to the cache */
if (entNo)
{
char *stmtID;
struct connection *con;
struct prepared_statement *prep;
ecpg_log("ecpg_auto_prepare on line %d: statement found in cache; entry %d\n", lineno, entNo);
stmtID = stmtCacheEntries[entNo].stmtID;
con = ecpg_get_connection(connection_name);
prep = ecpg_find_prepared_statement(stmtID, con, NULL);
/* This prepared name doesn't exist on this connection. */
if (!prep && !prepare_common(lineno, con, stmtID, query))
return (false);
*name = ecpg_strdup(stmtID, lineno);
}
else
{
char stmtID[STMTID_SIZE];
ecpg_log("ecpg_auto_prepare on line %d: statement not in cache; inserting\n", lineno);
/* generate a statement ID */
sprintf(stmtID, "ecpg%d", nextStmtID++);
if (!ECPGprepare(lineno, connection_name, 0, stmtID, query))
return (false);
if (AddStmtToCache(lineno, stmtID, connection_name, compat, query) < 0)
return (false);
*name = ecpg_strdup(stmtID, lineno);
}
/* increase usage counter */
stmtCacheEntries[entNo].execs++;
return (true);
}
| bool ecpg_deallocate_all_conn | ( | int | lineno, | |
| enum COMPAT_MODE | c, | |||
| struct connection * | con | |||
| ) |
Definition at line 276 of file prepare.c.
References deallocate_one(), NULL, and connection::prep_stmts.
Referenced by ecpg_finish(), and ECPGdeallocate_all().
{
/* deallocate all prepared statements */
while (con->prep_stmts)
{
if (!deallocate_one(lineno, c, con, NULL, con->prep_stmts))
return false;
}
return true;
}
| struct prepared_statement* ecpg_find_prepared_statement | ( | const char * | name, | |
| struct connection * | con, | |||
| struct prepared_statement ** | prev_ | |||
| ) | [read] |
Definition at line 182 of file prepare.c.
References prepared_statement::next, NULL, and connection::prep_stmts.
Referenced by ecpg_auto_prepare(), ecpg_freeStmtCacheEntry(), ecpg_prepared(), ECPGdeallocate(), ECPGdescribe(), and ECPGprepare().
{
struct prepared_statement *this,
*prev;
for (this = con->prep_stmts, prev = NULL; this != NULL; prev = this, this = this->next)
{
if (strcmp(this->name, name) == 0)
{
if (prev_)
*prev_ = prev;
return this;
}
}
return NULL;
}
| static int ecpg_freeStmtCacheEntry | ( | int | lineno, | |
| int | compat, | |||
| int | entNo | |||
| ) | [static] |
Definition at line 383 of file prepare.c.
References stmtCacheEntry::connection, deallocate_one(), ecpg_find_prepared_statement(), ecpg_free(), ecpg_get_connection(), stmtCacheEntry::ecpgQuery, and stmtCacheEntry::stmtID.
Referenced by AddStmtToCache().
{
stmtCacheEntry *entry;
struct connection *con;
struct prepared_statement *this,
*prev;
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 'prepared_statement' list entry */
this = ecpg_find_prepared_statement(entry->stmtID, con, &prev);
if (this && !deallocate_one(lineno, compat, con, prev, this))
return (-1);
entry->stmtID[0] = '\0';
/* free the memory used by the cache entry */
if (entry->ecpgQuery)
{
ecpg_free(entry->ecpgQuery);
entry->ecpgQuery = 0;
}
return (entNo);
}
| char* ecpg_prepared | ( | const char * | name, | |
| struct connection * | con | |||
| ) |
Definition at line 295 of file prepare.c.
References statement::command, ecpg_find_prepared_statement(), NULL, and prepared_statement::stmt.
Referenced by ECPGdo(), and ECPGprepared_statement().
{
struct prepared_statement *this;
this = ecpg_find_prepared_statement(name, con, NULL);
return this ? this->stmt->command : NULL;
}
| bool ECPGdeallocate | ( | int | lineno, | |
| int | c, | |||
| const char * | connection_name, | |||
| const char * | name | |||
| ) |
Definition at line 253 of file prepare.c.
References deallocate_one(), ecpg_find_prepared_statement(), ecpg_get_connection(), ecpg_init(), ECPG_INVALID_STMT, ecpg_raise(), ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, and INFORMIX_MODE.
Referenced by main().
{
struct connection *con;
struct prepared_statement *this,
*prev;
con = ecpg_get_connection(connection_name);
if (!ecpg_init(con, connection_name, lineno))
return false;
this = ecpg_find_prepared_statement(name, con, &prev);
if (this)
return deallocate_one(lineno, c, con, prev, this);
/* prepared statement is not found */
if (INFORMIX_MODE(c))
return true;
ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
return false;
}
| bool ECPGdeallocate_all | ( | int | lineno, | |
| int | compat, | |||
| const char * | connection_name | |||
| ) |
Definition at line 289 of file prepare.c.
References ecpg_deallocate_all_conn(), and ecpg_get_connection().
Referenced by main().
{
return ecpg_deallocate_all_conn(lineno, compat, ecpg_get_connection(connection_name));
}
| bool ECPGprepare | ( | int | lineno, | |
| const char * | connection_name, | |||
| const bool | questionmarks, | |||
| const char * | name, | |||
| const char * | variable | |||
| ) |
Definition at line 161 of file prepare.c.
References deallocate_one(), ECPG_COMPAT_PGSQL, ecpg_find_prepared_statement(), ecpg_get_connection(), ecpg_init(), and prepare_common().
Referenced by ecpg_auto_prepare(), main(), and test().
{
struct connection *con;
struct prepared_statement *this,
*prev;
(void) questionmarks; /* quiet the compiler */
con = ecpg_get_connection(connection_name);
if (!ecpg_init(con, connection_name, lineno))
return false;
/* check if we already have prepared this statement */
this = ecpg_find_prepared_statement(name, con, &prev);
if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
return false;
return prepare_common(lineno, con, name, variable);
}
| char* ECPGprepared_statement | ( | const char * | connection_name, | |
| const char * | name, | |||
| int | lineno | |||
| ) |
Definition at line 306 of file prepare.c.
References ecpg_get_connection(), and ecpg_prepared().
Referenced by main(), and test().
{
(void) lineno; /* keep the compiler quiet */
return ecpg_prepared(name, ecpg_get_connection(connection_name));
}
| static int HashStmt | ( | const char * | ecpgQuery | ) | [static] |
Definition at line 316 of file prepare.c.
References stmtCacheEntPerBucket, and stmtCacheNBuckets.
Referenced by AddStmtToCache(), and SearchStmtCache().
{
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);
}
| static bool isvarchar | ( | unsigned char | c | ) | [static] |
| static bool prepare_common | ( | int | lineno, | |
| struct connection * | con, | |||
| const char * | name, | |||
| const char * | variable | |||
| ) | [static] |
Definition at line 103 of file prepare.c.
References statement::command, statement::compat, connection::connection, statement::connection, ecpg_alloc(), ecpg_check_PQresult(), ecpg_free(), ecpg_log(), ecpg_strdup(), statement::inlist, statement::lineno, next(), NULL, statement::outlist, PQclear(), PQprepare(), connection::prep_stmts, and replace_variables().
Referenced by ecpg_auto_prepare(), and ECPGprepare().
{
struct statement *stmt;
struct prepared_statement *this;
PGresult *query;
/* allocate new statement */
this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
if (!this)
return false;
stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
if (!stmt)
{
ecpg_free(this);
return false;
}
/* create statement */
stmt->lineno = lineno;
stmt->connection = con;
stmt->command = ecpg_strdup(variable, lineno);
stmt->inlist = stmt->outlist = NULL;
/* if we have C variables in our statement replace them with '?' */
replace_variables(&(stmt->command), lineno);
/* add prepared statement to our list */
this->name = ecpg_strdup(name, lineno);
this->stmt = stmt;
/* and finally really prepare the statement */
query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL);
if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat))
{
ecpg_free(stmt->command);
ecpg_free(this->name);
ecpg_free(this);
ecpg_free(stmt);
return false;
}
ecpg_log("prepare_common on line %d: name %s; query: \"%s\"\n", stmt->lineno, name, stmt->command);
PQclear(query);
this->prepared = true;
if (con->prep_stmts == NULL)
this->next = NULL;
else
this->next = con->prep_stmts;
con->prep_stmts = this;
return true;
}
| static bool replace_variables | ( | char ** | text, | |
| int | lineno | |||
| ) | [static] |
Definition at line 49 of file prepare.c.
References ecpg_alloc(), ecpg_free(), isvarchar(), and snprintf().
Referenced by prepare_common().
{
bool string = false;
int counter = 1,
ptr = 0;
for (; (*text)[ptr] != '\0'; ptr++)
{
if ((*text)[ptr] == '\'')
string = string ? false : true;
if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?')))
continue;
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;
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)))
{
ecpg_free(buffer);
return false;
}
strncpy(newcopy, *text, ptr);
strcpy(newcopy + ptr, buffer);
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 */
}
}
return true;
}
| static int SearchStmtCache | ( | const char * | ecpgQuery | ) | [static] |
Definition at line 351 of file prepare.c.
References HashStmt(), and stmtCacheEntPerBucket.
Referenced by ecpg_auto_prepare().
{
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) == 0)
break; /* found it */
}
++entNo; /* incr entry # */
}
/* if entry wasn't found - set entry # to zero */
if (entIx >= stmtCacheEntPerBucket)
entNo = 0;
return (entNo);
}
int nextStmtID = 1 [static] |
Definition at line 25 of file prepare.c.
Referenced by ecpg_auto_prepare().
const int stmtCacheEntPerBucket = 8 [static] |
Definition at line 27 of file prepare.c.
Referenced by AddStmtToCache(), HashStmt(), and SearchStmtCache().
stmtCacheEntry stmtCacheEntries[16384] = {{0, {0}, 0, 0, 0}} [static] |
const int stmtCacheNBuckets = 2039 [static] |
Definition at line 26 of file prepare.c.
Referenced by HashStmt().
1.7.1