Allow DECLARE CURSOR to take parameters from the portal in which it is

executed.  Previously, the DECLARE would succeed but subsequent FETCHes
would fail since the parameter values supplied to DECLARE were not
propagated to the portal created for the cursor.
In support of this, add type Oids to ParamListInfo entries, which seems
like a good idea anyway since code that extracts a value can double-check
that it got the type of value it was expecting.
Oliver Jowett, with minor editorialization by Tom Lane.
This commit is contained in:
Tom Lane 2004-08-02 01:30:51 +00:00
parent 410b1dfb88
commit f622c54049
18 changed files with 262 additions and 114 deletions

View File

@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.30 2004/07/31 00:45:31 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.31 2004/08/02 01:30:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -36,7 +36,7 @@
* Execute SQL DECLARE CURSOR command.
*/
void
PerformCursorOpen(DeclareCursorStmt *stmt)
PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
{
List *rewritten;
Query *query;
@ -104,6 +104,17 @@ PerformCursorOpen(DeclareCursorStmt *stmt)
list_make1(plan),
PortalGetHeapMemory(portal));
/*
* Also copy the outer portal's parameter list into the inner portal's
* memory context. We want to pass down the parameter values in case
* we had a command like
* DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
* This will have been parsed using the outer parameter set and the
* parameter value needs to be preserved for use when the cursor is
* executed.
*/
params = copyParamList(params);
MemoryContextSwitchTo(oldContext);
/*
@ -123,9 +134,9 @@ PerformCursorOpen(DeclareCursorStmt *stmt)
}
/*
* Start execution --- never any params for a cursor.
* Start execution, inserting parameters if any.
*/
PortalStart(portal, NULL);
PortalStart(portal, params);
Assert(portal->strategy == PORTAL_ONE_SELECT);

View File

@ -10,7 +10,7 @@
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.28 2004/06/11 01:08:38 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.29 2004/08/02 01:30:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -200,7 +200,7 @@ ExecuteQuery(ExecuteStmt *stmt, DestReceiver *dest, char *completionTag)
/*
* Evaluates a list of parameters, using the given executor state. It
* requires a list of the parameter values themselves, and a list of
* requires a list of the parameter expressions themselves, and a list of
* their types. It returns a filled-in ParamListInfo -- this can later
* be passed to CreateQueryDesc(), which allows the executor to make use
* of the parameters during query execution.
@ -211,7 +211,7 @@ EvaluateParams(EState *estate, List *params, List *argtypes)
int nargs = list_length(argtypes);
ParamListInfo paramLI;
List *exprstates;
ListCell *l;
ListCell *le, *la;
int i = 0;
/* Parser should have caught this error, but check for safety */
@ -223,9 +223,9 @@ EvaluateParams(EState *estate, List *params, List *argtypes)
paramLI = (ParamListInfo)
palloc0((nargs + 1) * sizeof(ParamListInfoData));
foreach(l, exprstates)
forboth(le, exprstates, la, argtypes)
{
ExprState *n = lfirst(l);
ExprState *n = lfirst(le);
bool isNull;
paramLI[i].value = ExecEvalExprSwitchContext(n,
@ -234,6 +234,7 @@ EvaluateParams(EState *estate, List *params, List *argtypes)
NULL);
paramLI[i].kind = PARAM_NUM;
paramLI[i].id = i + 1;
paramLI[i].ptype = lfirst_oid(la);
paramLI[i].isnull = isNull;
i++;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.21 2004/08/01 20:30:48 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.22 2004/08/02 01:30:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -168,7 +168,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
/* schemas should contain only utility stmts */
Assert(querytree->commandType == CMD_UTILITY);
/* do this step */
ProcessUtility(querytree->utilityStmt, None_Receiver, NULL);
ProcessUtility(querytree->utilityStmt, NULL, None_Receiver, NULL);
/* make sure later steps can see the object created here */
CommandCounterIncrement();
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.164 2004/06/09 19:08:14 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.165 2004/08/02 01:30:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -589,56 +589,18 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
else
{
/*
* All other parameter types must be sought in
* ecxt_param_list_info. NOTE: The last entry in the param array
* is always an entry with kind == PARAM_INVALID.
* All other parameter types must be sought in ecxt_param_list_info.
*/
ParamListInfo paramList = econtext->ecxt_param_list_info;
char *thisParamName = expression->paramname;
bool matchFound = false;
ParamListInfo paramInfo;
if (paramList != NULL)
{
while (paramList->kind != PARAM_INVALID && !matchFound)
{
if (thisParamKind == paramList->kind)
{
switch (thisParamKind)
{
case PARAM_NAMED:
if (strcmp(paramList->name, thisParamName) == 0)
matchFound = true;
break;
case PARAM_NUM:
if (paramList->id == thisParamId)
matchFound = true;
break;
default:
elog(ERROR, "unrecognized paramkind: %d",
thisParamKind);
}
}
if (!matchFound)
paramList++;
} /* while */
} /* if */
if (!matchFound)
{
if (thisParamKind == PARAM_NAMED)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("no value found for parameter \"%s\"",
thisParamName)));
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("no value found for parameter %d",
thisParamId)));
}
*isNull = paramList->isnull;
return paramList->value;
paramInfo = lookupParam(econtext->ecxt_param_list_info,
thisParamKind,
expression->paramname,
thisParamId,
false);
Assert(paramInfo->ptype == expression->paramtype);
*isNull = paramInfo->isnull;
return paramInfo->value;
}
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.83 2004/07/15 13:51:38 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.84 2004/08/02 01:30:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -58,6 +58,7 @@ typedef struct local_es
*/
typedef struct
{
Oid *argtypes; /* resolved types of arguments */
Oid rettype; /* actual return type */
int typlen; /* length of the return type */
bool typbyval; /* true if return type is pass by value */
@ -223,6 +224,7 @@ init_sql_fcache(FmgrInfo *finfo)
}
else
argOidVect = NULL;
fcache->argtypes = argOidVect;
tmp = SysCacheGetAttr(PROCOID,
procedureTuple,
@ -283,7 +285,8 @@ postquel_getnext(execution_state *es)
if (es->qd->operation == CMD_UTILITY)
{
ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest, NULL);
ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->params,
es->qd->dest, NULL);
return NULL;
}
@ -332,6 +335,7 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
{
paramLI[i].kind = PARAM_NUM;
paramLI[i].id = i + 1;
paramLI[i].ptype = fcache->argtypes[i];
paramLI[i].value = fcinfo->arg[i];
paramLI[i].isnull = fcinfo->argnull[i];
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.122 2004/07/31 20:55:41 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.123 2004/08/02 01:30:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -820,6 +820,7 @@ SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
{
paramLI[k].kind = PARAM_NUM;
paramLI[k].id = k + 1;
paramLI[k].ptype = spiplan->argtypes[k];
paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
if (paramLI[k].isnull)
{
@ -1251,7 +1252,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
res = SPI_OK_UTILITY;
if (plan == NULL)
{
ProcessUtility(queryTree->utilityStmt, dest, NULL);
ProcessUtility(queryTree->utilityStmt, NULL, dest, NULL);
CommandCounterIncrement();
}
}
@ -1319,6 +1320,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
{
paramLI[k].kind = PARAM_NUM;
paramLI[k].id = k + 1;
paramLI[k].ptype = plan->argtypes[k];
paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
paramLI[k].value = Values[k];
}
@ -1366,7 +1368,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
if (queryTree->commandType == CMD_UTILITY)
{
ProcessUtility(queryTree->utilityStmt, dest, NULL);
ProcessUtility(queryTree->utilityStmt, paramLI, dest, NULL);
res = SPI_OK_UTILITY;
CommandCounterIncrement();
}

View File

@ -4,7 +4,7 @@
# Makefile for backend/nodes
#
# IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/nodes/Makefile,v 1.16 2004/01/07 18:43:36 neilc Exp $
# $PostgreSQL: pgsql/src/backend/nodes/Makefile,v 1.17 2004/08/02 01:30:42 tgl Exp $
#
#-------------------------------------------------------------------------
@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = nodeFuncs.o nodes.o list.o bitmapset.o \
copyfuncs.o equalfuncs.o makefuncs.o \
outfuncs.o readfuncs.o print.o read.o value.o
outfuncs.o readfuncs.o print.o read.o params.o value.o
all: SUBSYS.o

122
src/backend/nodes/params.c Normal file
View File

@ -0,0 +1,122 @@
/*-------------------------------------------------------------------------
*
* params.c
* Support functions for plan parameter lists.
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.1 2004/08/02 01:30:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/params.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
/*
* Copy a ParamList.
*
* The result is allocated in CurrentMemoryContext.
*/
ParamListInfo
copyParamList(ParamListInfo from)
{
ParamListInfo retval;
int i, size;
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;
if (from[i].kind == PARAM_NAMED)
retval[i].name = pstrdup(from[i].name);
retval[i].id = from[i].id;
retval[i].ptype = from[i].ptype;
/* copy value */
retval[i].isnull = from[i].isnull;
if (from[i].isnull)
{
retval[i].value = from[i].value; /* nulls just copy */
}
else
{
int16 typLen;
bool typByVal;
get_typlenbyval(from[i].ptype, &typLen, &typByVal);
retval[i].value = datumCopy(from[i].value, typByVal, typLen);
}
}
retval[size].kind = PARAM_INVALID;
return retval;
}
/*
* Search a ParamList for a given parameter.
*
* On success, returns a pointer to the parameter's entry.
* On failure, returns NULL if noError is true, else ereports the error.
*/
ParamListInfo
lookupParam(ParamListInfo paramList, int thisParamKind,
const char *thisParamName, AttrNumber thisParamId,
bool noError)
{
if (paramList != NULL)
{
while (paramList->kind != PARAM_INVALID)
{
if (thisParamKind == paramList->kind)
{
switch (thisParamKind)
{
case PARAM_NAMED:
if (strcmp(paramList->name, thisParamName) == 0)
return paramList;
break;
case PARAM_NUM:
if (paramList->id == thisParamId)
return paramList;
break;
default:
elog(ERROR, "unrecognized paramkind: %d",
thisParamKind);
}
}
paramList++;
}
}
if (!noError)
{
if (thisParamKind == PARAM_NAMED)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("no value found for parameter \"%s\"",
thisParamName)));
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("no value found for parameter %d",
thisParamId)));
}
return NULL;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.176 2004/06/11 01:08:54 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.177 2004/08/02 01:30:43 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -1120,39 +1120,20 @@ eval_const_expressions_mutator(Node *node,
if (IsA(node, Param))
{
Param *param = (Param *) node;
int thisParamKind = param->paramkind;
/* OK to try to substitute value? */
if (context->estimate && thisParamKind != PARAM_EXEC &&
if (context->estimate && param->paramkind != PARAM_EXEC &&
PlannerBoundParamList != NULL)
{
ParamListInfo paramList = PlannerBoundParamList;
bool matchFound = false;
ParamListInfo paramInfo;
/* Search to see if we've been given a value for this Param */
while (paramList->kind != PARAM_INVALID && !matchFound)
{
if (thisParamKind == paramList->kind)
{
switch (thisParamKind)
{
case PARAM_NAMED:
if (strcmp(paramList->name, param->paramname) == 0)
matchFound = true;
break;
case PARAM_NUM:
if (paramList->id == param->paramid)
matchFound = true;
break;
default:
elog(ERROR, "unrecognized paramkind: %d",
thisParamKind);
}
}
if (!matchFound)
paramList++;
}
if (matchFound)
paramInfo = lookupParam(PlannerBoundParamList,
param->paramkind,
param->paramname,
param->paramid,
true);
if (paramInfo)
{
/*
* Found it, so return a Const representing the param value.
@ -1164,11 +1145,12 @@ eval_const_expressions_mutator(Node *node,
int16 typLen;
bool typByVal;
Assert(paramInfo->ptype == param->paramtype);
get_typlenbyval(param->paramtype, &typLen, &typByVal);
return (Node *) makeConst(param->paramtype,
(int) typLen,
paramList->value,
paramList->isnull,
paramInfo->value,
paramInfo->isnull,
typByVal);
}
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.427 2004/07/31 00:45:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.428 2004/08/02 01:30:44 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@ -1488,6 +1488,7 @@ exec_bind_message(StringInfo input_message)
params[i].kind = PARAM_NUM;
params[i].id = i + 1;
params[i].ptype = ptype;
params[i].isnull = isNull;
i++;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.82 2004/07/31 00:45:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.83 2004/08/02 01:30:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -833,14 +833,14 @@ PortalRunUtility(Portal portal, Query *query,
if (query->canSetTag)
{
/* utility statement can override default tag string */
ProcessUtility(utilityStmt, dest, completionTag);
ProcessUtility(utilityStmt, portal->portalParams, 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);
ProcessUtility(utilityStmt, portal->portalParams, dest, NULL);
}
/* Some utility statements may change context on us */

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.222 2004/08/01 17:32:18 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.223 2004/08/02 01:30:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -294,6 +294,7 @@ check_xact_readonly(Node *parsetree)
* general utility function invoker
*
* parsetree: the parse tree for the utility statement
* params: parameters to use during execution (currently only used by DECLARE)
* dest: where to send results
* completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
* in which to store a command completion status string.
@ -305,6 +306,7 @@ check_xact_readonly(Node *parsetree)
*/
void
ProcessUtility(Node *parsetree,
ParamListInfo params,
DestReceiver *dest,
char *completionTag)
{
@ -406,7 +408,7 @@ ProcessUtility(Node *parsetree,
* Portal (cursor) manipulation
*/
case T_DeclareCursorStmt:
PerformCursorOpen((DeclareCursorStmt *) parsetree);
PerformCursorOpen((DeclareCursorStmt *) parsetree, params);
break;
case T_ClosePortalStmt:

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.15 2004/07/17 03:30:56 tgl Exp $
* $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.16 2004/08/02 01:30:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -17,7 +17,7 @@
#include "utils/portal.h"
extern void PerformCursorOpen(DeclareCursorStmt *stmt);
extern void PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params);
extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
char *completionTag);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.24 2003/11/29 22:41:06 pgsql Exp $
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.25 2004/08/02 01:30:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -56,7 +56,8 @@
*
* kind : the kind of parameter (PARAM_NAMED or PARAM_NUM)
* name : the parameter name (valid if kind == PARAM_NAMED)
* id : the parameter id (valid if kind == PARAM_NUM)
* id : the parameter id (valid if kind == PARAM_NUM)
* ptype : the type of the parameter value
* isnull : true if the value is null (if so 'value' is undefined)
* value : the value that has to be substituted in the place
* of the parameter.
@ -72,6 +73,7 @@ typedef struct ParamListInfoData
int kind;
char *name;
AttrNumber id;
Oid ptype;
bool isnull;
Datum value;
} ParamListInfoData;
@ -103,4 +105,11 @@ typedef struct ParamExecData
bool isnull;
} ParamExecData;
/* Functions found in src/backend/nodes/params.c */
extern ParamListInfo copyParamList(ParamListInfo from);
extern ParamListInfo lookupParam(ParamListInfo paramList, int thisParamKind,
const char *thisParamName, AttrNumber thisParamId,
bool noError);
#endif /* PARAMS_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.21 2003/11/29 22:41:14 pgsql Exp $
* $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.22 2004/08/02 01:30:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -16,8 +16,9 @@
#include "executor/execdesc.h"
extern void ProcessUtility(Node *parsetree, DestReceiver *dest,
char *completionTag);
extern void ProcessUtility(Node *parsetree, ParamListInfo params,
DestReceiver *dest, char *completionTag);
extern bool UtilityReturnsTuples(Node *parsetree);

View File

@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.112 2004/08/01 17:32:21 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.113 2004/08/02 01:30:49 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -3582,7 +3582,7 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
* need to have more than one active param list.
*/
paramLI = (ParamListInfo)
MemoryContextAlloc(econtext->ecxt_per_tuple_memory,
MemoryContextAllocZero(econtext->ecxt_per_tuple_memory,
(expr->nparams + 1) * sizeof(ParamListInfoData));
/*
@ -3591,12 +3591,11 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
for (i = 0; i < expr->nparams; i++)
{
PLpgSQL_datum *datum = estate->datums[expr->params[i]];
Oid paramtypeid;
paramLI[i].kind = PARAM_NUM;
paramLI[i].id = i + 1;
exec_eval_datum(estate, datum, expr->plan_argtypes[i],
&paramtypeid,
&paramLI[i].ptype,
&paramLI[i].value, &paramLI[i].isnull);
}
paramLI[i].kind = PARAM_INVALID;

View File

@ -738,3 +738,38 @@ ROLLBACK;
-- should fail
FETCH FROM foo26;
ERROR: cursor "foo26" does not exist
--
-- Parameterized DECLARE needs to insert param values into the cursor portal
--
BEGIN;
CREATE FUNCTION declares_cursor(text)
RETURNS void
AS 'DECLARE c CURSOR FOR SELECT stringu1 FROM tenk1 WHERE stringu1 LIKE $1;'
LANGUAGE 'sql';
SELECT declares_cursor('AB%');
declares_cursor
-----------------
(1 row)
FETCH ALL FROM c;
stringu1
----------
ABAAAA
ABAAAA
ABAAAA
ABAAAA
ABAAAA
ABAAAA
ABAAAA
ABAAAA
ABAAAA
ABAAAA
ABAAAA
ABAAAA
ABAAAA
ABAAAA
ABAAAA
(15 rows)
ROLLBACK;

View File

@ -218,3 +218,20 @@ ROLLBACK;
-- should fail
FETCH FROM foo26;
--
-- Parameterized DECLARE needs to insert param values into the cursor portal
--
BEGIN;
CREATE FUNCTION declares_cursor(text)
RETURNS void
AS 'DECLARE c CURSOR FOR SELECT stringu1 FROM tenk1 WHERE stringu1 LIKE $1;'
LANGUAGE 'sql';
SELECT declares_cursor('AB%');
FETCH ALL FROM c;
ROLLBACK;