summaryrefslogtreecommitdiff
path: root/src/backend/commands/prepare.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2003-05-05 00:44:56 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2003-05-05 00:44:56 +0000
commit16503e6fa4a13051debe09698b6db9ce0d509af8 (patch)
treeb8165b6e9481ec187aee0b54f0cb722915d1090a /src/backend/commands/prepare.c
parenta59793f82c8bb7d9931dab8675d91e06c1a41f5a (diff)
Extended query protocol: parse, bind, execute, describe FE/BE messages.
Only lightly tested as yet, since libpq doesn't know anything about 'em.
Diffstat (limited to 'src/backend/commands/prepare.c')
-rw-r--r--src/backend/commands/prepare.c204
1 files changed, 125 insertions, 79 deletions
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 5a3e3f589d1..3f8beac53c1 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -3,10 +3,14 @@
* prepare.c
* Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE
*
- * Copyright (c) 2002, PostgreSQL Global Development Group
+ * This module also implements storage of prepared statements that are
+ * accessed via the extended FE/BE query protocol.
+ *
+ *
+ * Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.14 2003/05/02 20:54:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.15 2003/05/05 00:44:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,31 +29,15 @@
#include "utils/memutils.h"
-#define HASH_KEY_LEN NAMEDATALEN
-
-/* All the data we need to remember about a stored query */
-typedef struct
-{
- /* dynahash.c requires key to be first field */
- char key[HASH_KEY_LEN];
- List *query_list; /* list of queries */
- List *plan_list; /* list of plans */
- List *argtype_list; /* list of parameter type OIDs */
- MemoryContext context; /* context containing this query */
-} QueryHashEntry;
-
/*
* The hash table in which prepared queries are stored. This is
* per-backend: query plans are not shared between backends.
- * The keys for this hash table are the arguments to PREPARE
- * and EXECUTE ("plan names"); the entries are QueryHashEntry structs.
+ * The keys for this hash table are the arguments to PREPARE and EXECUTE
+ * (statement names); the entries are PreparedStatement structs.
*/
static HTAB *prepared_queries = NULL;
static void InitQueryHashTable(void);
-static void StoreQuery(const char *stmt_name, List *query_list,
- List *plan_list, List *argtype_list);
-static QueryHashEntry *FetchQuery(const char *plan_name);
static ParamListInfo EvaluateParams(EState *estate,
List *params, List *argtypes);
@@ -59,14 +47,36 @@ static ParamListInfo EvaluateParams(EState *estate,
void
PrepareQuery(PrepareStmt *stmt)
{
+ const char *commandTag;
List *query_list,
*plan_list;
- if (!stmt->name)
- elog(ERROR, "No statement name given");
+ /*
+ * Disallow empty-string statement name (conflicts with protocol-level
+ * unnamed statement).
+ */
+ if (!stmt->name || stmt->name[0] == '\0')
+ elog(ERROR, "Invalid statement name: must not be empty");
- if (stmt->query->commandType == CMD_UTILITY)
- elog(ERROR, "Utility statements cannot be prepared");
+ switch (stmt->query->commandType)
+ {
+ case CMD_SELECT:
+ commandTag = "SELECT";
+ break;
+ case CMD_INSERT:
+ commandTag = "INSERT";
+ break;
+ case CMD_UPDATE:
+ commandTag = "UPDATE";
+ break;
+ case CMD_DELETE:
+ commandTag = "DELETE";
+ break;
+ default:
+ elog(ERROR, "Utility statements cannot be prepared");
+ commandTag = NULL; /* keep compiler quiet */
+ break;
+ }
/*
* Parse analysis is already done, but we must still rewrite and plan
@@ -80,7 +90,12 @@ PrepareQuery(PrepareStmt *stmt)
plan_list = pg_plan_queries(query_list, false);
/* Save the results. */
- StoreQuery(stmt->name, query_list, plan_list, stmt->argtype_oids);
+ StorePreparedStatement(stmt->name,
+ NULL, /* text form not available */
+ commandTag,
+ query_list,
+ plan_list,
+ stmt->argtype_oids);
}
/*
@@ -89,7 +104,8 @@ PrepareQuery(PrepareStmt *stmt)
void
ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
{
- QueryHashEntry *entry;
+ PreparedStatement *entry;
+ char *query_string;
List *query_list,
*plan_list;
MemoryContext qcontext;
@@ -98,8 +114,9 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
Portal portal;
/* Look it up in the hash table */
- entry = FetchQuery(stmt->name);
+ entry = FetchPreparedStatement(stmt->name, true);
+ query_string = entry->query_string;
query_list = entry->query_list;
plan_list = entry->plan_list;
qcontext = entry->context;
@@ -135,6 +152,8 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+ if (query_string)
+ query_string = pstrdup(query_string);
query_list = copyObject(query_list);
plan_list = copyObject(plan_list);
qcontext = PortalGetHeapMemory(portal);
@@ -150,8 +169,8 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
}
PortalDefineQuery(portal,
- NULL, /* XXX fixme: can we save query text? */
- NULL, /* no command tag known either */
+ query_string,
+ entry->commandTag,
query_list,
plan_list,
qcontext);
@@ -228,8 +247,8 @@ InitQueryHashTable(void)
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
- hash_ctl.keysize = HASH_KEY_LEN;
- hash_ctl.entrysize = sizeof(QueryHashEntry);
+ hash_ctl.keysize = NAMEDATALEN;
+ hash_ctl.entrysize = sizeof(PreparedStatement);
prepared_queries = hash_create("Prepared Queries",
32,
@@ -244,15 +263,24 @@ InitQueryHashTable(void)
* Store all the data pertaining to a query in the hash table using
* the specified key. A copy of the data is made in a memory context belonging
* to the hash entry, so the caller can dispose of their copy.
+ *
+ * Exception: commandTag is presumed to be a pointer to a constant string,
+ * or possibly NULL, so it need not be copied. Note that commandTag should
+ * be NULL only if the original query (before rewriting) was empty.
*/
-static void
-StoreQuery(const char *stmt_name, List *query_list,
- List *plan_list, List *argtype_list)
+void
+StorePreparedStatement(const char *stmt_name,
+ const char *query_string,
+ const char *commandTag,
+ List *query_list,
+ List *plan_list,
+ List *argtype_list)
{
- QueryHashEntry *entry;
+ PreparedStatement *entry;
MemoryContext oldcxt,
entrycxt;
- char key[HASH_KEY_LEN];
+ char *qstring;
+ char key[NAMEDATALEN];
bool found;
/* Initialize the hash table, if necessary */
@@ -260,7 +288,7 @@ StoreQuery(const char *stmt_name, List *query_list,
InitQueryHashTable();
/* Check for pre-existing entry of same name */
- /* See notes in FetchQuery */
+ /* See notes in FetchPreparedStatement */
MemSet(key, 0, sizeof(key));
strncpy(key, stmt_name, sizeof(key));
@@ -285,15 +313,16 @@ StoreQuery(const char *stmt_name, List *query_list,
* out-of-memory failure only wastes memory and doesn't leave us with
* an incomplete (ie corrupt) hashtable entry.
*/
+ qstring = query_string ? pstrdup(query_string) : (char *) NULL;
query_list = (List *) copyObject(query_list);
plan_list = (List *) copyObject(plan_list);
argtype_list = listCopy(argtype_list);
/* Now we can add entry to hash table */
- entry = (QueryHashEntry *) hash_search(prepared_queries,
- key,
- HASH_ENTER,
- &found);
+ entry = (PreparedStatement *) hash_search(prepared_queries,
+ key,
+ HASH_ENTER,
+ &found);
/* Shouldn't get a failure, nor a duplicate entry */
if (!entry || found)
@@ -301,6 +330,8 @@ StoreQuery(const char *stmt_name, List *query_list,
stmt_name);
/* Fill in the hash table entry with copied data */
+ entry->query_string = qstring;
+ entry->commandTag = commandTag;
entry->query_list = query_list;
entry->plan_list = plan_list;
entry->argtype_list = argtype_list;
@@ -311,52 +342,53 @@ StoreQuery(const char *stmt_name, List *query_list,
/*
* Lookup an existing query in the hash table. If the query does not
- * actually exist, an elog(ERROR) is thrown.
+ * actually exist, throw elog(ERROR) or return NULL per second parameter.
*/
-static QueryHashEntry *
-FetchQuery(const char *plan_name)
+PreparedStatement *
+FetchPreparedStatement(const char *stmt_name, bool throwError)
{
- char key[HASH_KEY_LEN];
- QueryHashEntry *entry;
+ char key[NAMEDATALEN];
+ PreparedStatement *entry;
/*
* If the hash table hasn't been initialized, it can't be storing
* anything, therefore it couldn't possibly store our plan.
*/
- if (!prepared_queries)
- elog(ERROR, "Prepared statement with name \"%s\" does not exist",
- plan_name);
-
- /*
- * We can't just use the statement name as supplied by the user: the
- * hash package is picky enough that it needs to be NULL-padded out to
- * the appropriate length to work correctly.
- */
- MemSet(key, 0, sizeof(key));
- strncpy(key, plan_name, sizeof(key));
+ if (prepared_queries)
+ {
+ /*
+ * We can't just use the statement name as supplied by the user: the
+ * hash package is picky enough that it needs to be NULL-padded out to
+ * the appropriate length to work correctly.
+ */
+ MemSet(key, 0, sizeof(key));
+ strncpy(key, stmt_name, sizeof(key));
- entry = (QueryHashEntry *) hash_search(prepared_queries,
- key,
- HASH_FIND,
- NULL);
+ entry = (PreparedStatement *) hash_search(prepared_queries,
+ key,
+ HASH_FIND,
+ NULL);
+ }
+ else
+ entry = NULL;
- if (!entry)
+ if (!entry && throwError)
elog(ERROR, "Prepared statement with name \"%s\" does not exist",
- plan_name);
+ stmt_name);
return entry;
}
/*
- * Given a plan name, look up the stored plan (giving error if not found).
+ * Look up a prepared statement given the name (giving error if not found).
* If found, return the list of argument type OIDs.
*/
List *
-FetchQueryParams(const char *plan_name)
+FetchPreparedStatementParams(const char *stmt_name)
{
- QueryHashEntry *entry;
+ PreparedStatement *entry;
- entry = FetchQuery(plan_name);
+ entry = FetchPreparedStatement(stmt_name, true);
return entry->argtype_list;
}
@@ -368,20 +400,34 @@ FetchQueryParams(const char *plan_name)
void
DeallocateQuery(DeallocateStmt *stmt)
{
- QueryHashEntry *entry;
+ DropPreparedStatement(stmt->name, true);
+}
+
+/*
+ * Internal version of DEALLOCATE
+ *
+ * If showError is false, dropping a nonexistent statement is a no-op.
+ */
+void
+DropPreparedStatement(const char *stmt_name, bool showError)
+{
+ PreparedStatement *entry;
- /* Find the query's hash table entry */
- entry = FetchQuery(stmt->name);
+ /* Find the query's hash table entry; raise error if wanted */
+ entry = FetchPreparedStatement(stmt_name, showError);
- /* Drop any open portals that depend on this prepared statement */
- Assert(MemoryContextIsValid(entry->context));
- DropDependentPortals(entry->context);
+ if (entry)
+ {
+ /* Drop any open portals that depend on this prepared statement */
+ Assert(MemoryContextIsValid(entry->context));
+ DropDependentPortals(entry->context);
- /* Flush the context holding the subsidiary data */
- MemoryContextDelete(entry->context);
+ /* Flush the context holding the subsidiary data */
+ MemoryContextDelete(entry->context);
- /* Now we can remove the hash table entry */
- hash_search(prepared_queries, entry->key, HASH_REMOVE, NULL);
+ /* Now we can remove the hash table entry */
+ hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
+ }
}
/*
@@ -391,7 +437,7 @@ void
ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
{
ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
- QueryHashEntry *entry;
+ PreparedStatement *entry;
List *l,
*query_list,
*plan_list;
@@ -402,7 +448,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
Assert(execstmt && IsA(execstmt, ExecuteStmt));
/* Look it up in the hash table */
- entry = FetchQuery(execstmt->name);
+ entry = FetchPreparedStatement(execstmt->name, true);
query_list = entry->query_list;
plan_list = entry->plan_list;