PREPARE/EXECUTE statements. Patch by Neil Conway, some kibitzing
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 27 Aug 2002 04:55:12 +0000 (04:55 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.47 2002/08/27 03:38:27 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.48 2002/08/27 04:55:07 tgl Exp $
 PostgreSQL documentation
 Complete list of usable sgml source files in this directory.
 -->
@@ -71,6 +71,7 @@ Complete list of usable sgml source files in this directory.
 <!entity createType         system "create_type.sgml">
 <!entity createUser         system "create_user.sgml">
 <!entity createView         system "create_view.sgml">
+<!entity deallocate         system "deallocate.sgml">
 <!entity declare            system "declare.sgml">
 <!entity delete             system "delete.sgml">
 <!entity dropAggregate      system "drop_aggregate.sgml">
@@ -93,6 +94,7 @@ Complete list of usable sgml source files in this directory.
 <!entity dropUser           system "drop_user.sgml">
 <!entity dropView           system "drop_view.sgml">
 <!entity end                system "end.sgml">
+<!entity execute            system "execute.sgml">
 <!entity explain            system "explain.sgml">
 <!entity fetch              system "fetch.sgml">
 <!entity grant              system "grant.sgml">
@@ -102,6 +104,7 @@ Complete list of usable sgml source files in this directory.
 <!entity lock               system "lock.sgml">
 <!entity move               system "move.sgml">
 <!entity notify             system "notify.sgml">
+<!entity prepare            system "prepare.sgml">
 <!entity reindex            system "reindex.sgml">
 <!entity reset              system "reset.sgml">
 <!entity revoke             system "revoke.sgml">
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 @@
+<!--
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/deallocate.sgml,v 1.1 2002/08/27 04:55:07 tgl Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-DEALLOCATE">
+ <refmeta>
+  <refentrytitle id="sql-deallocate-title">DEALLOCATE</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+ <refnamediv>
+  <refname>
+   DEALLOCATE
+  </refname>
+  <refpurpose>
+   remove a prepared query
+  </refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+  <refsynopsisdivinfo>
+   <date>2002-08-12</date>
+  </refsynopsisdivinfo>
+  <synopsis>
+   DEALLOCATE [ PREPARE ] <replaceable class="PARAMETER">plan_name</replaceable>
+  </synopsis>
+
+  <refsect2 id="R2-SQL-DEALLOCATE-1">
+   <refsect2info>
+    <date>2002-08-12</date>
+   </refsect2info>
+   <title>
+    Inputs
+   </title>
+
+   <para>
+    <variablelist>
+        <varlistentry>
+         <term>PREPARE</term>
+         <listitem>
+          <para>
+               This keyword is ignored.
+          </para>
+         </listitem>
+         </varlistentry>
+     <varlistentry>
+      <term><replaceable class="PARAMETER">plan_name</replaceable></term>
+      <listitem>
+       <para>
+               The name of the prepared query to remove.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+  </refsect2>
+  <refsect2 id="R2-SQL-DEALLOCATE-2">
+   <refsect2info>
+    <date>2002-08-12</date>
+   </refsect2info>
+   <title>
+    Outputs
+   </title>
+   <para>
+
+    <variablelist>
+     <varlistentry>
+      <term><computeroutput>
+               <returnvalue>DEALLOCATE</returnvalue>
+       </computeroutput></term>
+      <listitem>
+       <para>
+               The prepared query was removed successfully.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+  </refsect2>
+ </refsynopsisdiv>
+
+ <refsect1 id="R1-SQL-DEALLOCATE-1">
+  <refsect1info>
+   <date>2002-08-12</date>
+  </refsect1info>
+  <title>
+   Description
+  </title>
+
+  <para>
+   <command>DEALLOCATE</command> is used to remove a previously
+   prepared query. If you do not explicitly
+   <command>DEALLOCATE</command> a prepared query, it is removed when
+   the session ends.
+  </para>
+
+  <para>
+   For more information on prepared queries, see <xref
+   linkend="sql-prepare" endterm="sql-prepare-title">.
+  </para>
+ </refsect1>
+
+ <refsect1 id="R1-SQL-DEALLOCATE-2">
+  <title>
+   Compatibility
+  </title>
+
+  <refsect2 id="R2-SQL-DEALLOCATE-3">
+   <refsect2info>
+    <date>2002-08-12</date>
+   </refsect2info>
+   <title>
+    SQL92
+   </title>
+   <para>
+       SQL92 includes a <command>DEALLOCATE</command> statement, but it is
+    only for use in embedded SQL clients.
+   </para>
+  </refsect2>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:nil
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:"../reference.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:"/usr/lib/sgml/catalog"
+sgml-local-ecat-files:nil
+End:
+-->
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 @@
+<!--
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/execute.sgml,v 1.1 2002/08/27 04:55:07 tgl Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-EXECUTE">
+ <refmeta>
+  <refentrytitle id="sql-execute-title">EXECUTE</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+ <refnamediv>
+  <refname>
+   EXECUTE
+  </refname>
+  <refpurpose>
+   execute a prepared query
+  </refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+  <refsynopsisdivinfo>
+   <date>2002-08-12</date>
+  </refsynopsisdivinfo>
+  <synopsis>
+   EXECUTE <replaceable class="PARAMETER">plan_name</replaceable> [ (<replaceable class="PARAMETER">parameter</replaceable> [, ...] ) ]
+  </synopsis>
+
+  <refsect2 id="R2-SQL-EXECUTE-1">
+   <refsect2info>
+    <date>2002-08-12</date>
+   </refsect2info>
+   <title>
+    Inputs
+   </title>
+
+   <para>
+    <variablelist>
+     <varlistentry>
+      <term><replaceable class="PARAMETER">plan_name</replaceable></term>
+      <listitem>
+       <para>
+               The name of the prepared query to execute.
+       </para>
+      </listitem>
+     </varlistentry>
+     <varlistentry>
+      <term><replaceable class="PARAMETER">parameter</replaceable></term>
+      <listitem>
+       <para>
+               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
+               <command>PREPARE</command> statement that created the prepared
+               query.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+  </refsect2>
+ </refsynopsisdiv>
+
+ <refsect1 id="R1-SQL-EXECUTE-1">
+  <refsect1info>
+   <date>2002-08-12</date>
+  </refsect1info>
+  <title>
+   Description
+  </title>
+
+  <para>
+   <command>EXECUTE</command> 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
+   <command>PREPARE</command> statement executed earlier in the
+   current session.
+  </para>
+
+  <para>
+   If the <command>PREPARE</command> statement that created the query
+   specified some parameters, a compatible set of parameters must be
+   passed to the <command>EXECUTE</command> 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.
+  </para>
+
+  <para>
+   For more information on the creation and usage of prepared queries,
+   see <xref linkend="sql-prepare" endterm="sql-prepare-title">.
+  </para>
+ </refsect1>
+
+ <refsect1 id="R1-SQL-EXECUTE-2">
+  <title>
+   Compatibility
+  </title>
+
+  <refsect2 id="R2-SQL-EXECUTE-2">
+   <refsect2info>
+    <date>2002-08-12</date>
+   </refsect2info>
+   <title>
+    SQL92
+   </title>
+   <para>
+       SQL92 includes an <command>EXECUTE</command> statement, but it is
+    only for use in embedded SQL clients. The
+    <command>EXECUTE</command> statement implemented by
+    <productname>PostgreSQL</productname> also uses a somewhat
+    different syntax.
+   </para>
+  </refsect2>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:nil
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:"../reference.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:"/usr/lib/sgml/catalog"
+sgml-local-ecat-files:nil
+End:
+-->
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 @@
+<!--
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/prepare.sgml,v 1.1 2002/08/27 04:55:07 tgl Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-PREPARE">
+ <refmeta>
+  <refentrytitle id="sql-prepare-title">PREPARE</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+ <refnamediv>
+  <refname>
+   PREPARE
+  </refname>
+  <refpurpose>
+   create a prepared query
+  </refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+  <refsynopsisdivinfo>
+   <date>2002-08-12</date>
+  </refsynopsisdivinfo>
+  <synopsis>
+   PREPARE <replaceable class="PARAMETER">plan_name</replaceable> [ (<replaceable class="PARAMETER">datatype</replaceable> [, ...] ) ] AS <replaceable class="PARAMETER">query</replaceable>
+  </synopsis>
+
+  <refsect2 id="R2-SQL-PREPARE-1">
+   <refsect2info>
+    <date>2002-08-12</date>
+   </refsect2info>
+   <title>
+    Inputs
+   </title>
+
+   <para>
+    <variablelist>
+     <varlistentry>
+      <term><replaceable class="PARAMETER">plan_name</replaceable></term>
+      <listitem>
+       <para>
+               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.
+       </para>
+      </listitem>
+     </varlistentry>
+     <varlistentry>
+      <term><replaceable class="PARAMETER">datatype</replaceable></term>
+      <listitem>
+       <para>
+               The data-type of a parameter to the prepared query.
+               To refer to the parameters in the prepared query itself,
+               use <literal>$1</literal>, <literal>$2</literal>, etc.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+  </refsect2>
+
+  <refsect2 id="R2-SQL-PREPARE-2">
+   <refsect2info>
+    <date>2002-08-12</date>
+   </refsect2info>
+   <title>
+    Outputs
+   </title>
+   <para>
+
+    <variablelist>
+     <varlistentry>
+      <term><computeroutput>
+               <returnvalue>PREPARE</returnvalue>
+       </computeroutput></term>
+      <listitem>
+       <para>
+               The query has been prepared successfully.
+       </para>
+      </listitem>
+     </varlistentry>
+
+    </variablelist>
+   </para>
+  </refsect2>
+ </refsynopsisdiv>
+
+ <refsect1 id="R1-SQL-PREPARE-1">
+  <refsect1info>
+   <date>2002-08-12</date>
+  </refsect1info>
+  <title>
+   Description
+  </title>
+  <para>
+   <command>PREPARE</command> creates a prepared query. A prepared
+   query is a server-side object that can be used to optimize
+   performance. When the <command>PREPARE</command> statement is
+   executed, the specified query is parsed, rewritten, and
+   planned. When a subsequent <command>EXECUTE</command> 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.
+  </para>
+
+  <para>
+   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 <command>PREPARE</command> statement. In the query itself, you
+   can refer to the parameters by position using
+   <literal>$1</literal>, <literal>$2</literal>, etc. When executing
+   the query, specify the actual values for these parameters in the
+   <command>EXECUTE</command> statement -- refer to <xref
+   linkend="sql-execute" endterm="sql-execute-title">
+   for more information.
+  </para>
+
+  <para>
+   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.
+  </para>
+
+  <para>
+   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.
+  </para>
+
+  <refsect2 id="R2-SQL-PREPARE-3">
+   <refsect2info>
+    <date>2002-08-12</date>
+   </refsect2info>
+   <title>
+    Notes
+   </title>
+
+   <para>
+       In some situations, the query plan produced by
+       <productname>PostgreSQL</productname> 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. <productname>PostgreSQL</productname> 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.
+   </para>
+
+   <para>
+       For more information on query planning and the statistics
+       collected by <productname>PostgreSQL</productname> for query
+       optimization purposes, see the <xref linkend="sql-analyze"
+       endterm="sql-analyze-title"> documentation.
+   </para>
+  </refsect2>
+ </refsect1>
+
+ <refsect1 id="R1-SQL-PREPARE-3">
+  <title>
+   Compatibility
+  </title>
+
+  <refsect2 id="R2-SQL-PREPARE-4">
+   <refsect2info>
+    <date>2002-08-12</date>
+   </refsect2info>
+   <title>
+    SQL92
+   </title>
+   <para>
+       SQL92 includes a <command>PREPARE</command> statement, but it is
+    only for use in embedded SQL clients. The
+    <command>PREPARE</command> statement implemented by
+    <productname>PostgreSQL</productname> also uses a somewhat
+    different syntax.
+   </para>
+  </refsect2>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:nil
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:"../reference.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:"/usr/lib/sgml/catalog"
+sgml-local-ecat-files:nil
+End:
+-->
index effe495f1da38c004d76a07c1931926f766793da..22a3c07a3e69eefde9264d92ed50935d50b17eed 100644 (file)
@@ -1,5 +1,5 @@
 <!-- reference.sgml
-$Header: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v 1.36 2002/08/27 03:38:27 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v 1.37 2002/08/27 04:55:07 tgl Exp $
 
 PostgreSQL Reference Manual
 -->
@@ -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;
    &notify;
+   &prepare;
    &reindex;
    &reset;
    &revoke;
index 4be5868512091d62d9e0483937882c82ee68b99f..e457504ebefcf4a5352d3dac79ac6525cf7fa45a 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.151 2002/08/25 14:34:24 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.152 2002/08/27 04:55:07 tgl Exp $
 -->
 
 <appendix id="release">
@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
 worries about funny characters.
 -->
 <literallayout><![CDATA[
+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 <node>   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 <range>  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 <plan_name> [(args, ...)] AS <query>
+ *
+ *****************************************************************************/
+
+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 <plan_name> [(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] <plan_name>
+ *
+ *****************************************************************************/
+
+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;