fix for parameterized queries in DECLARE CURSOR statements - Mailing list pgsql-patches

From Oliver Jowett
Subject fix for parameterized queries in DECLARE CURSOR statements
Date
Msg-id 4104EE21.4040504@opencloud.com
Whole thread Raw
Responses Re: fix for parameterized queries in DECLARE CURSOR statements
List pgsql-patches
Here's a patch that allows parameterized queries to be used in a DECLARE
CURSOR statement.

Previously, the DECLARE would succeed but any FETCHes would fail as the
parameter values supplied to DECLARE were not propagated to the portal
it created. This patch adds that propagation. See
http://archives.postgresql.org/pgsql-hackers/2004-07/msg01047.php for
discussion.

I've tested the V3 protocol and SQL function paths by hand. The SPI path
hasn't been specifically tested but the regression tests appear happy.

-O

? src/Makefile.global
? src/include/pg_config.h
? src/include/stamp-h
Index: src/backend/commands/portalcmds.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/portalcmds.c,v
retrieving revision 1.29
diff -u -c -r1.29 portalcmds.c
*** src/backend/commands/portalcmds.c    17 Jul 2004 03:28:47 -0000    1.29
--- src/backend/commands/portalcmds.c    26 Jul 2004 11:37:24 -0000
***************
*** 36,42 ****
   *        Execute SQL DECLARE CURSOR command.
   */
  void
! PerformCursorOpen(DeclareCursorStmt *stmt)
  {
      List       *rewritten;
      Query       *query;
--- 36,42 ----
   *        Execute SQL DECLARE CURSOR command.
   */
  void
! PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
  {
      List       *rewritten;
      Query       *query;
***************
*** 104,111 ****
                        list_make1(plan),
                        PortalGetHeapMemory(portal));

-     MemoryContextSwitchTo(oldContext);
-
      /*
       * Set up options for portal.
       *
--- 104,109 ----
***************
*** 123,131 ****
      }

      /*
!      * Start execution --- never any params for a cursor.
       */
!     PortalStart(portal, NULL);

      Assert(portal->strategy == PORTAL_ONE_SELECT);

--- 121,133 ----
      }

      /*
!      * Start execution. The original DECLARE may have had parameters
!      * (if V3 protocol Parse/Bind messages were used); we must
!      * pass those parameters into the new portal, making sure to copy them
!      * into the new portal's memory context as the originals are owned by
!      * our caller.
       */
!     PortalStart(portal, copyParamList(params));

      Assert(portal->strategy == PORTAL_ONE_SELECT);

***************
*** 133,138 ****
--- 135,142 ----
       * We're done; the query won't actually be run until
       * PerformPortalFetch is called.
       */
+
+     MemoryContextSwitchTo(oldContext);
  }

  /*
Index: src/backend/commands/prepare.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/prepare.c,v
retrieving revision 1.28
diff -u -c -r1.28 prepare.c
*** src/backend/commands/prepare.c    11 Jun 2004 01:08:38 -0000    1.28
--- src/backend/commands/prepare.c    26 Jul 2004 11:37:24 -0000
***************
*** 211,217 ****
      int            nargs = list_length(argtypes);
      ParamListInfo paramLI;
      List       *exprstates;
!     ListCell   *l;
      int            i = 0;

      /* Parser should have caught this error, but check for safety */
--- 211,217 ----
      int            nargs = list_length(argtypes);
      ParamListInfo paramLI;
      List       *exprstates;
!     ListCell   *le, *la;
      int            i = 0;

      /* Parser should have caught this error, but check for safety */
***************
*** 223,231 ****
      paramLI = (ParamListInfo)
          palloc0((nargs + 1) * sizeof(ParamListInfoData));

!     foreach(l, exprstates)
      {
!         ExprState  *n = lfirst(l);
          bool        isNull;

          paramLI[i].value = ExecEvalExprSwitchContext(n,
--- 223,231 ----
      paramLI = (ParamListInfo)
          palloc0((nargs + 1) * sizeof(ParamListInfoData));

!     forboth(le, exprstates, la, argtypes)
      {
!         ExprState  *n = lfirst(le);
          bool        isNull;

          paramLI[i].value = ExecEvalExprSwitchContext(n,
***************
*** 235,240 ****
--- 235,241 ----
          paramLI[i].kind = PARAM_NUM;
          paramLI[i].id = i + 1;
          paramLI[i].isnull = isNull;
+         paramLI[i].valuetype = lfirst_oid(la);

          i++;
      }
Index: src/backend/commands/schemacmds.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/schemacmds.c,v
retrieving revision 1.20
diff -u -c -r1.20 schemacmds.c
*** src/backend/commands/schemacmds.c    25 Jun 2004 21:55:53 -0000    1.20
--- src/backend/commands/schemacmds.c    26 Jul 2004 11:37:24 -0000
***************
*** 168,174 ****
              /* schemas should contain only utility stmts */
              Assert(querytree->commandType == CMD_UTILITY);
              /* do this step */
!             ProcessUtility(querytree->utilityStmt, None_Receiver, NULL);
              /* make sure later steps can see the object created here */
              CommandCounterIncrement();
          }
--- 168,174 ----
              /* schemas should contain only utility stmts */
              Assert(querytree->commandType == CMD_UTILITY);
              /* do this step */
!             ProcessUtility(querytree->utilityStmt, None_Receiver, NULL, NULL);
              /* make sure later steps can see the object created here */
              CommandCounterIncrement();
          }
Index: src/backend/executor/functions.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/executor/functions.c,v
retrieving revision 1.83
diff -u -c -r1.83 functions.c
*** src/backend/executor/functions.c    15 Jul 2004 13:51:38 -0000    1.83
--- src/backend/executor/functions.c    26 Jul 2004 11:37:24 -0000
***************
*** 61,66 ****
--- 61,67 ----
      Oid            rettype;        /* actual return type */
      int            typlen;            /* length of the return type */
      bool        typbyval;        /* true if return type is pass by value */
+     Oid        *argtypes;       /* resolved types of arguments */
      bool        returnsTuple;    /* true if returning whole tuple result */
      bool        shutdown_reg;    /* true if registered shutdown callback */

***************
*** 77,83 ****
  static execution_state *init_execution_state(List *queryTree_list);
  static void init_sql_fcache(FmgrInfo *finfo);
  static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
! static TupleTableSlot *postquel_getnext(execution_state *es);
  static void postquel_end(execution_state *es);
  static void postquel_sub_params(SQLFunctionCachePtr fcache,
                      FunctionCallInfo fcinfo);
--- 78,84 ----
  static execution_state *init_execution_state(List *queryTree_list);
  static void init_sql_fcache(FmgrInfo *finfo);
  static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
! static TupleTableSlot *postquel_getnext(execution_state *es, ParamListInfo params);
  static void postquel_end(execution_state *es);
  static void postquel_sub_params(SQLFunctionCachePtr fcache,
                      FunctionCallInfo fcinfo);
***************
*** 224,229 ****
--- 225,236 ----
      else
          argOidVect = NULL;

+
+     /*
+      * remember computed arg types so we can set them on the ParamListInfo lists we generate.
+      */
+     fcache->argtypes = argOidVect;
+
      tmp = SysCacheGetAttr(PROCOID,
                            procedureTuple,
                            Anum_pg_proc_prosrc,
***************
*** 277,289 ****
  }

  static TupleTableSlot *
! postquel_getnext(execution_state *es)
  {
      long        count;

      if (es->qd->operation == CMD_UTILITY)
      {
!         ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest, NULL);
          return NULL;
      }

--- 284,296 ----
  }

  static TupleTableSlot *
! postquel_getnext(execution_state *es, ParamListInfo params)
  {
      long        count;

      if (es->qd->operation == CMD_UTILITY)
      {
!         ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest, NULL, params);
          return NULL;
      }

***************
*** 334,339 ****
--- 341,347 ----
              paramLI[i].id = i + 1;
              paramLI[i].value = fcinfo->arg[i];
              paramLI[i].isnull = fcinfo->argnull[i];
+             paramLI[i].valuetype = fcache->argtypes[i];
          }
          paramLI[nargs].kind = PARAM_INVALID;
      }
***************
*** 357,363 ****
      if (es->status == F_EXEC_START)
          postquel_start(es, fcache);

!     slot = postquel_getnext(es);

      if (TupIsNull(slot))
      {
--- 365,371 ----
      if (es->status == F_EXEC_START)
          postquel_start(es, fcache);

!     slot = postquel_getnext(es, fcache->paramLI);

      if (TupIsNull(slot))
      {
Index: src/backend/executor/spi.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/executor/spi.c,v
retrieving revision 1.120
diff -u -c -r1.120 spi.c
*** src/backend/executor/spi.c    1 Jul 2004 21:17:13 -0000    1.120
--- src/backend/executor/spi.c    26 Jul 2004 11:37:24 -0000
***************
*** 821,826 ****
--- 821,827 ----
              paramLI[k].kind = PARAM_NUM;
              paramLI[k].id = k + 1;
              paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
+             paramLI[k].valuetype = spiplan->argtypes[k];
              if (paramLI[k].isnull)
              {
                  /* nulls just copy */
***************
*** 1184,1190 ****
                  res = SPI_OK_UTILITY;
                  if (plan == NULL)
                  {
!                     ProcessUtility(queryTree->utilityStmt, dest, NULL);

                      if (IsA(queryTree->utilityStmt, TransactionStmt))
                      {
--- 1185,1191 ----
                  res = SPI_OK_UTILITY;
                  if (plan == NULL)
                  {
!                     ProcessUtility(queryTree->utilityStmt, dest, NULL, NULL);

                      if (IsA(queryTree->utilityStmt, TransactionStmt))
                      {
***************
*** 1306,1312 ****
              dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
              if (queryTree->commandType == CMD_UTILITY)
              {
!                 ProcessUtility(queryTree->utilityStmt, dest, NULL);
                  res = SPI_OK_UTILITY;

                  if (IsA(queryTree->utilityStmt, TransactionStmt))
--- 1307,1313 ----
              dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
              if (queryTree->commandType == CMD_UTILITY)
              {
!                 ProcessUtility(queryTree->utilityStmt, dest, NULL, paramLI);
                  res = SPI_OK_UTILITY;

                  if (IsA(queryTree->utilityStmt, TransactionStmt))
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.288
diff -u -c -r1.288 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    12 Jul 2004 05:37:21 -0000    1.288
--- src/backend/nodes/copyfuncs.c    26 Jul 2004 11:37:25 -0000
***************
*** 25,31 ****
--- 25,33 ----
  #include "nodes/parsenodes.h"
  #include "nodes/plannodes.h"
  #include "nodes/relation.h"
+ #include "nodes/params.h"
  #include "utils/datum.h"
+ #include "utils/lsyscache.h"


  /*
***************
*** 3094,3096 ****
--- 3096,3147 ----

      return retval;
  }
+
+ /*
+  * ParamListInfo copy function.
+  *
+  * ParamListInfo isn't a Node, but since the header lives in nodes/param.h
+  * this seems the best place.
+  */
+ ParamListInfo
+ copyParamList(ParamListInfo from)
+ {
+     int i, size;
+     ParamListInfo retval;
+
+     if (from == NULL)
+         return NULL;
+
+     size = 0;
+     while (from[size].kind != PARAM_INVALID)
+         ++size;
+
+     retval = (ParamListInfo) palloc0((size + 1) * sizeof(ParamListInfoData));
+
+     for (i = 0; i < size; ++i) {
+         /* copy metadata */
+         retval[i].kind = from[i].kind;
+         retval[i].id = from[i].id;
+         if (from[i].kind == PARAM_NAMED)
+             retval[i].name = pstrdup(from[i].name);
+
+         /* copy value */
+         retval[i].isnull = from[i].isnull;
+         retval[i].valuetype = from[i].valuetype;
+         if (from[i].isnull)
+         {
+             retval[i].value = from[i].value; /* nulls just copy */
+         }
+         else
+         {
+             int16 typLen;
+             bool  typByVal;
+             get_typlenbyval(from[i].valuetype, &typLen, &typByVal);
+             retval[i].value = datumCopy(from[i].value, typByVal, typLen);
+         }
+     }
+
+     retval[size].kind = PARAM_INVALID;
+     return retval;
+ }
+
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/tcop/postgres.c,v
retrieving revision 1.424
diff -u -c -r1.424 postgres.c
*** src/backend/tcop/postgres.c    17 Jul 2004 03:29:00 -0000    1.424
--- src/backend/tcop/postgres.c    26 Jul 2004 11:37:27 -0000
***************
*** 1496,1501 ****
--- 1496,1502 ----
              params[i].kind = PARAM_NUM;
              params[i].id = i + 1;
              params[i].isnull = isNull;
+             params[i].valuetype = ptype;

              i++;
          }
Index: src/backend/tcop/pquery.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/tcop/pquery.c,v
retrieving revision 1.81
diff -u -c -r1.81 pquery.c
*** src/backend/tcop/pquery.c    17 Jul 2004 03:29:00 -0000    1.81
--- src/backend/tcop/pquery.c    26 Jul 2004 11:37:27 -0000
***************
*** 800,813 ****
      if (query->canSetTag)
      {
          /* utility statement can override default tag string */
!         ProcessUtility(utilityStmt, dest, completionTag);
          if (completionTag && completionTag[0] == '\0' && portal->commandTag)
              strcpy(completionTag, portal->commandTag);    /* use the default */
      }
      else
      {
          /* utility added by rewrite cannot set tag */
!         ProcessUtility(utilityStmt, dest, NULL);
      }

      /* Some utility statements may change context on us */
--- 800,813 ----
      if (query->canSetTag)
      {
          /* utility statement can override default tag string */
!         ProcessUtility(utilityStmt, dest, completionTag, portal->portalParams);
          if (completionTag && completionTag[0] == '\0' && portal->commandTag)
              strcpy(completionTag, portal->commandTag);    /* use the default */
      }
      else
      {
          /* utility added by rewrite cannot set tag */
!         ProcessUtility(utilityStmt, dest, NULL, portal->portalParams);
      }

      /* Some utility statements may change context on us */
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/tcop/utility.c,v
retrieving revision 1.220
diff -u -c -r1.220 utility.c
*** src/backend/tcop/utility.c    25 Jun 2004 21:55:57 -0000    1.220
--- src/backend/tcop/utility.c    26 Jul 2004 11:37:28 -0000
***************
*** 297,302 ****
--- 297,303 ----
   *    dest: where to send results
   *    completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
   *        in which to store a command completion status string.
+  *  params: parameters to use during execution (currently only used by DECLARE)
   *
   * completionTag is only set nonempty if we want to return a nondefault
   * status (currently, only used for MOVE/FETCH).
***************
*** 306,312 ****
  void
  ProcessUtility(Node *parsetree,
                 DestReceiver *dest,
!                char *completionTag)
  {
      check_xact_readonly(parsetree);

--- 307,314 ----
  void
  ProcessUtility(Node *parsetree,
                 DestReceiver *dest,
!                char *completionTag,
!                ParamListInfo params)
  {
      check_xact_readonly(parsetree);

***************
*** 368,374 ****
               * Portal (cursor) manipulation
               */
          case T_DeclareCursorStmt:
!             PerformCursorOpen((DeclareCursorStmt *) parsetree);
              break;

          case T_ClosePortalStmt:
--- 370,376 ----
               * Portal (cursor) manipulation
               */
          case T_DeclareCursorStmt:
!             PerformCursorOpen((DeclareCursorStmt *) parsetree, params);
              break;

          case T_ClosePortalStmt:
Index: src/include/commands/portalcmds.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/commands/portalcmds.h,v
retrieving revision 1.15
diff -u -c -r1.15 portalcmds.h
*** src/include/commands/portalcmds.h    17 Jul 2004 03:30:56 -0000    1.15
--- src/include/commands/portalcmds.h    26 Jul 2004 11:37:29 -0000
***************
*** 15,23 ****
  #define PORTALCMDS_H

  #include "utils/portal.h"


! extern void PerformCursorOpen(DeclareCursorStmt *stmt);

  extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
                     char *completionTag);
--- 15,24 ----
  #define PORTALCMDS_H

  #include "utils/portal.h"
+ #include "nodes/params.h"


! extern void PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params);

  extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
                     char *completionTag);
Index: src/include/nodes/params.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/nodes/params.h,v
retrieving revision 1.24
diff -u -c -r1.24 params.h
*** src/include/nodes/params.h    29 Nov 2003 22:41:06 -0000    1.24
--- src/include/nodes/params.h    26 Jul 2004 11:37:29 -0000
***************
*** 58,63 ****
--- 58,64 ----
   *        name   : the parameter name (valid if kind == PARAM_NAMED)
   *        id       : the parameter id (valid if kind == PARAM_NUM)
   *        isnull : true if the value is null (if so 'value' is undefined)
+  *              valuetype: the type of the parameter value.
   *        value  : the value that has to be substituted in the place
   *                 of the parameter.
   *
***************
*** 73,78 ****
--- 74,80 ----
      char       *name;
      AttrNumber    id;
      bool        isnull;
+         Oid             valuetype;
      Datum        value;
  } ParamListInfoData;

***************
*** 103,106 ****
--- 105,113 ----
      bool        isnull;
  } ParamExecData;

+ /*
+  * Copying of parameter lists; implemented in backend/nodes/copyfuncs.c.
+  */
+ extern ParamListInfo copyParamList(ParamListInfo from);
+
  #endif   /* PARAMS_H */
Index: src/include/tcop/utility.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/tcop/utility.h,v
retrieving revision 1.21
diff -u -c -r1.21 utility.h
*** src/include/tcop/utility.h    29 Nov 2003 22:41:14 -0000    1.21
--- src/include/tcop/utility.h    26 Jul 2004 11:37:30 -0000
***************
*** 15,23 ****
  #define UTILITY_H

  #include "executor/execdesc.h"

  extern void ProcessUtility(Node *parsetree, DestReceiver *dest,
!                char *completionTag);

  extern bool UtilityReturnsTuples(Node *parsetree);

--- 15,24 ----
  #define UTILITY_H

  #include "executor/execdesc.h"
+ #include "nodes/params.h"

  extern void ProcessUtility(Node *parsetree, DestReceiver *dest,
!                char *completionTag, ParamListInfo params);

  extern bool UtilityReturnsTuples(Node *parsetree);

Index: src/test/regress/expected/portals.out
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/test/regress/expected/portals.out,v
retrieving revision 1.7
diff -u -c -r1.7 portals.out
*** src/test/regress/expected/portals.out    24 Aug 2003 21:02:43 -0000    1.7
--- src/test/regress/expected/portals.out    26 Jul 2004 11:37:30 -0000
***************
*** 738,740 ****
--- 738,796 ----
  -- should fail
  FETCH FROM foo26;
  ERROR:  cursor "foo26" does not exist
+ --
+ -- Parameterized DECLARE
+ --
+ BEGIN;
+ CREATE FUNCTION declares_cursor(int4)
+    RETURNS void
+    AS 'DECLARE c CURSOR FOR SELECT * FROM tenk1 WHERE unique1 = $1;'
+    LANGUAGE 'sql';
+ SELECT declares_cursor(42);
+  declares_cursor
+ -----------------
+
+ (1 row)
+
+ FETCH ALL FROM c;
+  unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even
|stringu1 | stringu2 | string4  
+
---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+       42 |    5530 |   0 |    2 |   2 |      2 |      42 |       42 |          42 |        42 |       42 |  84 |   85
|QBAAAA   | SEIAAA   | OOOOxx 
+ (1 row)
+
+ ROLLBACK;
+ --
+ -- Parameterized DECLARE with a non-primitive type
+ --
+ BEGIN;
+ CREATE FUNCTION declares_cursor(text)
+    RETURNS void
+    AS 'DECLARE c CURSOR FOR SELECT * FROM tenk1 WHERE stringu1 LIKE $1;'
+    LANGUAGE 'sql';
+ SELECT declares_cursor('AB%');
+  declares_cursor
+ -----------------
+
+ (1 row)
+
+ FETCH ALL FROM c;
+  unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even
|stringu1 | stringu2 | string4  
+
---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+     1378 |     604 |   0 |    2 |   8 |     18 |      78 |      378 |        1378 |      1378 |     1378 | 156 |  157
|ABAAAA   | GXAAAA   | AAAAxx 
+     4758 |     758 |   0 |    2 |   8 |     18 |      58 |      758 |         758 |      4758 |     4758 | 116 |  117
|ABAAAA   | EDBAAA   | OOOOxx 
+     2054 |     778 |   0 |    2 |   4 |     14 |      54 |       54 |          54 |      2054 |     2054 | 108 |  109
|ABAAAA   | YDBAAA   | OOOOxx 
+     8138 |    1871 |   0 |    2 |   8 |     18 |      38 |      138 |         138 |      3138 |     8138 |  76 |   77
|ABAAAA   | ZTCAAA   | VVVVxx 
+     6786 |    2206 |   0 |    2 |   6 |      6 |      86 |      786 |         786 |      1786 |     6786 | 172 |  173
|ABAAAA   | WGDAAA   | OOOOxx 
+      702 |    2433 |   0 |    2 |   2 |      2 |       2 |      702 |         702 |       702 |      702 |   4 |    5
|ABAAAA   | PPDAAA   | HHHHxx 
+     7462 |    2529 |   0 |    2 |   2 |      2 |      62 |      462 |        1462 |      2462 |     7462 | 124 |  125
|ABAAAA   | HTDAAA   | HHHHxx 
+     8814 |    3033 |   0 |    2 |   4 |     14 |      14 |      814 |         814 |      3814 |     8814 |  28 |   29
|ABAAAA   | RMEAAA   | HHHHxx 
+     4082 |    3403 |   0 |    2 |   2 |      2 |      82 |       82 |          82 |      4082 |     4082 | 164 |  165
|ABAAAA   | XAFAAA   | VVVVxx 
+     6110 |    4039 |   0 |    2 |   0 |     10 |      10 |      110 |         110 |      1110 |     6110 |  20 |   21
|ABAAAA   | JZFAAA   | VVVVxx 
+       26 |    8152 |   0 |    2 |   6 |      6 |      26 |       26 |          26 |        26 |       26 |  52 |   53
|ABAAAA   | OBMAAA   | AAAAxx 
+     5434 |    8243 |   0 |    2 |   4 |     14 |      34 |      434 |        1434 |       434 |     5434 |  68 |   69
|ABAAAA   | BFMAAA   | VVVVxx 
+     3406 |    8275 |   0 |    2 |   6 |      6 |       6 |      406 |        1406 |      3406 |     3406 |  12 |   13
|ABAAAA   | HGMAAA   | VVVVxx 
+     2730 |    8511 |   0 |    2 |   0 |     10 |      30 |      730 |         730 |      2730 |     2730 |  60 |   61
|ABAAAA   | JPMAAA   | VVVVxx 
+     9490 |    8928 |   0 |    2 |   0 |     10 |      90 |      490 |        1490 |      4490 |     9490 | 180 |  181
|ABAAAA   | KFNAAA   | AAAAxx 
+ (15 rows)
+
+ ROLLBACK;
Index: src/test/regress/sql/portals.sql
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/test/regress/sql/portals.sql,v
retrieving revision 1.4
diff -u -c -r1.4 portals.sql
*** src/test/regress/sql/portals.sql    29 Apr 2003 03:21:30 -0000    1.4
--- src/test/regress/sql/portals.sql    26 Jul 2004 11:37:30 -0000
***************
*** 218,220 ****
--- 218,256 ----

  -- should fail
  FETCH FROM foo26;
+
+ --
+ -- Parameterized DECLARE
+ --
+
+ BEGIN;
+
+ CREATE FUNCTION declares_cursor(int4)
+    RETURNS void
+    AS 'DECLARE c CURSOR FOR SELECT * FROM tenk1 WHERE unique1 = $1;'
+    LANGUAGE 'sql';
+
+ SELECT declares_cursor(42);
+
+ FETCH ALL FROM c;
+
+ ROLLBACK;
+
+ --
+ -- Parameterized DECLARE with a non-primitive type
+ --
+
+ BEGIN;
+
+ CREATE FUNCTION declares_cursor(text)
+    RETURNS void
+    AS 'DECLARE c CURSOR FOR SELECT * FROM tenk1 WHERE stringu1 LIKE $1;'
+    LANGUAGE 'sql';
+
+ SELECT declares_cursor('AB%');
+
+ FETCH ALL FROM c;
+
+ ROLLBACK;
+
+

pgsql-patches by date:

Previous
From: "Dave Page"
Date:
Subject: Re: [HACKERS] Function to kill backend
Next
From: Andreas Pflug
Date:
Subject: Re: [HACKERS] Function to kill backend