1/* src/interfaces/ecpg/ecpglib/prepare.c */
3 #define POSTGRES_ECPG_INTERNAL
14 #define STMTID_SIZE 32
17 * The statement cache contains stmtCacheNBuckets hash buckets, each
18 * having stmtCacheEntPerBucket entries, which we recycle as needed,
19 * giving up the least-executed entry in the bucket.
20 * stmtCacheEntries[0] is never used, so that zero can be a "not found"
23 #define stmtCacheNBuckets 2039 /* should be a prime number */
24 #define stmtCacheEntPerBucket 8
26 #define stmtCacheArraySize (stmtCacheNBuckets * stmtCacheEntPerBucket + 1)
33 long execs;
/* # of executions */
34 const char *
connection;
/* connection for the statement */
49 if (
c ==
'_' ||
c ==
'>' ||
c ==
'-' ||
c ==
'.')
65 int lineno =
stmt->lineno;
67 /* check if we already have prepared this statement */
72 /* allocate new statement */
83 memset(prep_stmt, 0,
sizeof(
struct statement));
85 /* create statement */
104 this->stmt = prep_stmt;
105 this->prepared =
true;
123 for (; (*text)[ptr] !=
'0円'; ptr++)
125 if ((*
text)[ptr] ==
'\'')
126 string =
string ?
false :
true;
128 if (
string || (((*
text)[ptr] !=
':') && ((*
text)[ptr] !=
'?')))
131 if (((*
text)[ptr] ==
':') && ((*
text)[ptr + 1] ==
':'))
132 ptr += 2;
/* skip '::' */
135 /* a rough guess of the size we need: */
136 int buffersize =
sizeof(int) * CHAR_BIT * 10 / 3;
144 snprintf(buffer, buffersize,
"$%d", counter++);
154 memcpy(newcopy, *
text, ptr);
155 strcpy(newcopy + ptr, buffer);
156 strcat(newcopy, (*
text) +ptr +
len);
163 if ((*
text)[ptr] ==
'0円')
/* we reached the end */
164 ptr--;
/* since we will (*text)[ptr]++ in the top
178 /* allocate new statement */
190 /* create statement */
192 stmt->connection = con;
200 stmt->inlist =
stmt->outlist = NULL;
202 /* if we have C variables in our statement replace them with '?' */
205 /* add prepared statement to our list */
216 /* and finally really prepare the statement */
227 ecpg_log(
"prepare_common on line %d: name %s; query: \"%s\"\n",
stmt->lineno,
name,
stmt->command);
229 this->prepared =
true;
240/* handle the EXEC SQL PREPARE statement */
241/* questionmarks is not needed but remains in there for the time being to not change the API */
253 if (!
ecpg_init(con, connection_name, lineno))
256 /* check if we already have prepared this statement */
273 prev =
this,
this = this->
next)
275 if (strcmp(this->name,
name) == 0)
291 ecpg_log(
"deallocate_one on line %d: name %s\n", lineno, this->
name);
293 /* first deallocate the statement in the backend */
304 query =
PQexec(this->
stmt->connection->connection, text);
307 this->
stmt->connection->connection,
317 * Just ignore all errors since we do not know the list of cursors we are
318 * allowed to free. We have to trust the software.
326 /* okay, free all the resources */
339/* handle the EXEC SQL DEALLOCATE PREPARE statement */
348 if (!
ecpg_init(con, connection_name, lineno))
355 /* prepared statement is not found */
365 /* deallocate all prepared statements */
388 return this ? this->
stmt->command : NULL;
391/* return the prepared statement */
392/* lineno is not used here, but kept in to not break API */
396 (void) lineno;
/* keep the compiler quiet */
402 * hash a SQL statement - returns entry # of first entry in the bucket
414 stmtLeng = strlen(ecpgQuery);
415 hashLeng = 50;
/* use 1st 50 characters of statement */
416 if (hashLeng > stmtLeng)
/* if the statement isn't that long */
417 hashLeng = stmtLeng;
/* use its actual length */
420 for (stmtIx = 0; stmtIx < hashLeng; ++stmtIx)
422 hashVal = hashVal + (
unsigned char) ecpgQuery[stmtIx];
423 /* rotate 32-bit hash value left 13 bits */
424 hashVal = hashVal << 13;
425 rotVal = (hashVal &
UINT64CONST(0x1fff00000000)) >> 32;
426 hashVal = (hashVal &
UINT64CONST(0xffffffff)) | rotVal;
431 /* Add 1 so that array entry 0 is never used */
436 * search the statement cache - search for entry with matching ECPG-format query
437 * Returns entry # in cache if found
438 * OR zero if not present (zero'th entry isn't used)
446 /* quick failure if cache not set up */
450 /* hash the statement */
453 /* search the cache */
459 break;
/* found it */
461 ++entNo;
/* incr entry # */
464 /* if entry wasn't found - set entry # to zero */
472 * free an entry in the statement cache
473 * Returns entry # in cache used
474 * OR negative error code
478 int entNo)
/* entry # to free */
485 /* fail if cache isn't set up */
490 if (!entry->
stmtID[0])
/* return if the entry isn't in use */
495 /* free the 'prepared_statement' list entry */
502 /* free the memory used by the cache entry */
513 * add an entry to the statement cache
514 * returns entry # in cache used OR negative error code
518 const char *stmtID,
/* statement ID */
520 int compat,
/* compatibility level */
521 const char *ecpgQuery)
/* query */
529 /* allocate and zero cache array if we haven't already */
538 /* hash the statement */
541 /* search for an unused entry */
542 entNo = initEntNo;
/* start with the initial entry # for the
544 luEntNo = initEntNo;
/* use it as the initial 'least used' entry */
548 if (!entry->
stmtID[0])
/* unused entry - use it */
551 luEntNo = entNo;
/* save new 'least used' entry */
552 ++entNo;
/* increment entry # */
556 * if no unused entries were found, re-use the 'least used' entry found in
562 /* 'entNo' is the entry to use - make sure its free */
566 /* add the query to the entry */
579/* handle cache and preparation of statements in auto-prepare mode */
585 /* search the statement cache for this statement */
588 /* if not found - add the statement to the cache */
595 ecpg_log(
"ecpg_auto_prepare on line %d: statement found in cache; entry %d\n", lineno, entNo);
604 /* This prepared name doesn't exist on this connection. */
616 ecpg_log(
"ecpg_auto_prepare on line %d: statement not in cache; inserting\n", lineno);
618 /* generate a statement ID */
624 if (!
ECPGprepare(lineno, connection_name, 0, stmtID, query))
638 /* increase usage counter */
struct connection * ecpg_get_connection(const char *connection_name)
#define ECPG_INVALID_STMT
bool ecpg_check_PQresult(PGresult *results, int lineno, PGconn *connection, enum COMPAT_MODE compat)
char * ecpg_strdup(const char *string, int lineno, bool *alloc_failed)
char * ecpg_alloc(long size, int lineno)
void ecpg_log(const char *format,...) pg_attribute_printf(1
bool ecpg_init(const struct connection *con, const char *connection_name, const int lineno)
void ecpg_raise(int line, int code, const char *sqlstate, const char *str)
void ecpg_free(void *ptr)
#define ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME
PGresult * PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)
PGresult * PQexec(PGconn *conn, const char *query)
static bool prepare_common(int lineno, struct connection *con, const char *name, const char *variable)
static bool deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con, struct prepared_statement *prev, struct prepared_statement *this)
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)
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_register_prepared_stmt(struct statement *stmt)
static int SearchStmtCache(const char *ecpgQuery)
static bool isvarchar(unsigned char c)
char * ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
static int HashStmt(const char *ecpgQuery)
bool ECPGprepare(int lineno, const char *connection_name, const bool questionmarks, const char *name, const char *variable)
char * ecpg_prepared(const char *name, struct connection *con)
#define stmtCacheEntPerBucket
struct prepared_statement * ecpg_find_prepared_statement(const char *name, struct connection *con, struct prepared_statement **prev_)
static stmtCacheEntry * stmtCacheEntries
bool ecpg_auto_prepare(int lineno, const char *connection_name, const int compat, char **name, const char *query)
static bool replace_variables(char **text, int lineno)
bool ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name)
#define stmtCacheNBuckets
#define stmtCacheArraySize
struct prepared_statement * prep_stmts
struct prepared_statement * next
struct variable * outlist
struct connection * connection