PREPARE/EXECUTE statements. Patch by Neil Conway, some kibitzing
authorTom Lane
Tue, 27 Aug 2002 04:55:12 +0000 (04:55 +0000)
committerTom Lane
Tue, 27 Aug 2002 04:55:12 +0000 (04:55 +0000)
from Tom Lane.

24 files changed:
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/deallocate.sgml [new file with mode: 0644]
doc/src/sgml/ref/execute.sgml [new file with mode: 0644]
doc/src/sgml/ref/prepare.sgml [new file with mode: 0644]
doc/src/sgml/reference.sgml
doc/src/sgml/release.sgml
src/backend/commands/Makefile
src/backend/commands/prepare.c [new file with mode: 0644]
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/parser/parser.c
src/backend/tcop/postgres.c
src/backend/tcop/utility.c
src/include/commands/prepare.h [new file with mode: 0644]
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/gramparse.h
src/test/regress/expected/prepare.out [new file with mode: 0644]
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/prepare.sql [new file with mode: 0644]

index 79221044c2130f6fdad7479b5752e45197d54738..a923e260edb4f6e5ffde6a3e1d6e45f254dc459f 100644 (file)
@@ -1,5 +1,5 @@
 
@@ -71,6 +71,7 @@ Complete list of usable sgml source files in this directory.
 
 
 
+
 
 
 
@@ -93,6 +94,7 @@ Complete list of usable sgml source files in this directory.
 
 
 
+
 
 
 
@@ -102,6 +104,7 @@ Complete list of usable sgml source files in this directory.
 
 
 
+
 
 
 
diff --git a/doc/src/sgml/ref/deallocate.sgml b/doc/src/sgml/ref/deallocate.sgml
new file mode 100644 (file)
index 0000000..29925ad
--- /dev/null
@@ -0,0 +1,137 @@
+
+
+
+  DEALLOCATE
+  SQL - Language Statements
+  
+   DEALLOCATE
+  
+  
+   remove a prepared query
+  
+  
+   2002-08-12
+  
+  
+   DEALLOCATE [ PREPARE ] plan_name
+  
+
+  
+   
+    2002-08-12
+   
+   </div> <div class="diff add">+    Inputs</div> <div class="diff add">+   
+
+   
+    
+    
+     PREPARE
+     
+      
+       This keyword is ignored.
+      
+     
+     
+     
+      plan_name
+      
+       
+       The name of the prepared query to remove.
+       
+      
+     
+    
+   
+  
+  
+   
+    2002-08-12
+   
+   </div> <div class="diff add">+    Outputs</div> <div class="diff add">+   
+   
+
+    
+     
+      
+       DEALLOCATE
+       
+      
+       
+       The prepared query was removed successfully.
+       
+      
+     
+    
+   
+  
+
+  
+   2002-08-12
+  
+  </div> <div class="diff add">+   Description</div> <div class="diff add">+  
+
+  
+   DEALLOCATE is used to remove a previously
+   prepared query. If you do not explicitly
+   DEALLOCATE a prepared query, it is removed when
+   the session ends.
+  
+
+  
+   For more information on prepared queries, see 
+   linkend="sql-prepare" endterm="sql-prepare-title">.
+  
+
+  </div> <div class="diff add">+   Compatibility</div> <div class="diff add">+  
+
+  
+   
+    2002-08-12
+   
+   </div> <div class="diff add">+    SQL92</div> <div class="diff add">+   
+   
+   SQL92 includes a DEALLOCATE statement, but it is
+    only for use in embedded SQL clients.
+   
+  
+
+
+
diff --git a/doc/src/sgml/ref/execute.sgml b/doc/src/sgml/ref/execute.sgml
new file mode 100644 (file)
index 0000000..6703557
--- /dev/null
@@ -0,0 +1,132 @@
+
+
+
+  EXECUTE
+  SQL - Language Statements
+  
+   EXECUTE
+  
+  
+   execute a prepared query
+  
+  
+   2002-08-12
+  
+  
+   EXECUTE plan_name [ (parameter [, ...] ) ]
+  
+
+  
+   
+    2002-08-12
+   
+   </div> <div class="diff add">+    Inputs</div> <div class="diff add">+   
+
+   
+    
+     
+      plan_name
+      
+       
+       The name of the prepared query to execute.
+       
+      
+     
+     
+      parameter
+      
+       
+       The actual value of a parameter to the prepared query.
+       This must be an expression yielding a value of a type
+       compatible with
+       the data-type specified for this parameter position in the
+       PREPARE statement that created the prepared
+       query.
+       
+      
+     
+    
+   
+  
+
+  
+   2002-08-12
+  
+  </div> <div class="diff add">+   Description</div> <div class="diff add">+  
+
+  
+   EXECUTE is used to execute a previously prepared
+   query. Since prepared queries only exist for the duration of a
+   session, the prepared query must have been created by a
+   PREPARE statement executed earlier in the
+   current session.
+  
+
+  
+   If the PREPARE statement that created the query
+   specified some parameters, a compatible set of parameters must be
+   passed to the EXECUTE statement, or else an
+   error is raised. Note that (unlike functions) prepared queries are
+   not overloaded based on the type or number of their parameters: the
+   name of a prepared query must be unique within a database session.
+  
+
+  
+   For more information on the creation and usage of prepared queries,
+   see .
+  
+
+  </div> <div class="diff add">+   Compatibility</div> <div class="diff add">+  
+
+  
+   
+    2002-08-12
+   
+   </div> <div class="diff add">+    SQL92</div> <div class="diff add">+   
+   
+   SQL92 includes an EXECUTE statement, but it is
+    only for use in embedded SQL clients. The
+    EXECUTE statement implemented by
+    PostgreSQL also uses a somewhat
+    different syntax.
+   
+  
+
+
+
diff --git a/doc/src/sgml/ref/prepare.sgml b/doc/src/sgml/ref/prepare.sgml
new file mode 100644 (file)
index 0000000..d9fa864
--- /dev/null
@@ -0,0 +1,209 @@
+
+
+
+  PREPARE
+  SQL - Language Statements
+  
+   PREPARE
+  
+  
+   create a prepared query
+  
+  
+   2002-08-12
+  
+  
+   PREPARE plan_name [ (datatype [, ...] ) ] AS query
+  
+
+  
+   
+    2002-08-12
+   
+   </div> <div class="diff add">+    Inputs</div> <div class="diff add">+   
+
+   
+    
+     
+      plan_name
+      
+       
+       An arbitrary name given to this particular prepared query. It
+       must be unique within a single session, and is used to execute
+       or remove a previously prepared query.
+       
+      
+     
+     
+      datatype
+      
+       
+       The data-type of a parameter to the prepared query.
+       To refer to the parameters in the prepared query itself,
+       use $1$2, etc.
+       
+      
+     
+    
+   
+  
+
+  
+   
+    2002-08-12
+   
+   </div> <div class="diff add">+    Outputs</div> <div class="diff add">+   
+   
+
+    
+     
+      
+       PREPARE
+       
+      
+       
+       The query has been prepared successfully.
+       
+      
+     
+
+    
+   
+  
+
+  
+   2002-08-12
+  
+  </div> <div class="diff add">+   Description</div> <div class="diff add">+  
+  
+   PREPARE creates a prepared query. A prepared
+   query is a server-side object that can be used to optimize
+   performance. When the PREPARE statement is
+   executed, the specified query is parsed, rewritten, and
+   planned. When a subsequent EXECUTE statement is
+   issued, the prepared query need only be executed. Thus, the
+   parsing, rewriting, and planning stages are only performed once,
+   instead of every time the query is executed.
+  
+
+  
+   Prepared queries can take parameters: values that are
+   substituted into the query when it is executed. To specify the
+   parameters to a prepared query, include a list of data-types with
+   the PREPARE statement. In the query itself, you
+   can refer to the parameters by position using
+   $1$2, etc. When executing
+   the query, specify the actual values for these parameters in the
+   EXECUTE statement -- refer to 
+   linkend="sql-execute" endterm="sql-execute-title">
+   for more information.
+  
+
+  
+   Prepared queries are stored locally (in the current backend), and
+   only exist for the duration of the current database session. When
+   the client exits, the prepared query is forgotten, and so it must be
+   re-created before being used again. This also means that a single
+   prepared query cannot be used by multiple simultaneous database
+   clients; however, each client can create their own prepared query
+   to use.
+  
+
+  
+   Prepared queries have the largest performance advantage when a
+   single backend is being used to execute a large number of similar
+   queries. The performance difference will be particularly
+   significant if the queries are complex to plan or rewrite. For
+   example, if the query involves a join of many tables or requires
+   the application of several rules. If the query is relatively simple
+   to plan and rewrite but relatively expensive to execute, the
+   performance advantage of prepared queries will be less noticeable.
+  
+
+  
+   
+    2002-08-12
+   
+   </div> <div class="diff add">+    Notes</div> <div class="diff add">+   
+
+   
+   In some situations, the query plan produced by
+   PostgreSQL for a prepared query may be
+   inferior to the plan produced if the query were submitted and
+   executed normally. This is because when the query is planned (and
+   the optimizer attempts to determine the optimal query plan), the
+   actual values of any parameters specified in the query are
+   unavailable. PostgreSQL collects
+   statistics on the distribution of data in the table, and can use
+   constant values in a query to make guesses about the likely
+   result of executing the query. Since this data is unavailable when
+   planning prepared queries with parameters, the chosen plan may be
+   sub-optimal.
+   
+
+   
+   For more information on query planning and the statistics
+   collected by PostgreSQL for query
+   optimization purposes, see the 
+   endterm="sql-analyze-title"> documentation.
+   
+  
+
+  </div> <div class="diff add">+   Compatibility</div> <div class="diff add">+  
+
+  
+   
+    2002-08-12
+   
+   </div> <div class="diff add">+    SQL92</div> <div class="diff add">+   
+   
+   SQL92 includes a PREPARE statement, but it is
+    only for use in embedded SQL clients. The
+    PREPARE statement implemented by
+    PostgreSQL also uses a somewhat
+    different syntax.
+   
+  
+
+
+
index effe495f1da38c004d76a07c1931926f766793da..22a3c07a3e69eefde9264d92ed50935d50b17eed 100644 (file)
@@ -1,5 +1,5 @@
 
@@ -80,6 +80,7 @@ PostgreSQL Reference Manual
    &createType;
    &createUser;
    &createView;
+   &deallocate;
    &declare;
    &delete;
    &dropAggregate;
@@ -98,10 +99,11 @@ PostgreSQL Reference Manual
    &dropSequence;
    &dropTable;
    &dropTrigger;
-   &dropType
+   &dropType;
    &dropUser;
    &dropView;
    &end;
+   &execute;
    &explain;
    &fetch;
    &grant;
@@ -111,6 +113,7 @@ PostgreSQL Reference Manual
    &lock;
    &move;
    ¬ify;
+   &prepare;
    &reindex;
    &reset;
    &revoke;
index 4be5868512091d62d9e0483937882c82ee68b99f..e457504ebefcf4a5352d3dac79ac6525cf7fa45a 100644 (file)
@@ -1,5 +1,5 @@
 
 
 
@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
 worries about funny characters.
 -->
 
+PREPARE statement allows caching query plans for interactive statements
 Type OPAQUE is now deprecated in favor of pseudo-types cstring, trigger, etc
 Files larger than 2 GB are now supported (if supported by the operating system)
 SERIAL no longer implies UNIQUE; specify explicitly if index is wanted
index 92961049d775b57c8da00f9d9ecebfb51e004834..db91bf1d0e074b78a22dd91487d46771903bd367 100644 (file)
@@ -4,7 +4,7 @@
 #    Makefile for backend/commands
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.30 2002/07/29 22:14:10 tgl Exp $
+#    $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.31 2002/08/27 04:55:07 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -16,7 +16,7 @@ OBJS = aggregatecmds.o analyze.o async.o cluster.o comment.o  \
    conversioncmds.o copy.o \
    dbcommands.o define.o explain.o functioncmds.o \
    indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
-   portalcmds.o proclang.o \
+   portalcmds.o prepare.o proclang.o \
    schemacmds.o sequence.o tablecmds.o trigger.o typecmds.o user.o \
    vacuum.o vacuumlazy.o variable.o view.o
 
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
new file mode 100644 (file)
index 0000000..9dbe214
--- /dev/null
@@ -0,0 +1,407 @@
+/*-------------------------------------------------------------------------
+ *
+ * prepare.c
+ *   Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE
+ *
+ * Copyright (c) 2002, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *   $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.1 2002/08/27 04:55:07 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "commands/prepare.h"
+#include "executor/executor.h"
+#include "utils/guc.h"
+#include "optimizer/planner.h"
+#include "rewrite/rewriteHandler.h"
+#include "tcop/pquery.h"
+#include "tcop/tcopprot.h"
+#include "tcop/utility.h"
+#include "utils/hsearch.h"
+#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.
+ */
+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 void RunQuery(QueryDesc *qdesc, EState *state);
+
+
+/*
+ * Implements the 'PREPARE' utility statement.
+ */
+void
+PrepareQuery(PrepareStmt *stmt)
+{
+   List *plan_list = NIL;
+   List *query_list,
+        *query_list_item;
+
+   if (!stmt->name)
+       elog(ERROR, "No statement name given");
+
+   if (stmt->query->commandType == CMD_UTILITY)
+       elog(ERROR, "Utility statements cannot be prepared");
+
+   /* Rewrite the query. The result could be 0, 1, or many queries. */
+   query_list = QueryRewrite(stmt->query);
+
+   foreach(query_list_item, query_list)
+   {
+       Query *query = (Query *) lfirst(query_list_item);
+       Plan  *plan;
+
+       /* We can't generate plans for utility statements. */
+       if (query->commandType == CMD_UTILITY)
+           plan = NULL;
+       else
+       {
+           /* Call the query planner to generate a plan. */
+           plan = planner(query);
+       }
+
+       plan_list = lappend(plan_list, plan);
+   }
+
+   StoreQuery(stmt->name, query_list, plan_list, stmt->argtype_oids);
+}
+
+/*
+ * Implements the 'EXECUTE' utility statement.
+ */
+void
+ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
+{
+   QueryHashEntry  *entry;
+   List        *l,
+               *query_list,
+               *plan_list;
+   ParamListInfo paramLI = NULL;
+
+   /* Look it up in the hash table */
+   entry = FetchQuery(stmt->name);
+
+   /* Make working copies the executor can safely scribble on */
+   query_list = (List *) copyObject(entry->query_list);
+   plan_list = (List *) copyObject(entry->plan_list);
+
+   Assert(length(query_list) == length(plan_list));
+
+   /* Evaluate parameters, if any */
+   if (entry->argtype_list != NIL)
+   {
+       int nargs = length(entry->argtype_list);
+       int i = 0;
+       ExprContext *econtext = MakeExprContext(NULL, CurrentMemoryContext);
+
+       /* Parser should have caught this error, but check */
+       if (nargs != length(stmt->params))
+           elog(ERROR, "ExecuteQuery: wrong number of arguments");
+
+       paramLI = (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
+       MemSet(paramLI, 0, (nargs + 1) * sizeof(ParamListInfoData));
+
+       foreach (l, stmt->params)
+       {
+           Node *n = lfirst(l);
+           bool isNull;
+
+           paramLI[i].value = ExecEvalExprSwitchContext(n,
+                                                        econtext,
+                                                        &isNull,
+                                                        NULL);
+           paramLI[i].kind = PARAM_NUM;
+           paramLI[i].id = i + 1;
+           paramLI[i].isnull = isNull;
+
+           i++;
+       }
+       paramLI[i].kind = PARAM_INVALID;
+   }
+
+   /* Execute each query */
+   foreach(l, query_list)
+   {
+       Query *query = lfirst(l);
+       Plan *plan = lfirst(plan_list);
+       bool is_last_query;
+
+       plan_list = lnext(plan_list);
+       is_last_query = (plan_list == NIL);
+
+       if (query->commandType == CMD_UTILITY)
+           ProcessUtility(query->utilityStmt, outputDest, NULL);
+       else
+       {
+           QueryDesc *qdesc;
+           EState    *state;
+
+           if (Show_executor_stats)
+               ResetUsage();
+
+           qdesc = CreateQueryDesc(query, plan, outputDest, NULL);
+           state = CreateExecutorState();
+
+           state->es_param_list_info = paramLI;
+
+           if (stmt->into)
+           {
+               if (qdesc->operation != CMD_SELECT)
+                   elog(ERROR, "INTO clause specified for non-SELECT query");
+
+               query->into = stmt->into;
+               qdesc->dest = None;
+           }
+
+           RunQuery(qdesc, state);
+
+           if (Show_executor_stats)
+               ShowUsage("EXECUTOR STATISTICS");
+       }
+
+       /*
+        * If we're processing multiple queries, we need to increment
+        * the command counter between them. For the last query,
+        * there's no need to do this, it's done automatically.
+        */
+       if (! is_last_query)
+           CommandCounterIncrement();
+   }
+
+   /* No need to pfree memory, MemoryContext will be reset */
+}
+
+/*
+ * Initialize query hash table upon first use.
+ */
+static void
+InitQueryHashTable(void)
+{
+   HASHCTL hash_ctl;
+
+   MemSet(&hash_ctl, 0, sizeof(hash_ctl));
+
+   hash_ctl.keysize = HASH_KEY_LEN;
+   hash_ctl.entrysize = sizeof(QueryHashEntry);
+
+   prepared_queries = hash_create("Prepared Queries",
+                                  32,
+                                  &hash_ctl,
+                                  HASH_ELEM);
+
+   if (!prepared_queries)
+       elog(ERROR, "InitQueryHashTable: unable to create hash table");
+}
+
+/*
+ * 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.
+ */
+static void
+StoreQuery(const char *stmt_name, List *query_list, List *plan_list,
+          List *argtype_list)
+{
+   QueryHashEntry *entry;
+   MemoryContext oldcxt,
+                 entrycxt;
+   char key[HASH_KEY_LEN];
+   bool found;
+
+   /* Initialize the hash table, if necessary */
+   if (!prepared_queries)
+       InitQueryHashTable();
+
+   /* Check for pre-existing entry of same name */
+   /* See notes in FetchQuery */
+   MemSet(key, 0, sizeof(key));
+   strncpy(key, stmt_name, sizeof(key));
+
+   hash_search(prepared_queries, key, HASH_FIND, &found);
+
+   if (found)
+       elog(ERROR, "Prepared statement with name \"%s\" already exists",
+            stmt_name);
+
+   /* Okay. Make a permanent memory context for the hashtable entry */
+   entrycxt = AllocSetContextCreate(TopMemoryContext,
+                                    stmt_name,
+                                    1024,
+                                    1024,
+                                    ALLOCSET_DEFAULT_MAXSIZE);
+
+   oldcxt = MemoryContextSwitchTo(entrycxt);
+
+   /*
+    * We need to copy the data so that it is stored in the correct
+    * memory context.  Do this before making hashtable entry, so that
+    * an out-of-memory failure only wastes memory and doesn't leave us
+    * with an incomplete (ie corrupt) hashtable entry.
+    */
+   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);
+
+   /* Shouldn't get a failure, nor duplicate entry */
+   if (!entry || found)
+       elog(ERROR, "Unable to store prepared statement \"%s\"!",
+            stmt_name);
+
+   /* Fill in the hash table entry with copied data */
+   entry->query_list = query_list;
+   entry->plan_list = plan_list;
+   entry->argtype_list = argtype_list;
+   entry->context = entrycxt;
+
+   MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * Lookup an existing query in the hash table.
+ */
+static QueryHashEntry *
+FetchQuery(const char *plan_name)
+{
+   char key[HASH_KEY_LEN];
+   QueryHashEntry *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));
+
+   entry = (QueryHashEntry *) hash_search(prepared_queries,
+                                          key,
+                                          HASH_FIND,
+                                          NULL);
+
+   if (!entry)
+       elog(ERROR, "Prepared statement with name \"%s\" does not exist",
+            plan_name);
+
+   return entry;
+}
+
+/*
+ * Given a plan name, look up the stored plan (giving error if not found).
+ * If found, return the list of argument type OIDs.
+ */
+List *
+FetchQueryParams(const char *plan_name)
+{
+   QueryHashEntry *entry;
+
+   entry = FetchQuery(plan_name);
+
+   return entry->argtype_list;
+}
+
+/*
+ * Actually execute a prepared query.
+ */
+static void
+RunQuery(QueryDesc *qdesc, EState *state)
+{
+   TupleDesc tupdesc;
+
+   tupdesc = ExecutorStart(qdesc, state);
+
+   ExecutorRun(qdesc, state, state->es_direction, 0L);
+
+   ExecutorEnd(qdesc, state);
+}
+
+/*
+ * Implements the 'DEALLOCATE' utility statement: deletes the
+ * specified plan from storage.
+ *
+ * The initial part of this routine is identical to FetchQuery(),
+ * but we repeat the coding because we need to use the key twice.
+ */
+void
+DeallocateQuery(DeallocateStmt *stmt)
+{
+   char key[HASH_KEY_LEN];
+   QueryHashEntry *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",
+            stmt->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, stmt->name, sizeof(key));
+
+   /*
+    * First lookup the entry, so we can release all the subsidiary memory
+    * it has allocated (when it's removed, hash_search() will return
+    * a dangling pointer, so it needs to be done prior to HASH_REMOVE).
+    * This requires an extra hash-table lookup, but DEALLOCATE
+    * isn't exactly a performance bottleneck.
+    */
+   entry = (QueryHashEntry *) hash_search(prepared_queries,
+                                          key,
+                                          HASH_FIND,
+                                          NULL);
+
+   if (!entry)
+       elog(ERROR, "Prepared statement with name \"%s\" does not exist",
+            stmt->name);
+
+   /* Flush the context holding the subsidiary data */
+   if (MemoryContextIsValid(entry->context))
+       MemoryContextDelete(entry->context);
+
+   /* Now we can remove the hash table entry */
+   hash_search(prepared_queries, key, HASH_REMOVE, NULL);
+}
index 4de9ba5b8dec55d6b7e9f9cbf060c1786afebb38..b3920e38b2dfbb44e73731441de537d25009ddf6 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.206 2002/08/26 17:53:57 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.207 2002/08/27 04:55:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2647,6 +2647,41 @@ _copyDropCastStmt(DropCastStmt *from)
    return newnode;
 }
 
+static PrepareStmt *
+_copyPrepareStmt(PrepareStmt *from)
+{
+   PrepareStmt *newnode = makeNode(PrepareStmt);
+
+   newnode->name = pstrdup(from->name);
+   Node_Copy(from, newnode, argtypes);
+   newnode->argtype_oids = listCopy(from->argtype_oids);
+   Node_Copy(from, newnode, query);
+
+   return newnode;
+}
+
+static ExecuteStmt *
+_copyExecuteStmt(ExecuteStmt *from)
+{
+   ExecuteStmt *newnode = makeNode(ExecuteStmt);
+
+   newnode->name = pstrdup(from->name);
+   Node_Copy(from, newnode, into);
+   Node_Copy(from, newnode, params);
+
+   return newnode;
+}
+
+static DeallocateStmt *
+_copyDeallocateStmt(DeallocateStmt *from)
+{
+   DeallocateStmt *newnode = makeNode(DeallocateStmt);
+
+   newnode->name = pstrdup(from->name);
+
+   return newnode;
+}
+
 
 /* ****************************************************************
  *                 pg_list.h copy functions
@@ -3079,6 +3114,15 @@ copyObject(void *from)
        case T_DropCastStmt:
            retval = _copyDropCastStmt(from);
            break;
+       case T_PrepareStmt:
+           retval = _copyPrepareStmt(from);
+           break;
+       case T_ExecuteStmt:
+           retval = _copyExecuteStmt(from);
+           break;
+       case T_DeallocateStmt:
+           retval = _copyDeallocateStmt(from);
+           break;
 
        case T_A_Expr:
            retval = _copyAExpr(from);
index c96389f1e8b28e67324350d9d63eb8d53b7352b6..408a94ff1b3922f272c3e4e15ade3d2eb4a54fcb 100644 (file)
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.154 2002/08/26 17:53:57 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.155 2002/08/27 04:55:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1490,6 +1490,43 @@ _equalDropCastStmt(DropCastStmt *a, DropCastStmt *b)
    return true;
 }
 
+static bool
+_equalPrepareStmt(PrepareStmt *a, PrepareStmt *b)
+{
+   if (!equalstr(a->name, b->name))
+       return false;
+   if (!equal(a->argtypes, b->argtypes))
+       return false;
+   if (!equali(a->argtype_oids, b->argtype_oids))
+       return false;
+   if (!equal(a->query, b->query))
+       return false;
+
+   return true;
+}
+
+static bool
+_equalExecuteStmt(ExecuteStmt *a, ExecuteStmt *b)
+{
+   if (!equalstr(a->name, b->name))
+       return false;
+   if (!equal(a->into, b->into))
+       return false;
+   if (!equal(a->params, b->params))
+       return false;
+
+   return true;
+}
+
+static bool
+_equalDeallocateStmt(DeallocateStmt *a, DeallocateStmt *b)
+{
+   if (!equalstr(a->name, b->name))
+       return false;
+
+   return true;
+}
+
 static bool
 _equalAExpr(A_Expr *a, A_Expr *b)
 {
@@ -2249,6 +2286,15 @@ equal(void *a, void *b)
        case T_DropCastStmt:
            retval = _equalDropCastStmt(a, b);
            break;
+       case T_PrepareStmt:
+           retval = _equalPrepareStmt(a, b);
+           break;
+       case T_ExecuteStmt:
+           retval = _equalExecuteStmt(a, b);
+           break;
+       case T_DeallocateStmt:
+           retval = _equalDeallocateStmt(a, b);
+           break;
 
        case T_A_Expr:
            retval = _equalAExpr(a, b);
index 01e6d867ce477135880443d1e9efa4ffaa025628..ffa371d92605d38ffacbda3b215f2075fa6be1c5 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.243 2002/08/27 03:56:34 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.244 2002/08/27 04:55:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "catalog/namespace.h"
 #include "catalog/pg_index.h"
 #include "catalog/pg_type.h"
+#include "commands/prepare.h"
 #include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
 #include "parser/analyze.h"
 #include "parser/gramparse.h"
 #include "parser/parsetree.h"
@@ -94,6 +97,8 @@ static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
 static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
 static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
 static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
+static Query *transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt);
+static Query *transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt);
 static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
                        List **extras_before, List **extras_after);
 static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
@@ -277,6 +282,14 @@ transformStmt(ParseState *pstate, Node *parseTree,
                                        extras_before, extras_after);
            break;
 
+       case T_PrepareStmt:
+           result = transformPrepareStmt(pstate, (PrepareStmt *) parseTree);
+           break;
+
+       case T_ExecuteStmt:
+           result = transformExecuteStmt(pstate, (ExecuteStmt *) parseTree);
+           break;
+
            /*
             * Optimizable statements
             */
@@ -2454,6 +2467,131 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
    return qry;
 }
 
+static Query *
+transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt)
+{
+   Query   *result = makeNode(Query);
+   List    *extras_before  = NIL,
+           *extras_after   = NIL;
+   List    *argtype_oids = NIL; /* argtype OIDs in a list */
+   Oid     *argtoids = NULL;   /* as an array for parser_param_set */
+   int     nargs;
+
+   result->commandType = CMD_UTILITY;
+   result->utilityStmt = (Node *) stmt;
+
+   /* Transform list of TypeNames to list (and array) of type OIDs */
+   nargs = length(stmt->argtypes);
+
+   if (nargs)
+   {
+       List *l;
+       int i = 0;
+
+       argtoids = (Oid *) palloc(nargs * sizeof(Oid));
+
+       foreach (l, stmt->argtypes)
+       {
+           TypeName *tn = lfirst(l);
+           Oid toid = typenameTypeId(tn);
+
+           argtype_oids = lappendi(argtype_oids, toid);
+           argtoids[i++] = toid;
+       }
+   }
+
+   stmt->argtype_oids = argtype_oids;
+
+   /*
+    * We need to adjust the parameters expected by the
+    * rest of the system, so that $1, ... $n are parsed properly.
+    *
+    * This is somewhat of a hack; however, the main parser interface
+    * only allows parameters to be specified when working with a
+    * raw query string, which is not helpful here.
+    */
+   parser_param_set(argtoids, nargs);
+
+   stmt->query = transformStmt(pstate, (Node *) stmt->query,
+                               &extras_before, &extras_after);
+
+   /* Shouldn't get any extras, since grammar only allows OptimizableStmt */
+   if (extras_before || extras_after)
+       elog(ERROR, "transformPrepareStmt: internal error");
+
+   /* Remove links to our local parameters */
+   parser_param_set(NULL, 0);
+
+   return result;
+}
+
+static Query *
+transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
+{
+   Query *result = makeNode(Query);
+   List   *paramtypes;
+
+   result->commandType = CMD_UTILITY;
+   result->utilityStmt = (Node *) stmt;
+
+   paramtypes = FetchQueryParams(stmt->name);
+
+   if (stmt->params || paramtypes)
+   {
+       int nparams = length(stmt->params);
+       int nexpected = length(paramtypes);
+       List *l;
+       int i = 1;
+
+       if (nparams != nexpected)
+           elog(ERROR, "Wrong number of parameters, expected %d but got %d",
+                nexpected, nparams);
+
+       foreach (l, stmt->params)
+       {
+           Node *expr = lfirst(l);
+           Oid expected_type_id,
+               given_type_id;
+
+           expr = transformExpr(pstate, expr);
+
+           /* Cannot contain subselects or aggregates */
+           if (contain_subplans(expr))
+               elog(ERROR, "Cannot use subselects in EXECUTE parameters");
+           if (contain_agg_clause(expr))
+               elog(ERROR, "Cannot use aggregates in EXECUTE parameters");
+
+           given_type_id = exprType(expr);
+           expected_type_id = (Oid) lfirsti(paramtypes);
+
+           if (given_type_id != expected_type_id)
+           {
+               expr = CoerceTargetExpr(pstate,
+                                       expr,
+                                       given_type_id,
+                                       expected_type_id,
+                                       -1,
+                                       false);
+
+               if (!expr)
+                   elog(ERROR, "Parameter $%d of type %s cannot be coerced into the expected type %s"
+                               "\n\tYou will need to rewrite or cast the expression",
+                        i,
+                        format_type_be(given_type_id),
+                        format_type_be(expected_type_id));
+           }
+
+           fix_opids(expr);
+           lfirst(l) = expr;
+
+           paramtypes = lnext(paramtypes);
+           i++;
+       }
+   }
+
+   return result;
+}
+
 /* exported so planner can check again after rewriting, query pullup, etc */
 void
 CheckSelectForUpdate(Query *qry)
index 80aa372f9c572f21faf6685010506a10ffddfa54..0f512c7d31b32df62755d13d20ee3273ae1c8738 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.360 2002/08/19 15:08:47 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.361 2002/08/27 04:55:08 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -68,8 +68,6 @@
 extern List *parsetree;            /* final parse result is delivered here */
 
 static bool QueryIsRule = FALSE;
-static Oid *param_type_info;
-static int pfunc_num_args;
 
 /*
  * If you need access to certain yacc-generated variables and find that
@@ -149,7 +147,8 @@ static void doNegateFloat(Value *v);
        SelectStmt, TransactionStmt, TruncateStmt,
        UnlistenStmt, UpdateStmt, VacuumStmt,
        VariableResetStmt, VariableSetStmt, VariableShowStmt,
-       ViewStmt, CheckPointStmt, CreateConversionStmt
+       ViewStmt, CheckPointStmt, CreateConversionStmt,
+       DeallocateStmt, PrepareStmt, ExecuteStmt
 
 %type    select_no_parens, select_with_parens, select_clause,
                simple_select
@@ -218,7 +217,8 @@ static void doNegateFloat(Value *v);
                group_clause, TriggerFuncArgs, select_limit,
                opt_select_limit, opclass_item_list, trans_options,
                TableFuncElementList, OptTableFuncElementList,
-               convert_args
+               convert_args, prep_type_clause, prep_type_list,
+               execute_param_clause, execute_param_list
 
 %type   into_clause, OptTempTableName
 
@@ -335,7 +335,7 @@ static void doNegateFloat(Value *v);
    CREATEUSER, CROSS, CURRENT_DATE, CURRENT_TIME,
    CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, CYCLE,
 
-   DATABASE, DAY_P, DEC, DECIMAL, DECLARE, DEFAULT,
+   DATABASE, DAY_P, DEALLOCATE, DEC, DECIMAL, DECLARE, DEFAULT,
    DEFERRABLE, DEFERRED, DEFINER, DELETE_P, DELIMITER, DELIMITERS,
     DESC, DISTINCT, DO, DOMAIN_P, DOUBLE, DROP,
 
@@ -371,7 +371,7 @@ static void doNegateFloat(Value *v);
    ORDER, OUT_P, OUTER_P, OVERLAPS, OVERLAY, OWNER,
 
    PARTIAL, PASSWORD, PATH_P, PENDANT, PLACING, POSITION,
-   PRECISION, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE,
+   PRECISION, PREPARE, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE,
    PROCEDURAL,
 
    READ, REAL, RECHECK, REFERENCES, REINDEX, RELATIVE, RENAME, REPLACE,
@@ -490,6 +490,7 @@ stmt :
            | CreateTrigStmt
            | CreateUserStmt
            | ClusterStmt
+           | DeallocateStmt
            | DefineStmt
            | DropStmt
            | TruncateStmt
@@ -502,6 +503,7 @@ stmt :
            | DropTrigStmt
            | DropRuleStmt
            | DropUserStmt
+           | ExecuteStmt
            | ExplainStmt
            | FetchStmt
            | GrantStmt
@@ -510,6 +512,7 @@ stmt :
            | UnlistenStmt
            | LockStmt
            | NotifyStmt
+           | PrepareStmt
            | ReindexStmt
            | RemoveAggrStmt
            | RemoveOperStmt
@@ -3875,6 +3878,77 @@ ExplainStmt:
                }
        ;
 
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             PREPARE  [(args, ...)] AS 
+ *
+ *****************************************************************************/
+
+PrepareStmt: PREPARE name prep_type_clause AS OptimizableStmt
+               {
+                   PrepareStmt *n = makeNode(PrepareStmt);
+                   n->name = $2;
+                   n->argtypes = $3;
+                   n->query = (Query *) $5;
+                   $$ = (Node *) n;
+               }
+       ;
+
+prep_type_clause: '(' prep_type_list ')'   { $$ = $2; }
+               | /* EMPTY */               { $$ = NIL; }
+       ;
+
+prep_type_list: Typename           { $$ = makeList1($1); }
+             | prep_type_list ',' Typename
+                                   { $$ = lappend($1, $3); }
+       ;
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             EXECUTE  [(params, ...)] [INTO ...]
+ *
+ *****************************************************************************/
+
+ExecuteStmt: EXECUTE name execute_param_clause into_clause
+               {
+                   ExecuteStmt *n = makeNode(ExecuteStmt);
+                   n->name = $2;
+                   n->params = $3;
+                   n->into = $4;
+                   $$ = (Node *) n;
+               }
+       ;
+
+execute_param_clause: '(' execute_param_list ')'   { $$ = $2; }
+                   | /* EMPTY */                   { $$ = NIL; }
+                   ;
+
+execute_param_list: a_expr                         { $$ = makeList1($1); }
+                 | execute_param_list ',' a_expr   { $$ = lappend($1, $3); }
+                 ;
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             DEALLOCATE [PREPARE] 
+ *
+ *****************************************************************************/
+
+DeallocateStmt: DEALLOCATE name
+                   {
+                       DeallocateStmt *n = makeNode(DeallocateStmt);
+                       n->name = $2;
+                       $$ = (Node *) n;
+                   }
+               | DEALLOCATE PREPARE name
+                   {
+                       DeallocateStmt *n = makeNode(DeallocateStmt);
+                       n->name = $3;
+                       $$ = (Node *) n;
+                   }
+       ;
 
 /*****************************************************************************
  *                                                                          *
@@ -6947,6 +7021,7 @@ unreserved_keyword:
            | CYCLE
            | DATABASE
            | DAY_P
+           | DEALLOCATE
            | DECLARE
            | DEFERRED
            | DEFINER
@@ -7019,6 +7094,7 @@ unreserved_keyword:
            | PATH_P
            | PENDANT
            | PRECISION
+           | PREPARE
            | PRIOR
            | PRIVILEGES
            | PROCEDURAL
@@ -7589,26 +7665,9 @@ SystemTypeName(char *name)
  * Initialize to parse one query string
  */
 void
-parser_init(Oid *typev, int nargs)
+parser_init(void)
 {
    QueryIsRule = FALSE;
-   /*
-    * Keep enough information around to fill out the type of param nodes
-    * used in postquel functions
-    */
-   param_type_info = typev;
-   pfunc_num_args = nargs;
-}
-
-/* param_type()
- * Fetch a parameter type previously passed to parser_init
- */
-Oid
-param_type(int t)
-{
-   if ((t > pfunc_num_args) || (t <= 0))
-       return InvalidOid;
-   return param_type_info[t - 1];
 }
 
 /* exprIsNullConstant()
index 2bb6772054ad26d29305bd17d9c244806fe35edb..9a3064ad661596bea9b1ce1287679dc4117f6dab 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.125 2002/08/18 09:36:25 petere Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.126 2002/08/27 04:55:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -96,6 +96,7 @@ static const ScanKeyword ScanKeywords[] = {
    {"cycle", CYCLE},
    {"database", DATABASE},
    {"day", DAY_P},
+   {"deallocate", DEALLOCATE},
    {"dec", DEC},
    {"decimal", DECIMAL},
    {"declare", DECLARE},
@@ -229,6 +230,7 @@ static const ScanKeyword ScanKeywords[] = {
    {"placing", PLACING},
    {"position", POSITION},
    {"precision", PRECISION},
+   {"prepare", PREPARE},
    {"primary", PRIMARY},
    {"prior", PRIOR},
    {"privileges", PRIVILEGES},
index f4cd24e0c4ff7835a94bbc8b847a0493915bac40..8c129cb9161bd316a1e325c3f8c9b7db69d71239 100644 (file)
@@ -14,7 +14,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.53 2002/06/20 20:29:33 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.54 2002/08/27 04:55:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,9 @@
 
 List      *parsetree;          /* result of parsing is left here */
 
+static Oid *param_type_info;   /* state for param_type() */
+static int param_count;
+
 static int lookahead_token;    /* one-token lookahead */
 static bool have_lookahead;        /* lookahead_token set? */
 
@@ -50,8 +53,9 @@ parser(StringInfo str, Oid *typev, int nargs)
    have_lookahead = false;
 
    scanner_init(str);
-   parser_init(typev, nargs);
+   parser_init();
    parse_expr_init();
+   parser_param_set(typev, nargs);
 
    yyresult = yyparse();
 
@@ -65,6 +69,35 @@ parser(StringInfo str, Oid *typev, int nargs)
 }
 
 
+/*
+ * Save information needed to fill out the type of Param references ($n)
+ *
+ * This is used for SQL functions, PREPARE statements, etc.  It's split
+ * out from parser() setup because PREPARE needs to change the info after
+ * the grammar runs and before parse analysis is done on the preparable
+ * query.
+ */
+void
+parser_param_set(Oid *typev, int nargs)
+{
+   param_type_info = typev;
+   param_count = nargs;
+}
+
+/*
+ * param_type()
+ *
+ * Fetch a parameter type previously passed to parser_param_set
+ */
+Oid
+param_type(int t)
+{
+   if (t > param_count || t <= 0)
+       return InvalidOid;
+   return param_type_info[t - 1];
+}
+
+
 /*
  * Intermediate filter between parser and base lexer (base_yylex in scan.l).
  *
index 605f44ba70b9f7ac9c5a50f598219f407f4341e8..28576b8fad610864929606cf0d96f9bda3f96777 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.283 2002/08/17 15:12:07 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.284 2002/08/27 04:55:11 tgl Exp $
  *
  * NOTES
  *   this is the "main" module of the postgres backend and
@@ -1666,7 +1666,7 @@ PostgresMain(int argc, char *argv[], const char *username)
    if (!IsUnderPostmaster)
    {
        puts("\nPOSTGRES backend interactive interface ");
-       puts("$Revision: 1.283 $ $Date: 2002/08/17 15:12:07 $\n");
+       puts("$Revision: 1.284 $ $Date: 2002/08/27 04:55:11 $\n");
    }
 
    /*
@@ -2406,6 +2406,18 @@ CreateCommandTag(Node *parsetree)
            tag = "DROP OPERATOR CLASS";
            break;
 
+       case T_PrepareStmt:
+           tag = "PREPARE";
+           break;
+
+       case T_ExecuteStmt:
+           tag = "EXECUTE";
+           break;
+
+       case T_DeallocateStmt:
+           tag = "DEALLOCATE";
+           break;
+
        default:
            elog(LOG, "CreateCommandTag: unknown parse node type %d",
                 nodeTag(parsetree));
index 1ae0a89fd6be669ff456774de8abcee36d92a5c2..b16adef54dbce3e68463c23891c3f929c4b8053f 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.172 2002/08/17 13:04:15 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.173 2002/08/27 04:55:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,7 @@
 #include "commands/explain.h"
 #include "commands/lockcmds.h"
 #include "commands/portalcmds.h"
+#include "commands/prepare.h"
 #include "commands/proclang.h"
 #include "commands/schemacmds.h"
 #include "commands/sequence.h"
@@ -379,6 +380,18 @@ ProcessUtility(Node *parsetree,
            }
            break;
 
+       case T_PrepareStmt:
+           PrepareQuery((PrepareStmt *) parsetree);
+           break;
+
+       case T_ExecuteStmt:
+           ExecuteQuery((ExecuteStmt *) parsetree, dest);
+           break;
+
+       case T_DeallocateStmt:
+           DeallocateQuery((DeallocateStmt *) parsetree);
+           break;
+
            /*
             * schema
             */
@@ -541,11 +554,7 @@ ProcessUtility(Node *parsetree,
 
 
        case T_GrantStmt:
-           {
-               GrantStmt  *stmt = (GrantStmt *) parsetree;
-
-               ExecuteGrantStmt(stmt);
-           }
+           ExecuteGrantStmt((GrantStmt *) parsetree);
            break;
 
            /*
@@ -841,9 +850,7 @@ ProcessUtility(Node *parsetree,
            break;
 
        case T_CreateConversionStmt:
-           {
-               CreateConversionCommand((CreateConversionStmt *) parsetree);
-           }
+           CreateConversionCommand((CreateConversionStmt *) parsetree);
            break;
 
        case T_CreateCastStmt:
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
new file mode 100644 (file)
index 0000000..6af60fe
--- /dev/null
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * prepare.h
+ *   PREPARE, EXECUTE and DEALLOCATE command prototypes
+ *
+ *
+ * Copyright (c) 2002, PostgreSQL Global Development Group
+ *
+ * $Id: prepare.h,v 1.1 2002/08/27 04:55:11 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PREPARE_H
+#define PREPARE_H
+
+#include "nodes/parsenodes.h"
+#include "tcop/dest.h"
+
+
+extern void PrepareQuery(PrepareStmt *stmt);
+
+extern void ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest);
+
+extern void DeallocateQuery(DeallocateStmt *stmt);
+
+extern List *FetchQueryParams(const char *plan_name);
+
+#endif /* PREPARE_H */
index f3437ce4cbf66adf1df958b2e9799aac0217292f..3f5f6d744998d40c868a95d727132d14c8e159b2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.116 2002/08/19 15:08:47 tgl Exp $
+ * $Id: nodes.h,v 1.117 2002/08/27 04:55:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -200,6 +200,9 @@ typedef enum NodeTag
    T_DropCastStmt,
    T_CreateOpClassStmt,
    T_RemoveOpClassStmt,
+   T_PrepareStmt,
+   T_ExecuteStmt,
+   T_DeallocateStmt,
 
    T_A_Expr = 700,
    T_ColumnRef,
index ecf59f30c10536dd695c737e50bffd08b52d8c7b..25ec8a3542b9dd0710417cb05cb64bcd6b4150d0 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.201 2002/08/19 15:08:47 tgl Exp $
+ * $Id: parsenodes.h,v 1.202 2002/08/27 04:55:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1620,4 +1620,42 @@ typedef struct DropCastStmt
 } DropCastStmt;
 
 
+/* ----------------------
+ *     PREPARE Statement
+ * ----------------------
+ */
+typedef struct PrepareStmt
+{
+   NodeTag      type;
+   char        *name;          /* Name of plan, arbitrary */
+   List        *argtypes;      /* Types of parameters (TypeNames) */
+   List        *argtype_oids;  /* Types of parameters (OIDs) */
+   Query       *query;         /* The query itself */
+} PrepareStmt;
+
+
+/* ----------------------
+ *     EXECUTE Statement
+ * ----------------------
+ */
+
+typedef struct ExecuteStmt
+{
+   NodeTag      type;
+   char        *name;          /* The name of the plan to execute */
+   RangeVar    *into;          /* Optional table to store results in */
+   List        *params;        /* Values to assign to parameters */
+} ExecuteStmt;
+
+
+/* ----------------------
+ *     DEALLOCATE Statement
+ * ----------------------
+ */
+typedef struct DeallocateStmt
+{
+   NodeTag     type;
+   char       *name;           /* The name of the plan to remove */
+} DeallocateStmt;
+
 #endif   /* PARSENODES_H */
index 0bd00cb1b0cac29891e1fc7c99491459d1eaf29e..6af3bafbfb3d796379e3def4c7a9d69098f2f856 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: gramparse.h,v 1.23 2002/06/20 20:29:51 momjian Exp $
+ * $Id: gramparse.h,v 1.24 2002/08/27 04:55:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,8 @@
 #include "nodes/parsenodes.h"
 
 /* from parser.c */
+extern void parser_param_set(Oid *typev, int nargs);
+extern Oid param_type(int t);
 extern int yylex(void);
 
 /* from scan.l */
@@ -28,8 +30,7 @@ extern int    base_yylex(void);
 extern void yyerror(const char *message);
 
 /* from gram.y */
-extern void parser_init(Oid *typev, int nargs);
-extern Oid param_type(int t);
+extern void parser_init(void);
 extern int yyparse(void);
 extern List *SystemFuncName(char *name);
 extern TypeName *SystemTypeName(char *name);
diff --git a/src/test/regress/expected/prepare.out b/src/test/regress/expected/prepare.out
new file mode 100644 (file)
index 0000000..166be86
--- /dev/null
@@ -0,0 +1,107 @@
+-- Regression tests for prepareable statements
+PREPARE q1 AS SELECT 1;
+EXECUTE q1;
+ ?column? 
+----------
+        1
+(1 row)
+
+-- should fail
+PREPARE q1 AS SELECT 2;
+ERROR:  Prepared statement with name "q1" already exists
+-- should succeed
+DEALLOCATE q1;
+PREPARE q1 AS SELECT 2;
+EXECUTE q1;
+ ?column? 
+----------
+        2
+(1 row)
+
+-- sql92 syntax
+DEALLOCATE PREPARE q1;
+-- parameterized queries
+PREPARE q2(text) AS
+   SELECT datname, datistemplate, datallowconn
+   FROM pg_database WHERE datname = $1;
+EXECUTE q2('regression');
+  datname   | datistemplate | datallowconn 
+------------+---------------+--------------
+ regression | f             | t
+(1 row)
+
+PREPARE q3(text, int, float, boolean, oid, smallint) AS
+   SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR
+   ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int);
+EXECUTE q3('AAAAxx', 5::smallint, 10.5::float, false, 500::oid, 4::bigint);
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+    4502 |     412 |   0 |    2 |   2 |      2 |       2 |      502 |         502 |      4502 |     4502 |   4 |    5 | ERAAAA   | WPAAAA   | AAAAxx
+     102 |     612 |   0 |    2 |   2 |      2 |       2 |      102 |         102 |       102 |      102 |   4 |    5 | YDAAAA   | OXAAAA   | AAAAxx
+    7602 |    1040 |   0 |    2 |   2 |      2 |       2 |      602 |        1602 |      2602 |     7602 |   4 |    5 | KGAAAA   | AOBAAA   | AAAAxx
+     902 |    1104 |   0 |    2 |   2 |      2 |       2 |      902 |         902 |       902 |      902 |   4 |    5 | SIAAAA   | MQBAAA   | AAAAxx
+    4902 |    1600 |   0 |    2 |   2 |      2 |       2 |      902 |         902 |      4902 |     4902 |   4 |    5 | OGAAAA   | OJCAAA   | AAAAxx
+    9502 |    1812 |   0 |    2 |   2 |      2 |       2 |      502 |        1502 |      4502 |     9502 |   4 |    5 | MBAAAA   | SRCAAA   | AAAAxx
+    4702 |    2520 |   0 |    2 |   2 |      2 |       2 |      702 |         702 |      4702 |     4702 |   4 |    5 | WYAAAA   | YSDAAA   | AAAAxx
+    1002 |    2580 |   0 |    2 |   2 |      2 |       2 |        2 |        1002 |      1002 |     1002 |   4 |    5 | OMAAAA   | GVDAAA   | AAAAxx
+       2 |    2716 |   0 |    2 |   2 |      2 |       2 |        2 |           2 |         2 |        2 |   4 |    5 | CAAAAA   | MAEAAA   | AAAAxx
+     802 |    2908 |   0 |    2 |   2 |      2 |       2 |      802 |         802 |       802 |      802 |   4 |    5 | WEAAAA   | WHEAAA   | AAAAxx
+    6402 |    3808 |   0 |    2 |   2 |      2 |       2 |      402 |         402 |      1402 |     6402 |   4 |    5 | GMAAAA   | MQFAAA   | AAAAxx
+    8602 |    5440 |   0 |    2 |   2 |      2 |       2 |      602 |         602 |      3602 |     8602 |   4 |    5 | WSAAAA   | GBIAAA   | AAAAxx
+    8402 |    5708 |   0 |    2 |   2 |      2 |       2 |      402 |         402 |      3402 |     8402 |   4 |    5 | ELAAAA   | OLIAAA   | AAAAxx
+    2102 |    6184 |   0 |    2 |   2 |      2 |       2 |      102 |         102 |      2102 |     2102 |   4 |    5 | WCAAAA   | WDJAAA   | AAAAxx
+    4202 |    6628 |   0 |    2 |   2 |      2 |       2 |      202 |         202 |      4202 |     4202 |   4 |    5 | QFAAAA   | YUJAAA   | AAAAxx
+    2902 |    6816 |   0 |    2 |   2 |      2 |       2 |      902 |         902 |      2902 |     2902 |   4 |    5 | QHAAAA   | ECKAAA   | AAAAxx
+    2302 |    7112 |   0 |    2 |   2 |      2 |       2 |      302 |         302 |      2302 |     2302 |   4 |    5 | OKAAAA   | ONKAAA   | AAAAxx
+    3202 |    7128 |   0 |    2 |   2 |      2 |       2 |      202 |        1202 |      3202 |     3202 |   4 |    5 | ETAAAA   | EOKAAA   | AAAAxx
+    7802 |    7508 |   0 |    2 |   2 |      2 |       2 |      802 |        1802 |      2802 |     7802 |   4 |    5 | COAAAA   | UCLAAA   | AAAAxx
+    4102 |    7676 |   0 |    2 |   2 |      2 |       2 |      102 |         102 |      4102 |     4102 |   4 |    5 | UBAAAA   | GJLAAA   | AAAAxx
+    8302 |    7800 |   0 |    2 |   2 |      2 |       2 |      302 |         302 |      3302 |     8302 |   4 |    5 | IHAAAA   | AOLAAA   | AAAAxx
+    1702 |    7940 |   0 |    2 |   2 |      2 |       2 |      702 |        1702 |      1702 |     1702 |   4 |    5 | MNAAAA   | KTLAAA   | AAAAxx
+    2202 |    8028 |   0 |    2 |   2 |      2 |       2 |      202 |         202 |      2202 |     2202 |   4 |    5 | SGAAAA   | UWLAAA   | AAAAxx
+    1602 |    8148 |   0 |    2 |   2 |      2 |       2 |      602 |        1602 |      1602 |     1602 |   4 |    5 | QJAAAA   | KBMAAA   | AAAAxx
+    5602 |    8796 |   0 |    2 |   2 |      2 |       2 |      602 |        1602 |       602 |     5602 |   4 |    5 | MHAAAA   | IANAAA   | AAAAxx
+    6002 |    8932 |   0 |    2 |   2 |      2 |       2 |        2 |           2 |      1002 |     6002 |   4 |    5 | WWAAAA   | OFNAAA   | AAAAxx
+    3902 |    9224 |   0 |    2 |   2 |      2 |       2 |      902 |        1902 |      3902 |     3902 |   4 |    5 | CUAAAA   | UQNAAA   | AAAAxx
+    9602 |    9972 |   0 |    2 |   2 |      2 |       2 |      602 |        1602 |      4602 |     9602 |   4 |    5 | IFAAAA   | OTOAAA   | AAAAxx
+    8002 |    9980 |   0 |    2 |   2 |      2 |       2 |        2 |           2 |      3002 |     8002 |   4 |    5 | UVAAAA   | WTOAAA   | AAAAxx
+(29 rows)
+
+-- too few params
+EXECUTE q3('bool');
+ERROR:  Wrong number of parameters, expected 6 but got 1
+-- too many params
+EXECUTE q3('bytea', 5::smallint, 10.5::float, false, 500::oid, 4::bigint, true);
+ERROR:  Wrong number of parameters, expected 6 but got 7
+-- wrong param types
+EXECUTE q3(5::smallint, 10.5::float, false, 500::oid, 4::bigint, 'bytea');
+ERROR:  Parameter $2 of type double precision cannot be coerced into the expected type integer
+   You will need to rewrite or cast the expression
+-- invalid type
+PREPARE q4(nonexistenttype) AS SELECT $1;
+ERROR:  Type "nonexistenttype" does not exist
+-- execute into
+PREPARE q5(int, text) AS
+   SELECT * FROM tenk1 WHERE unique1 = $1 OR stringu1 = $2;
+EXECUTE q5(200, 'DTAAAA') INTO TEMPORARY q5_prep_results;
+SELECT * FROM q5_prep_results;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+    2525 |      64 |   1 |    1 |   5 |      5 |      25 |      525 |         525 |      2525 |     2525 |  50 |   51 | DTAAAA   | MCAAAA   | AAAAxx
+    7257 |    1895 |   1 |    1 |   7 |     17 |      57 |      257 |        1257 |      2257 |     7257 | 114 |  115 | DTAAAA   | XUCAAA   | VVVVxx
+    9961 |    2058 |   1 |    1 |   1 |      1 |      61 |      961 |        1961 |      4961 |     9961 | 122 |  123 | DTAAAA   | EBDAAA   | OOOOxx
+    3877 |    4060 |   1 |    1 |   7 |     17 |      77 |      877 |        1877 |      3877 |     3877 | 154 |  155 | DTAAAA   | EAGAAA   | AAAAxx
+    4553 |    4113 |   1 |    1 |   3 |     13 |      53 |      553 |         553 |      4553 |     4553 | 106 |  107 | DTAAAA   | FCGAAA   | HHHHxx
+    7933 |    4514 |   1 |    1 |   3 |     13 |      33 |      933 |        1933 |      2933 |     7933 |  66 |   67 | DTAAAA   | QRGAAA   | OOOOxx
+    6581 |    4686 |   1 |    1 |   1 |      1 |      81 |      581 |         581 |      1581 |     6581 | 162 |  163 | DTAAAA   | GYGAAA   | OOOOxx
+    8609 |    5918 |   1 |    1 |   9 |      9 |       9 |      609 |         609 |      3609 |     8609 |  18 |   19 | DTAAAA   | QTIAAA   | OOOOxx
+    5229 |    6407 |   1 |    1 |   9 |      9 |      29 |      229 |        1229 |       229 |     5229 |  58 |   59 | DTAAAA   | LMJAAA   | VVVVxx
+    1173 |    6699 |   1 |    1 |   3 |     13 |      73 |      173 |        1173 |      1173 |     1173 | 146 |  147 | DTAAAA   | RXJAAA   | VVVVxx
+    3201 |    7309 |   1 |    1 |   1 |      1 |       1 |      201 |        1201 |      3201 |     3201 |   2 |    3 | DTAAAA   | DVKAAA   | HHHHxx
+    1849 |    8143 |   1 |    1 |   9 |      9 |      49 |      849 |        1849 |      1849 |     1849 |  98 |   99 | DTAAAA   | FBMAAA   | VVVVxx
+    9285 |    8469 |   1 |    1 |   5 |      5 |      85 |      285 |        1285 |      4285 |     9285 | 170 |  171 | DTAAAA   | TNMAAA   | HHHHxx
+     497 |    9092 |   1 |    1 |   7 |     17 |      97 |      497 |         497 |       497 |      497 | 194 |  195 | DTAAAA   | SLNAAA   | AAAAxx
+     200 |    9441 |   0 |    0 |   0 |      0 |       0 |      200 |         200 |       200 |      200 |   0 |    1 | SHAAAA   | DZNAAA   | HHHHxx
+    5905 |    9537 |   1 |    1 |   5 |      5 |       5 |      905 |        1905 |       905 |     5905 |  10 |   11 | DTAAAA   | VCOAAA   | HHHHxx
+(16 rows)
+
index e82d9421679cfb5d1e7045702790853ea91d89e4..94dedb2b4917d6589b5a0be7a1e196ec774b66fb 100644 (file)
@@ -74,4 +74,4 @@ test: select_views alter_table portals_p2 rules foreign_key cluster
 # The sixth group of parallel test
 # ----------
 # "plpgsql" cannot run concurrently with "rules"
-test: limit plpgsql temp domain rangefuncs copy2 conversion without_oid truncate
+test: limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate
index 553df2dfe7d6939ae97789eb4dc043e8f85ec5c7..cf00eeb961cd27f3a2b7c829af3d4c6c127e04b3 100644 (file)
@@ -1,4 +1,4 @@
-# $Header: /cvsroot/pgsql/src/test/regress/serial_schedule,v 1.16 2002/08/22 04:51:06 momjian Exp $
+# $Header: /cvsroot/pgsql/src/test/regress/serial_schedule,v 1.17 2002/08/27 04:55:12 tgl Exp $
 # This should probably be in an order similar to parallel_schedule.
 test: boolean
 test: char
@@ -86,6 +86,7 @@ test: copy2
 test: temp
 test: domain
 test: rangefuncs
+test: prepare
 test: without_oid
 test: conversion
 test: truncate
diff --git a/src/test/regress/sql/prepare.sql b/src/test/regress/sql/prepare.sql
new file mode 100644 (file)
index 0000000..ee8df42
--- /dev/null
@@ -0,0 +1,45 @@
+-- Regression tests for prepareable statements
+
+PREPARE q1 AS SELECT 1;
+EXECUTE q1;
+
+-- should fail
+PREPARE q1 AS SELECT 2;
+
+-- should succeed
+DEALLOCATE q1;
+PREPARE q1 AS SELECT 2;
+EXECUTE q1;
+
+-- sql92 syntax
+DEALLOCATE PREPARE q1;
+
+-- parameterized queries
+PREPARE q2(text) AS
+   SELECT datname, datistemplate, datallowconn
+   FROM pg_database WHERE datname = $1;
+EXECUTE q2('regression');
+
+PREPARE q3(text, int, float, boolean, oid, smallint) AS
+   SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR
+   ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int);
+
+EXECUTE q3('AAAAxx', 5::smallint, 10.5::float, false, 500::oid, 4::bigint);
+
+-- too few params
+EXECUTE q3('bool');
+
+-- too many params
+EXECUTE q3('bytea', 5::smallint, 10.5::float, false, 500::oid, 4::bigint, true);
+
+-- wrong param types
+EXECUTE q3(5::smallint, 10.5::float, false, 500::oid, 4::bigint, 'bytea');
+
+-- invalid type
+PREPARE q4(nonexistenttype) AS SELECT $1;
+
+-- execute into
+PREPARE q5(int, text) AS
+   SELECT * FROM tenk1 WHERE unique1 = $1 OR stringu1 = $2;
+EXECUTE q5(200, 'DTAAAA') INTO TEMPORARY q5_prep_results;
+SELECT * FROM q5_prep_results;