aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/commands/command.c8
-rw-r--r--src/backend/executor/spi.c321
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/pg_type.h7
-rw-r--r--src/include/executor/spi.h10
-rw-r--r--src/include/executor/spi_priv.h3
-rw-r--r--src/include/utils/portal.h4
-rw-r--r--src/pl/plpgsql/src/gram.y408
-rw-r--r--src/pl/plpgsql/src/pl_comp.c15
-rw-r--r--src/pl/plpgsql/src/pl_exec.c704
-rw-r--r--src/pl/plpgsql/src/pl_funcs.c93
-rw-r--r--src/pl/plpgsql/src/plpgsql.h44
-rw-r--r--src/pl/plpgsql/src/scan.l7
13 files changed, 1514 insertions, 114 deletions
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index cd7e1d29524..bc5153b8005 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.127 2001/05/09 21:10:38 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.128 2001/05/21 14:22:11 wieck Exp $
*
* NOTES
* The PerformAddAttribute() code, like most of the relation
@@ -108,6 +108,7 @@ PerformPortalFetch(char *name,
QueryDesc *queryDesc;
EState *estate;
MemoryContext oldcontext;
+ bool faked_desc = false;
/*
* sanity checks
@@ -143,13 +144,14 @@ PerformPortalFetch(char *name,
queryDesc = PortalGetQueryDesc(portal);
estate = PortalGetState(portal);
- if (dest == None) /* MOVE */
+ if (dest != queryDesc->dest) /* MOVE */
{
QueryDesc *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc));
memcpy(qdesc, queryDesc, sizeof(QueryDesc));
qdesc->dest = dest;
queryDesc = qdesc;
+ faked_desc = true;
}
BeginCommand(name,
@@ -197,7 +199,7 @@ PerformPortalFetch(char *name,
/*
* Clean up and switch back to old context.
*/
- if (dest == None) /* MOVE */
+ if (faked_desc) /* MOVE */
pfree(queryDesc);
MemoryContextSwitchTo(oldcontext);
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 4aa8c475c30..d3698277427 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -3,12 +3,13 @@
* spi.c
* Server Programming Interface
*
- * $Id: spi.c,v 1.53 2001/03/22 03:59:29 momjian Exp $
+ * $Id: spi.c,v 1.54 2001/05/21 14:22:17 wieck Exp $
*
*-------------------------------------------------------------------------
*/
#include "executor/spi_priv.h"
#include "access/printtup.h"
+#include "commands/command.h"
uint32 SPI_processed = 0;
Oid SPI_lastoid = InvalidOid;
@@ -26,6 +27,9 @@ static int _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount);
static int _SPI_execute_plan(_SPI_plan *plan,
Datum *Values, char *Nulls, int tcount);
+static void _SPI_cursor_operation(Portal portal, bool forward, int count,
+ CommandDest dest);
+
static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
static int _SPI_begin_call(bool execmem);
@@ -272,6 +276,18 @@ SPI_saveplan(void *plan)
}
+int
+SPI_freeplan(void *plan)
+{
+ _SPI_plan *spiplan = (_SPI_plan *)plan;
+
+ if (plan == NULL)
+ return SPI_ERROR_ARGUMENT;
+
+ MemoryContextDelete(spiplan->plancxt);
+ return 0;
+}
+
HeapTuple
SPI_copytuple(HeapTuple tuple)
{
@@ -555,6 +571,181 @@ SPI_freetuple(HeapTuple tuple)
heap_freetuple(tuple);
}
+void
+SPI_freetuptable(SPITupleTable *tuptable)
+{
+ if (tuptable != NULL)
+ MemoryContextDelete(tuptable->tuptabcxt);
+}
+
+
+
+/*
+ * SPI_cursor_open()
+ *
+ * Open a prepared SPI plan as a portal
+ */
+Portal
+SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls)
+{
+ static int unnamed_portal_count = 0;
+
+ _SPI_plan *spiplan = (_SPI_plan *)plan;
+ List *qtlist = spiplan->qtlist;
+ List *ptlist = spiplan->ptlist;
+ Query *queryTree;
+ Plan *planTree;
+ QueryDesc *queryDesc;
+ EState *eState;
+ TupleDesc attinfo;
+ MemoryContext oldcontext;
+ Portal portal;
+ char portalname[64];
+ int k;
+
+ /* Ensure that the plan contains only one regular SELECT query */
+ if (length(ptlist) != 1)
+ elog(ERROR, "cannot open multi-query plan as cursor");
+ queryTree = (Query *)lfirst(qtlist);
+ planTree = (Plan *)lfirst(ptlist);
+
+ if (queryTree->commandType != CMD_SELECT)
+ elog(ERROR, "plan in SPI_cursor_open() is not a SELECT");
+ if (queryTree->isPortal)
+ elog(ERROR, "plan in SPI_cursor_open() must NOT be a DECLARE already");
+ else if (queryTree->into != NULL)
+ elog(ERROR, "plan in SPI_cursor_open() must NOT be a SELECT INTO");
+
+ /* Reset SPI result */
+ SPI_processed = 0;
+ SPI_tuptable = NULL;
+ _SPI_current->processed = 0;
+ _SPI_current->tuptable = NULL;
+
+ /* Make up a portal name if none given */
+ if (name == NULL)
+ {
+ for (;;)
+ {
+ unnamed_portal_count++;
+ if (unnamed_portal_count < 0)
+ unnamed_portal_count = 0;
+ sprintf(portalname, "<unnamed cursor %d>", unnamed_portal_count);
+ if (GetPortalByName(portalname) == NULL)
+ break;
+ }
+
+ name = portalname;
+ }
+
+ /* Ensure the portal doesn't exist already */
+ portal = GetPortalByName(name);
+ if (portal != NULL)
+ elog(ERROR, "cursor \"%s\" already in use", name);
+
+ /* Create the portal */
+ portal = CreatePortal(name);
+ if (portal == NULL)
+ elog(ERROR, "failed to create portal \"%s\"", name);
+
+ /* Switch to portals memory and copy the parsetree and plan to there */
+ oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+ queryTree = copyObject(queryTree);
+ planTree = copyObject(planTree);
+
+ /* Modify the parsetree to be a cursor */
+ queryTree->isPortal = true;
+ queryTree->into = pstrdup(name);
+ queryTree->isBinary = false;
+
+ /* Create the QueryDesc object and the executor state */
+ queryDesc = CreateQueryDesc(queryTree, planTree, SPI);
+ eState = CreateExecutorState();
+
+ /* If the plan has parameters, put them into the executor state */
+ if (spiplan->nargs > 0)
+ {
+ ParamListInfo paramLI = (ParamListInfo) palloc((spiplan->nargs + 1) *
+ sizeof(ParamListInfoData));
+ eState->es_param_list_info = paramLI;
+ for (k = 0; k < spiplan->nargs; paramLI++, k++)
+ {
+ paramLI->kind = PARAM_NUM;
+ paramLI->id = k + 1;
+ paramLI->isnull = (Nulls && Nulls[k] == 'n');
+ paramLI->value = Values[k];
+ }
+ paramLI->kind = PARAM_INVALID;
+ }
+ else
+ eState->es_param_list_info = NULL;
+
+ /* Start the executor */
+ attinfo = ExecutorStart(queryDesc, eState);
+
+ /* Put all the objects into the portal */
+ PortalSetQuery(portal, queryDesc, attinfo, eState, PortalCleanup);
+
+ /* Switch back to the callers memory context */
+ MemoryContextSwitchTo(oldcontext);
+
+ /* Return the created portal */
+ return portal;
+}
+
+
+/*
+ * SPI_cursor_find()
+ *
+ * Find the portal of an existing open cursor
+ */
+Portal
+SPI_cursor_find(char *name)
+{
+ return GetPortalByName(name);
+}
+
+
+/*
+ * SPI_cursor_fetch()
+ *
+ * Fetch rows in a cursor
+ */
+void
+SPI_cursor_fetch(Portal portal, bool forward, int count)
+{
+ _SPI_cursor_operation(portal, forward, count, SPI);
+}
+
+
+/*
+ * SPI_cursor_move()
+ *
+ * Move in a cursor
+ */
+void
+SPI_cursor_move(Portal portal, bool forward, int count)
+{
+ _SPI_cursor_operation(portal, forward, count, None);
+}
+
+
+/*
+ * SPI_cursor_close()
+ *
+ * Close a cursor
+ */
+void
+SPI_cursor_close(Portal portal)
+{
+ Portal my_portal = portal;
+
+ if (!PortalIsValid(my_portal))
+ elog(ERROR, "invalid portal in SPI cursor operation");
+
+ PortalDrop(&my_portal);
+}
+
/* =================== private functions =================== */
/*
@@ -568,6 +759,7 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
{
SPITupleTable *tuptable;
MemoryContext oldcxt;
+ MemoryContext tuptabcxt;
/*
* When called by Executor _SPI_curid expected to be equal to
@@ -583,18 +775,31 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
tuptable = _SPI_current->tuptable;
if (tuptable == NULL)
{
+ tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
+ "SPI TupTable",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ MemoryContextSwitchTo(tuptabcxt);
+
_SPI_current->tuptable = tuptable = (SPITupleTable *)
palloc(sizeof(SPITupleTable));
+ tuptable->tuptabcxt = tuptabcxt;
tuptable->alloced = tuptable->free = 128;
tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
tuptable->tupdesc = CreateTupleDescCopy(tupdesc);
}
- else if (tuptable->free == 0)
+ else
{
- tuptable->free = 256;
- tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
- tuptable->alloced * sizeof(HeapTuple));
+ MemoryContextSwitchTo(tuptable->tuptabcxt);
+
+ if (tuptable->free == 0)
+ {
+ tuptable->free = 256;
+ tuptable->alloced += tuptable->free;
+ tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
+ tuptable->alloced * sizeof(HeapTuple));
+ }
}
tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
@@ -876,6 +1081,86 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
}
+/*
+ * _SPI_cursor_operation()
+ *
+ * Do a FETCH or MOVE in a cursor
+ */
+static void
+_SPI_cursor_operation(Portal portal, bool forward, int count,
+ CommandDest dest)
+{
+ QueryDesc *querydesc;
+ EState *estate;
+ MemoryContext oldcontext;
+ CommandDest olddest;
+
+ /* Check that the portal is valid */
+ if (!PortalIsValid(portal))
+ elog(ERROR, "invalid portal in SPI cursor operation");
+
+ /* Push the SPI stack */
+ _SPI_begin_call(true);
+
+ /* Reset the SPI result */
+ SPI_processed = 0;
+ SPI_tuptable = NULL;
+ _SPI_current->processed = 0;
+ _SPI_current->tuptable = NULL;
+
+ /* Switch to the portals memory context */
+ oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+ querydesc = PortalGetQueryDesc(portal);
+ estate = PortalGetState(portal);
+
+ /* Save the queries command destination and set it to SPI (for fetch) */
+ /* or None (for move) */
+ olddest = querydesc->dest;
+ querydesc->dest = dest;
+
+ /* Run the executor like PerformPortalFetch and remember states */
+ if (forward)
+ {
+ if (!portal->atEnd)
+ {
+ ExecutorRun(querydesc, estate, EXEC_FOR, (long)count);
+ _SPI_current->processed = estate->es_processed;
+ if (estate->es_processed > 0)
+ portal->atStart = false;
+ if (count <= 0 || (int) estate->es_processed < count)
+ portal->atEnd = true;
+ }
+ }
+ else
+ {
+ if (!portal->atStart)
+ {
+ ExecutorRun(querydesc, estate, EXEC_BACK, (long) count);
+ _SPI_current->processed = estate->es_processed;
+ if (estate->es_processed > 0)
+ portal->atEnd = false;
+ if (count <= 0 || estate->es_processed < count)
+ portal->atStart = true;
+ }
+ }
+
+ /* Restore the old command destination and switch back to callers */
+ /* memory context */
+ querydesc->dest = olddest;
+ MemoryContextSwitchTo(oldcontext);
+
+ if (dest == SPI && _SPI_checktuples())
+ elog(FATAL, "SPI_fetch: # of processed tuples check failed");
+
+ /* Put the result into place for access by caller */
+ SPI_processed = _SPI_current->processed;
+ SPI_tuptable = _SPI_current->tuptable;
+
+ /* Pop the SPI stack */
+ _SPI_end_call(true);
+}
+
+
static MemoryContext
_SPI_execmem()
{
@@ -956,14 +1241,33 @@ static _SPI_plan *
_SPI_copy_plan(_SPI_plan *plan, int location)
{
_SPI_plan *newplan;
- MemoryContext oldcxt = NULL;
+ MemoryContext oldcxt;
+ MemoryContext plancxt;
+ MemoryContext parentcxt = CurrentMemoryContext;
+ /* Determine correct parent for the plans memory context */
if (location == _SPI_CPLAN_PROCXT)
+ parentcxt = _SPI_current->procCxt;
+ /*
oldcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
+ */
else if (location == _SPI_CPLAN_TOPCXT)
+ parentcxt = TopMemoryContext;
+ /*
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ */
+ /* Create a memory context for the plan */
+ plancxt = AllocSetContextCreate(parentcxt,
+ "SPI Plan",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ oldcxt = MemoryContextSwitchTo(plancxt);
+
+ /* Copy the SPI plan into it's own context */
newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
+ newplan->plancxt = plancxt;
newplan->qtlist = (List *) copyObject(plan->qtlist);
newplan->ptlist = (List *) copyObject(plan->ptlist);
newplan->nargs = plan->nargs;
@@ -975,8 +1279,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
else
newplan->argtypes = NULL;
- if (oldcxt != NULL)
- MemoryContextSwitchTo(oldcxt);
+ MemoryContextSwitchTo(oldcxt);
return newplan;
}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index e04f97e0ec8..2616b4af7d4 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: catversion.h,v 1.79 2001/05/20 20:28:19 tgl Exp $
+ * $Id: catversion.h,v 1.80 2001/05/21 14:22:17 wieck Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200105191
+#define CATALOG_VERSION_NO 200105211
#endif
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 1cb9d2a5702..5b330d9ea30 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_type.h,v 1.105 2001/05/14 20:30:21 momjian Exp $
+ * $Id: pg_type.h,v 1.106 2001/05/21 14:22:18 wieck Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -413,6 +413,11 @@ DATA(insert OID = 1700 ( numeric PGUID -1 -1 f b t \054 0 0 numeric_in nume
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
+/* OID 1790 */
+DATA(insert OID = 1790 ( refcursor PGUID -1 -1 f b t \054 0 0 textin textout textin textout i x _null_ ));
+DESCR("reference cursor (portal name)");
+#define REFCURSOROID 1790
+
/*
* prototypes for functions in pg_type.c
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index b95eaae4e9e..3a7c7807942 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -41,6 +41,7 @@
typedef struct
{
+ MemoryContext tuptabcxt; /* memory context of result table */
uint32 alloced; /* # of alloced vals */
uint32 free; /* # of free vals */
TupleDesc tupdesc; /* tuple descriptor */
@@ -83,6 +84,7 @@ extern int SPI_exec(char *src, int tcount);
extern int SPI_execp(void *plan, Datum *values, char *Nulls, int tcount);
extern void *SPI_prepare(char *src, int nargs, Oid *argtypes);
extern void *SPI_saveplan(void *plan);
+extern int SPI_freeplan(void *plan);
extern HeapTuple SPI_copytuple(HeapTuple tuple);
extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
@@ -98,6 +100,14 @@ extern void *SPI_palloc(Size size);
extern void *SPI_repalloc(void *pointer, Size size);
extern void SPI_pfree(void *pointer);
extern void SPI_freetuple(HeapTuple pointer);
+extern void SPI_freetuptable(SPITupleTable *tuptable);
+
+extern Portal SPI_cursor_open(char *name, void *plan,
+ Datum *Values, char *Nulls);
+extern Portal SPI_cursor_find(char *name);
+extern void SPI_cursor_fetch(Portal portal, bool forward, int count);
+extern void SPI_cursor_move(Portal portal, bool forward, int count);
+extern void SPI_cursor_close(Portal portal);
extern void AtEOXact_SPI(void);
diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h
index 00b28a58037..e5626634001 100644
--- a/src/include/executor/spi_priv.h
+++ b/src/include/executor/spi_priv.h
@@ -3,7 +3,7 @@
* spi.c
* Server Programming Interface private declarations
*
- * $Header: /cvsroot/pgsql/src/include/executor/spi_priv.h,v 1.7 2000/06/28 03:33:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/include/executor/spi_priv.h,v 1.8 2001/05/21 14:22:18 wieck Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,6 +25,7 @@ typedef struct
typedef struct
{
+ MemoryContext plancxt;
List *qtlist;
List *ptlist;
int nargs;
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 1cdd770dfdc..6a17ec86704 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: portal.h,v 1.27 2001/03/22 04:01:14 momjian Exp $
+ * $Id: portal.h,v 1.28 2001/05/21 14:22:18 wieck Exp $
*
*-------------------------------------------------------------------------
*/
@@ -55,7 +55,7 @@ typedef struct PortalData
* estimate of the maximum number of open portals a user would have,
* used in initially sizing the PortalHashTable in EnablePortalManager()
*/
-#define PORTALS_PER_USER 10
+#define PORTALS_PER_USER 64
extern void EnablePortalManager(void);
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index b1373674540..9dee73acc97 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.18 2001/05/18 21:16:59 wieck Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.19 2001/05/21 14:22:18 wieck Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -47,6 +47,7 @@
static PLpgSQL_expr *read_sqlstmt(int until, char *s, char *sqlstart);
static PLpgSQL_stmt *make_select_stmt(void);
+static PLpgSQL_stmt *make_fetch_stmt(void);
static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
%}
@@ -99,17 +100,17 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
%type <varname> decl_varname
%type <str> decl_renname
%type <ival> decl_const, decl_notnull, decl_atttypmod, decl_atttypmodval
-%type <expr> decl_defval
+%type <expr> decl_defval, decl_cursor_query
%type <dtype> decl_datatype, decl_dtypename
-%type <row> decl_rowtype
+%type <row> decl_rowtype, decl_cursor_args, decl_cursor_arglist
%type <nsitem> decl_aliasitem
%type <str> decl_stmts, decl_stmt
%type <expr> expr_until_semi, expr_until_then, expr_until_loop
%type <expr> opt_exitcond
-%type <ival> assign_var
-%type <var> fori_var
+%type <ival> assign_var, cursor_variable
+%type <var> fori_var, cursor_varptr, decl_cursor_arg
%type <varname> fori_varname
%type <forilow> fori_lower
%type <rec> fors_target
@@ -124,6 +125,7 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
%type <stmt> stmt_return, stmt_raise, stmt_execsql, stmt_fori
%type <stmt> stmt_fors, stmt_select, stmt_perform
%type <stmt> stmt_dynexecute, stmt_dynfors, stmt_getdiag
+%type <stmt> stmt_open, stmt_fetch, stmt_close
%type <intlist> raise_params
%type <ival> raise_level, raise_param
@@ -140,7 +142,9 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
%token K_ALIAS
%token K_ASSIGN
%token K_BEGIN
+%token K_CLOSE
%token K_CONSTANT
+%token K_CURSOR
%token K_DEBUG
%token K_DECLARE
%token K_DEFAULT
@@ -153,15 +157,18 @@ static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row);
%token K_EXECUTE
%token K_EXIT
%token K_FOR
+%token K_FETCH
%token K_FROM
%token K_GET
%token K_IF
%token K_IN
%token K_INTO
+%token K_IS
%token K_LOOP
%token K_NOT
%token K_NOTICE
%token K_NULL
+%token K_OPEN
%token K_PERFORM
%token K_ROW_COUNT
%token K_RAISE
@@ -300,6 +307,7 @@ decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval
PLpgSQL_var *new;
new = malloc(sizeof(PLpgSQL_var));
+ memset(new, 0, sizeof(PLpgSQL_var));
new->dtype = PLPGSQL_DTYPE_VAR;
new->refname = $1.name;
@@ -347,8 +355,152 @@ decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval
{
plpgsql_ns_rename($2, $4);
}
+ | decl_varname K_CURSOR decl_cursor_args K_IS K_SELECT decl_cursor_query
+ {
+ PLpgSQL_var *new;
+ PLpgSQL_expr *curname_def;
+ char buf[1024];
+ char *cp1;
+ char *cp2;
+
+ plpgsql_ns_pop();
+
+ new = malloc(sizeof(PLpgSQL_var));
+ memset(new, 0, sizeof(PLpgSQL_var));
+
+ curname_def = malloc(sizeof(PLpgSQL_var));
+ memset(curname_def, 0, sizeof(PLpgSQL_var));
+
+ new->dtype = PLPGSQL_DTYPE_VAR;
+ new->refname = $1.name;
+ new->lineno = $1.lineno;
+
+ curname_def->dtype = PLPGSQL_DTYPE_EXPR;
+ strcpy(buf, "SELECT '");
+ cp1 = new->refname;
+ cp2 = buf + strlen(buf);
+ while (*cp1 != '\0')
+ {
+ if (*cp1 == '\\' || *cp1 == '\'')
+ *cp2++ = '\\';
+ *cp2++ = *cp1++;
+ }
+ strcat(buf, "'");
+ curname_def->query = strdup(buf);
+ new->default_val = curname_def;
+
+ plpgsql_parse_word("refcursor");
+ new->datatype = yylval.dtype;
+
+ new->cursor_explicit_expr = $6;
+ if ($3 == NULL)
+ new->cursor_explicit_argrow = -1;
+ else
+ new->cursor_explicit_argrow = $3->rowno;
+
+ plpgsql_adddatum((PLpgSQL_datum *)new);
+ plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, new->varno,
+ $1.name);
+ }
+ ;
+
+decl_cursor_query :
+ {
+ PLpgSQL_expr *query;
+
+ plpgsql_ns_setlocal(false);
+ query = plpgsql_read_expression(';', ";");
+ plpgsql_ns_setlocal(true);
+
+ $$ = query;
+ }
;
+decl_cursor_args :
+ {
+ $$ = NULL;
+ }
+ | decl_cursor_openparen decl_cursor_arglist ')'
+ {
+ char **ftmp;
+ int *vtmp;
+
+ ftmp = malloc($2->nfields * sizeof(char *));
+ vtmp = malloc($2->nfields * sizeof(int));
+ memcpy(ftmp, $2->fieldnames, $2->nfields * sizeof(char *));
+ memcpy(vtmp, $2->varnos, $2->nfields * sizeof(int));
+
+ pfree((char *)($2->fieldnames));
+ pfree((char *)($2->varnos));
+
+ $2->fieldnames = ftmp;
+ $2->varnos = vtmp;
+
+ plpgsql_adddatum((PLpgSQL_datum *)$2);
+
+ $$ = $2;
+ }
+ ;
+
+decl_cursor_arglist : decl_cursor_arg
+ {
+ PLpgSQL_row *new;
+
+ new = malloc(sizeof(PLpgSQL_row));
+ memset(new, 0, sizeof(PLpgSQL_row));
+
+ new->dtype = PLPGSQL_DTYPE_ROW;
+ new->refname = strdup("*internal*");
+ new->lineno = yylineno;
+ new->rowtypeclass = InvalidOid;
+ new->fieldnames = palloc(1024 * sizeof(char *));
+ new->varnos = palloc(1024 * sizeof(int));
+ new->nfields = 1;
+
+ new->fieldnames[0] = $1->refname;
+ new->varnos[0] = $1->varno;
+
+ $$ = new;
+ }
+ | decl_cursor_arglist ',' decl_cursor_arg
+ {
+ int i = $1->nfields++;
+
+ $1->fieldnames[i] = $3->refname;
+ $1->varnos[i] = $3->varno;
+ }
+ ;
+
+decl_cursor_arg : decl_varname decl_datatype
+ {
+ PLpgSQL_var *new;
+
+ new = malloc(sizeof(PLpgSQL_var));
+ memset(new, 0, sizeof(PLpgSQL_var));
+
+ new->dtype = PLPGSQL_DTYPE_VAR;
+ new->refname = $1.name;
+ new->lineno = $1.lineno;
+
+ new->datatype = $2;
+ new->isconst = false;
+ new->notnull = false;
+
+ plpgsql_adddatum((PLpgSQL_datum *)new);
+ plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, new->varno,
+ $1.name);
+
+ $$ = new;
+ }
+ ;
+
+decl_cursor_openparen : '('
+ {
+ plpgsql_ns_push(NULL);
+ }
+ ;
+
+
decl_aliasitem : T_WORD
{
PLpgSQL_nsitem *nsi;
@@ -581,6 +733,12 @@ proc_stmt : pl_block
{ $$ = $1; }
| stmt_getdiag
{ $$ = $1; }
+ | stmt_open
+ { $$ = $1; }
+ | stmt_fetch
+ { $$ = $1; }
+ | stmt_close
+ { $$ = $1; }
;
stmt_perform : K_PERFORM lno expr_until_semi
@@ -836,6 +994,7 @@ fori_var : fori_varname
PLpgSQL_var *new;
new = malloc(sizeof(PLpgSQL_var));
+ memset(new, 0, sizeof(PLpgSQL_var));
new->dtype = PLPGSQL_DTYPE_VAR;
new->refname = $1.name;
@@ -1189,15 +1348,137 @@ stmt_execsql : execsql_start lno
stmt_dynexecute : K_EXECUTE lno expr_until_semi
{
- PLpgSQL_stmt_dynexecute *new;
+ PLpgSQL_stmt_dynexecute *new;
- new = malloc(sizeof(PLpgSQL_stmt_dynexecute));
- new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
- new->lineno = $2;
- new->query = $3;
+ new = malloc(sizeof(PLpgSQL_stmt_dynexecute));
+ new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
+ new->lineno = $2;
+ new->query = $3;
+
+ $$ = (PLpgSQL_stmt *)new;
+ }
+ ;
+
+stmt_open : K_OPEN lno cursor_varptr
+ {
+ PLpgSQL_stmt_open *new;
+ int tok;
+
+ new = malloc(sizeof(PLpgSQL_stmt_open));
+ memset(new, 0, sizeof(PLpgSQL_stmt_open));
+
+ new->cmd_type = PLPGSQL_STMT_OPEN;
+ new->lineno = $2;
+ new->curvar = $3->varno;
+
+ if ($3->cursor_explicit_expr == NULL)
+ {
+ tok = yylex();
+
+ if (tok != K_FOR)
+ {
+ plpgsql_comperrinfo();
+ elog(ERROR, "syntax error at \"%s\" - expected FOR to open a reference cursor", yytext);
+ }
+
+ tok = yylex();
+ switch (tok)
+ {
+ case K_SELECT:
+ new->query = plpgsql_read_expression(';', ";");
+ break;
+
+ case K_EXECUTE:
+ new->dynquery = plpgsql_read_expression(';', ";");
+ break;
+
+ default:
+ plpgsql_comperrinfo();
+ elog(ERROR, "syntax error at \"%s\"", yytext);
+ }
+
+ }
+ else
+ {
+ if ($3->cursor_explicit_argrow >= 0)
+ {
+ tok = yylex();
+
+ if (tok != '(')
+ {
+ plpgsql_comperrinfo();
+ elog(ERROR, "cursor %s has arguments", $3->refname);
+ }
+
+ new->argquery = read_sqlstmt(';', ";", "SELECT (");
+ }
+ else
+ {
+ tok = yylex();
+
+ if (tok == '(')
+ {
+ plpgsql_comperrinfo();
+ elog(ERROR, "cursor %s has no arguments", $3->refname);
+ }
+
+ if (tok != ';')
+ {
+ plpgsql_comperrinfo();
+ elog(ERROR, "syntax error at \"%s\"", yytext);
+ }
+ }
+ }
+
+ $$ = (PLpgSQL_stmt *)new;
+ }
+ ;
+
+stmt_fetch : K_FETCH lno cursor_variable K_INTO
+ {
+ PLpgSQL_stmt_fetch *new;
+
+ new = (PLpgSQL_stmt_fetch *)make_fetch_stmt();
+ new->curvar = $3;
$$ = (PLpgSQL_stmt *)new;
+ $$->lineno = $2;
+ }
+ ;
+
+stmt_close : K_CLOSE lno cursor_variable ';'
+ {
+ PLpgSQL_stmt_close *new;
+
+ new = malloc(sizeof(PLpgSQL_stmt_close));
+ new->cmd_type = PLPGSQL_STMT_CLOSE;
+ new->lineno = $2;
+ new->curvar = $3;
+
+ $$ = (PLpgSQL_stmt *)new;
+ }
+ ;
+
+cursor_varptr : T_VARIABLE
+ {
+ if (yylval.var->datatype->typoid != REFCURSOROID)
+ {
+ plpgsql_comperrinfo();
+ elog(ERROR, "%s must be of type cursor or refcursor", yylval.var->refname);
}
+ $$ = yylval.var;
+ }
+ ;
+
+cursor_variable : T_VARIABLE
+ {
+ if (yylval.var->datatype->typoid != REFCURSOROID)
+ {
+ plpgsql_comperrinfo();
+ elog(ERROR, "%s must be of type refcursor", yylval.var->refname);
+ }
+ $$ = yylval.var->varno;
+ }
;
execsql_start : T_WORD
@@ -1615,6 +1896,113 @@ make_select_stmt()
}
+static PLpgSQL_stmt *
+make_fetch_stmt()
+{
+ int tok;
+ PLpgSQL_row *row = NULL;
+ PLpgSQL_rec *rec = NULL;
+ PLpgSQL_stmt_fetch *fetch;
+ int have_nexttok = 0;
+
+ tok = yylex();
+ switch (tok)
+ {
+ case T_ROW:
+ row = yylval.row;
+ break;
+
+ case T_RECORD:
+ rec = yylval.rec;
+ break;
+
+ case T_VARIABLE:
+ case T_RECFIELD:
+ {
+ PLpgSQL_var *var;
+ PLpgSQL_recfield *recfield;
+ int nfields = 1;
+ char *fieldnames[1024];
+ int varnos[1024];
+
+ switch (tok)
+ {
+ case T_VARIABLE:
+ var = yylval.var;
+ fieldnames[0] = strdup(yytext);
+ varnos[0] = var->varno;
+ break;
+
+ case T_RECFIELD:
+ recfield = yylval.recfield;
+ fieldnames[0] = strdup(yytext);
+ varnos[0] = recfield->rfno;
+ break;
+ }
+
+ while ((tok = yylex()) == ',')
+ {
+ tok = yylex();
+ switch(tok)
+ {
+ case T_VARIABLE:
+ var = yylval.var;
+ fieldnames[nfields] = strdup(yytext);
+ varnos[nfields++] = var->varno;
+ break;
+
+ case T_RECFIELD:
+ recfield = yylval.recfield;
+ fieldnames[0] = strdup(yytext);
+ varnos[0] = recfield->rfno;
+ break;
+
+ default:
+ elog(ERROR, "plpgsql: %s is not a variable or record field", yytext);
+ }
+ }
+ row = malloc(sizeof(PLpgSQL_row));
+ row->dtype = PLPGSQL_DTYPE_ROW;
+ row->refname = strdup("*internal*");
+ row->lineno = yylineno;
+ row->rowtypeclass = InvalidOid;
+ row->nfields = nfields;
+ row->fieldnames = malloc(sizeof(char *) * nfields);
+ row->varnos = malloc(sizeof(int) * nfields);
+ while (--nfields >= 0)
+ {
+ row->fieldnames[nfields] = fieldnames[nfields];
+ row->varnos[nfields] = varnos[nfields];
+ }
+
+ plpgsql_adddatum((PLpgSQL_datum *)row);
+
+ have_nexttok = 1;
+ }
+ break;
+
+ default:
+ {
+ elog(ERROR, "syntax error at '%s'", yytext);
+ }
+ }
+
+ if (!have_nexttok)
+ tok = yylex();
+
+ if (tok != ';')
+ elog(ERROR, "syntax error at '%s'", yytext);
+
+ fetch = malloc(sizeof(PLpgSQL_stmt_select));
+ memset(fetch, 0, sizeof(PLpgSQL_stmt_fetch));
+ fetch->cmd_type = PLPGSQL_STMT_FETCH;
+ fetch->rec = rec;
+ fetch->row = row;
+
+ return (PLpgSQL_stmt *)fetch;
+}
+
+
static PLpgSQL_expr *
make_tupret_expr(PLpgSQL_row *row)
{
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 9cfa7482413..5d939850286 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.30 2001/04/18 20:42:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.31 2001/05/21 14:22:18 wieck Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -282,6 +282,7 @@ plpgsql_compile(Oid fn_oid, int functype)
perm_fmgr_info(typeStruct->typinput, &(var->datatype->typinput));
var->datatype->typelem = typeStruct->typelem;
var->datatype->typbyval = typeStruct->typbyval;
+ var->datatype->typlen = typeStruct->typlen;
var->datatype->atttypmod = -1;
var->isconst = true;
var->notnull = false;
@@ -313,6 +314,9 @@ plpgsql_compile(Oid fn_oid, int functype)
memset(rec, 0, sizeof(PLpgSQL_rec));
rec->dtype = PLPGSQL_DTYPE_REC;
rec->refname = strdup("new");
+ rec->tup = NULL;
+ rec->tupdesc = NULL;
+ rec->freetup = false;
plpgsql_adddatum((PLpgSQL_datum *) rec);
plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname);
function->new_varno = rec->recno;
@@ -324,6 +328,9 @@ plpgsql_compile(Oid fn_oid, int functype)
memset(rec, 0, sizeof(PLpgSQL_rec));
rec->dtype = PLPGSQL_DTYPE_REC;
rec->refname = strdup("old");
+ rec->tup = NULL;
+ rec->tupdesc = NULL;
+ rec->freetup = false;
plpgsql_adddatum((PLpgSQL_datum *) rec);
plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname);
function->old_varno = rec->recno;
@@ -632,6 +639,7 @@ plpgsql_parse_word(char *word)
perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
typ->typelem = typeStruct->typelem;
typ->typbyval = typeStruct->typbyval;
+ typ->typlen = typeStruct->typlen;
typ->atttypmod = -1;
plpgsql_yylval.dtype = typ;
@@ -948,6 +956,7 @@ plpgsql_parse_wordtype(char *word)
perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
typ->typelem = typeStruct->typelem;
typ->typbyval = typeStruct->typbyval;
+ typ->typlen = typeStruct->typlen;
typ->atttypmod = -1;
plpgsql_yylval.dtype = typ;
@@ -1091,6 +1100,7 @@ plpgsql_parse_dblwordtype(char *string)
perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
typ->typelem = typeStruct->typelem;
typ->typbyval = typeStruct->typbyval;
+ typ->typlen = typeStruct->typlen;
typ->atttypmod = attrStruct->atttypmod;
plpgsql_yylval.dtype = typ;
@@ -1230,13 +1240,14 @@ plpgsql_parse_wordrowtype(char *string)
perm_fmgr_info(typeStruct->typinput, &(var->datatype->typinput));
var->datatype->typelem = typeStruct->typelem;
var->datatype->typbyval = typeStruct->typbyval;
+ var->datatype->typlen = typeStruct->typlen;
var->datatype->atttypmod = attrStruct->atttypmod;
var->isconst = false;
var->notnull = false;
var->default_val = NULL;
var->value = (Datum) 0;
var->isnull = true;
- var->shouldfree = false;
+ var->freeval = false;
ReleaseSysCache(typetup);
ReleaseSysCache(attrtup);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index ba3b1b495fb..1ad04739a33 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.42 2001/05/08 01:00:53 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.43 2001/05/21 14:22:19 wieck Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -95,6 +95,12 @@ static int exec_stmt_fors(PLpgSQL_execstate * estate,
PLpgSQL_stmt_fors * stmt);
static int exec_stmt_select(PLpgSQL_execstate * estate,
PLpgSQL_stmt_select * stmt);
+static int exec_stmt_open(PLpgSQL_execstate * estate,
+ PLpgSQL_stmt_open * stmt);
+static int exec_stmt_fetch(PLpgSQL_execstate * estate,
+ PLpgSQL_stmt_fetch * stmt);
+static int exec_stmt_close(PLpgSQL_execstate * estate,
+ PLpgSQL_stmt_close * stmt);
static int exec_stmt_exit(PLpgSQL_execstate * estate,
PLpgSQL_stmt_exit * stmt);
static int exec_stmt_return(PLpgSQL_execstate * estate,
@@ -128,7 +134,7 @@ static Datum exec_eval_expr(PLpgSQL_execstate * estate,
bool *isNull,
Oid *rettype);
static int exec_run_select(PLpgSQL_execstate * estate,
- PLpgSQL_expr * expr, int maxtuples);
+ PLpgSQL_expr * expr, int maxtuples, Portal *portalP);
static void exec_move_row(PLpgSQL_execstate * estate,
PLpgSQL_rec * rec,
PLpgSQL_row * row,
@@ -232,6 +238,12 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
case PLPGSQL_STMT_DYNFORS:
stmttype = "for over execute statement";
break;
+ case PLPGSQL_STMT_FETCH:
+ stmttype = "fetch";
+ break;
+ case PLPGSQL_STMT_CLOSE:
+ stmttype = "close";
+ break;
default:
stmttype = "unknown";
break;
@@ -314,7 +326,7 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
var->value = fcinfo->arg[i];
var->isnull = fcinfo->argnull[i];
- var->shouldfree = false;
+ var->freeval = false;
}
break;
@@ -353,7 +365,7 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
var->value = 0;
var->isnull = true;
- var->shouldfree = false;
+ var->freeval = false;
}
break;
@@ -607,6 +619,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
rec_old = (PLpgSQL_rec *) (estate.datums[func->old_varno]);
var = (PLpgSQL_var *) (estate.datums[func->tg_op_varno]);
var->isnull = false;
+ var->freeval = false;
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
{
@@ -644,11 +657,13 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
*/
var = (PLpgSQL_var *) (estate.datums[func->tg_name_varno]);
var->isnull = false;
+ var->freeval = false;
var->value = DirectFunctionCall1(namein,
CStringGetDatum(trigdata->tg_trigger->tgname));
var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]);
var->isnull = false;
+ var->freeval = false;
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
var->value = DirectFunctionCall1(textin, CStringGetDatum("BEFORE"));
else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
@@ -658,6 +673,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
var = (PLpgSQL_var *) (estate.datums[func->tg_level_varno]);
var->isnull = false;
+ var->freeval = false;
if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
var->value = DirectFunctionCall1(textin, CStringGetDatum("ROW"));
else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
@@ -667,6 +683,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
var = (PLpgSQL_var *) (estate.datums[func->tg_relid_varno]);
var->isnull = false;
+ var->freeval = false;
var->value = (Datum) (trigdata->tg_relation->rd_id);
var = (PLpgSQL_var *) (estate.datums[func->tg_relname_varno]);
@@ -676,6 +693,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
var = (PLpgSQL_var *) (estate.datums[func->tg_nargs_varno]);
var->isnull = false;
+ var->freeval = false;
var->value = (Datum) (trigdata->tg_trigger->tgnargs);
/*
@@ -709,7 +727,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
var->value = 0;
var->isnull = true;
- var->shouldfree = false;
+ var->freeval = false;
}
break;
@@ -794,6 +812,7 @@ copy_var(PLpgSQL_var * var)
PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var));
memcpy(new, var, sizeof(PLpgSQL_var));
+ new->freeval = false;
return new;
}
@@ -805,6 +824,10 @@ copy_rec(PLpgSQL_rec * rec)
PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec));
memcpy(new, rec, sizeof(PLpgSQL_rec));
+ new->tup = NULL;
+ new->tupdesc = NULL;
+ new->freetup = false;
+ new->freetupdesc = false;
return new;
}
@@ -834,6 +857,12 @@ exec_stmt_block(PLpgSQL_execstate * estate, PLpgSQL_stmt_block * block)
{
PLpgSQL_var *var = (PLpgSQL_var *) (estate->datums[n]);
+ if (var->freeval)
+ {
+ pfree((void *)(var->value));
+ var->freeval = false;
+ }
+
if (!var->isconst || var->isnull)
{
if (var->default_val == NULL)
@@ -856,6 +885,13 @@ exec_stmt_block(PLpgSQL_execstate * estate, PLpgSQL_stmt_block * block)
{
PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[n]);
+ if (rec->freetup)
+ {
+ heap_freetuple(rec->tup);
+ FreeTupleDesc(rec->tupdesc);
+ rec->freetup = false;
+ }
+
rec->tup = NULL;
rec->tupdesc = NULL;
}
@@ -1002,6 +1038,18 @@ exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt)
rc = exec_stmt_dynfors(estate, (PLpgSQL_stmt_dynfors *) stmt);
break;
+ case PLPGSQL_STMT_OPEN:
+ rc = exec_stmt_open(estate, (PLpgSQL_stmt_open *) stmt);
+ break;
+
+ case PLPGSQL_STMT_FETCH:
+ rc = exec_stmt_fetch(estate, (PLpgSQL_stmt_fetch *) stmt);
+ break;
+
+ case PLPGSQL_STMT_CLOSE:
+ rc = exec_stmt_close(estate, (PLpgSQL_stmt_close *) stmt);
+ break;
+
default:
error_info_stmt = save_estmt;
elog(ERROR, "unknown cmdtype %d in exec_stmt",
@@ -1092,6 +1140,7 @@ exec_stmt_if(PLpgSQL_execstate * estate, PLpgSQL_stmt_if * stmt)
bool isnull = false;
value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype);
+ SPI_freetuptable(SPI_tuptable);
if (value)
{
@@ -1166,6 +1215,7 @@ exec_stmt_while(PLpgSQL_execstate * estate, PLpgSQL_stmt_while * stmt)
for (;;)
{
value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype);
+ SPI_freetuptable(SPI_tuptable);
if (!value)
break;
@@ -1227,6 +1277,7 @@ exec_stmt_fori(PLpgSQL_execstate * estate, PLpgSQL_stmt_fori * stmt)
elog(ERROR, "lower bound of FOR loop cannot be NULL");
var->value = value;
var->isnull = false;
+ SPI_freetuptable(SPI_tuptable);
/*
* Get the value of the upper bound
@@ -1238,6 +1289,7 @@ exec_stmt_fori(PLpgSQL_execstate * estate, PLpgSQL_stmt_fori * stmt)
var->datatype->atttypmod, &isnull);
if (isnull)
elog(ERROR, "upper bound of FOR loop cannot be NULL");
+ SPI_freetuptable(SPI_tuptable);
/*
* Now do the loop
@@ -1317,6 +1369,7 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
PLpgSQL_rec *rec = NULL;
PLpgSQL_row *row = NULL;
SPITupleTable *tuptab;
+ Portal portal;
int rc;
int i;
int n;
@@ -1340,12 +1393,12 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
}
/*
- * Run the query
+ * Open the implicit cursor for the statement and fetch
+ * the initial 10 rows.
*/
- exec_run_select(estate, stmt->query, 0);
+ exec_run_select(estate, stmt->query, 0, &portal);
+ SPI_cursor_fetch(portal, true, 10);
n = SPI_processed;
- tuptab = SPI_tuptable;
- SPI_tuptable = NULL;
/*
* If the query didn't return any row, set the target to NULL and
@@ -1354,6 +1407,7 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
if (n == 0)
{
exec_move_row(estate, rec, row, NULL, NULL);
+ SPI_cursor_close(portal);
return PLPGSQL_RC_OK;
}
@@ -1365,45 +1419,71 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
/*
* Now do the loop
*/
- for (i = 0; i < n; i++)
+ for (;;)
{
+ tuptab = SPI_tuptable;
+ SPI_tuptable = NULL;
- /*
- * Assign the tuple to the target
- */
- exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc);
+ for (i = 0; i < n; i++)
+ {
- /*
- * Execute the statements
- */
- rc = exec_stmts(estate, stmt->body);
+ /*
+ * Assign the tuple to the target
+ */
+ exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc);
- /*
- * Check returncode
- */
- switch (rc)
- {
- case PLPGSQL_RC_OK:
- break;
+ /*
+ * Execute the statements
+ */
+ rc = exec_stmts(estate, stmt->body);
- case PLPGSQL_RC_EXIT:
- if (estate->exitlabel == NULL)
+ /*
+ * Check returncode
+ */
+ switch (rc)
+ {
+ case PLPGSQL_RC_OK:
+ break;
+
+ case PLPGSQL_RC_EXIT:
+ SPI_freetuptable(tuptab);
+ SPI_cursor_close(portal);
+
+ if (estate->exitlabel == NULL)
+ return PLPGSQL_RC_OK;
+ if (stmt->label == NULL)
+ return PLPGSQL_RC_EXIT;
+ if (strcmp(stmt->label, estate->exitlabel))
+ return PLPGSQL_RC_EXIT;
+ estate->exitlabel = NULL;
return PLPGSQL_RC_OK;
- if (stmt->label == NULL)
- return PLPGSQL_RC_EXIT;
- if (strcmp(stmt->label, estate->exitlabel))
- return PLPGSQL_RC_EXIT;
- estate->exitlabel = NULL;
- return PLPGSQL_RC_OK;
- case PLPGSQL_RC_RETURN:
- return PLPGSQL_RC_RETURN;
+ case PLPGSQL_RC_RETURN:
+ SPI_freetuptable(tuptab);
+ SPI_cursor_close(portal);
- default:
- elog(ERROR, "unknown rc %d from exec_stmts()", rc);
+ return PLPGSQL_RC_RETURN;
+
+ default:
+ elog(ERROR, "unknown rc %d from exec_stmts()", rc);
+ }
}
+
+ SPI_freetuptable(tuptab);
+
+ /*
+ * Fetch the next 50 tuples
+ */
+ SPI_cursor_fetch(portal, true, 50);
+ if ((n = SPI_processed) == 0)
+ break;
}
+ /*
+ * Close the implicit cursor
+ */
+ SPI_cursor_close(portal);
+
return PLPGSQL_RC_OK;
}
@@ -1442,7 +1522,7 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
/*
* Run the query
*/
- exec_run_select(estate, stmt->query, 1);
+ exec_run_select(estate, stmt->query, 1, NULL);
n = SPI_processed;
tuptab = SPI_tuptable;
SPI_tuptable = NULL;
@@ -1461,8 +1541,8 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
* Put the result into the target and set found to true
*/
exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc);
-
exec_set_found(estate, true);
+ SPI_freetuptable(tuptab);
return PLPGSQL_RC_OK;
}
@@ -1485,6 +1565,7 @@ exec_stmt_exit(PLpgSQL_execstate * estate, PLpgSQL_stmt_exit * stmt)
if (stmt->cond != NULL)
{
value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype);
+ SPI_freetuptable(SPI_tuptable);
if (!value)
return PLPGSQL_RC_OK;
}
@@ -1523,7 +1604,7 @@ exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt)
}
else
{
- exec_run_select(estate, stmt->expr, 1);
+ exec_run_select(estate, stmt->expr, 1, NULL);
estate->retval = (Datum) SPI_copytuple(SPI_tuptable->vals[0]);
estate->rettupdesc = SPI_tuptable->tupdesc;
estate->retisnull = false;
@@ -1643,6 +1724,7 @@ exec_stmt_raise(PLpgSQL_execstate * estate, PLpgSQL_stmt_raise * stmt)
(estate->datums[stmt->params[pidx]]);
value = (int) exec_eval_expr(estate, trigarg->argnum,
&valisnull, &valtype);
+ SPI_freetuptable(SPI_tuptable);
if (valisnull)
extval = "<INDEX_IS_NULL>";
else
@@ -1835,6 +1917,7 @@ exec_stmt_execsql(PLpgSQL_execstate * estate,
trigarg = (PLpgSQL_trigarg *) (estate->datums[expr->params[i]]);
tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
&isnull, &tgargoid);
+ SPI_freetuptable(SPI_tuptable);
if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
{
values[i] = 0;
@@ -1922,8 +2005,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate * estate,
ObjectIdGetDatum(typeStruct->typelem),
Int32GetDatum(-1)));
- if (!typeStruct->typbyval)
- pfree((void *) query);
+ SPI_freetuptable(SPI_tuptable);
ReleaseSysCache(typetup);
@@ -1995,6 +2077,8 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
HeapTuple typetup;
Form_pg_type typeStruct;
FmgrInfo finfo_output;
+ void *plan;
+ Portal portal;
/*
* Initialize the global found variable to false
@@ -2038,21 +2122,28 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
ObjectIdGetDatum(typeStruct->typelem),
Int32GetDatum(-1)));
- if (!typeStruct->typbyval)
- pfree((void *) query);
+ SPI_freetuptable(SPI_tuptable);
ReleaseSysCache(typetup);
/*
- * Run the query
+ * Prepare a plan and open an implicit cursor for the query
*/
- if (SPI_exec(querystr, 0) != SPI_OK_SELECT)
- elog(ERROR, "FOR ... EXECUTE query '%s' was not SELECT", querystr);
+ plan = SPI_prepare(querystr, 0, NULL);
+ if (plan == NULL)
+ elog(ERROR, "SPI_prepare() failed for dynamic query \"%s\"", querystr);
+ portal = SPI_cursor_open(NULL, plan, NULL, NULL);
+ if (portal == NULL)
+ elog(ERROR, "failed to open implicit cursor for dynamic query \"%s\"",
+ querystr);
pfree(querystr);
+ SPI_freeplan(plan);
+ /*
+ * Fetch the initial 10 tuples
+ */
+ SPI_cursor_fetch(portal, true, 10);
n = SPI_processed;
- tuptab = SPI_tuptable;
- SPI_tuptable = NULL;
/*
* If the query didn't return any row, set the target to NULL and
@@ -2061,6 +2152,7 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
if (n == 0)
{
exec_move_row(estate, rec, row, NULL, NULL);
+ SPI_cursor_close(portal);
return PLPGSQL_RC_OK;
}
@@ -2072,44 +2164,431 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
/*
* Now do the loop
*/
- for (i = 0; i < n; i++)
+ for (;;)
{
+ tuptab = SPI_tuptable;
+ SPI_tuptable = NULL;
+
+ for (i = 0; i < n; i++)
+ {
+
+ /*
+ * Assign the tuple to the target
+ */
+ exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc);
+
+ /*
+ * Execute the statements
+ */
+ rc = exec_stmts(estate, stmt->body);
+
+ /*
+ * Check returncode
+ */
+ switch (rc)
+ {
+ case PLPGSQL_RC_OK:
+ break;
+
+ case PLPGSQL_RC_EXIT:
+ SPI_freetuptable(tuptab);
+ SPI_cursor_close(portal);
+
+ if (estate->exitlabel == NULL)
+ return PLPGSQL_RC_OK;
+ if (stmt->label == NULL)
+ return PLPGSQL_RC_EXIT;
+ if (strcmp(stmt->label, estate->exitlabel))
+ return PLPGSQL_RC_EXIT;
+ estate->exitlabel = NULL;
+ return PLPGSQL_RC_OK;
+
+ case PLPGSQL_RC_RETURN:
+ SPI_freetuptable(tuptab);
+ SPI_cursor_close(portal);
+
+ return PLPGSQL_RC_RETURN;
+
+ default:
+ elog(ERROR, "unknown rc %d from exec_stmts()", rc);
+ }
+ }
+
+ SPI_freetuptable(tuptab);
/*
- * Assign the tuple to the target
+ * Fetch the next 50 tuples
*/
- exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc);
+ SPI_cursor_fetch(portal, true, 50);
+ if ((n = SPI_processed) == 0)
+ break;
+ }
- /*
- * Execute the statements
+ /*
+ * Close the cursor
+ */
+ SPI_cursor_close(portal);
+
+ return PLPGSQL_RC_OK;
+}
+
+
+/* ----------
+ * exec_stmt_open Execute an OPEN cursor statement
+ * ----------
+ */
+static int
+exec_stmt_open(PLpgSQL_execstate * estate, PLpgSQL_stmt_open * stmt)
+{
+ PLpgSQL_var *curvar = NULL;
+ char *curname = NULL;
+ PLpgSQL_expr *query = NULL;
+ Portal portal;
+
+ PLpgSQL_var *var;
+ PLpgSQL_rec *rec;
+ PLpgSQL_recfield *recfield;
+ PLpgSQL_trigarg *trigarg;
+ int tgargno;
+ Oid tgargoid;
+ int i;
+ Datum *values;
+ char *nulls;
+ int fno;
+ bool isnull;
+
+
+ /* ----------
+ * Get the cursor variable and if it has an assigned name, check
+ * that it's not in use currently.
+ * ----------
+ */
+ curvar = (PLpgSQL_var *) (estate->datums[stmt->curvar]);
+ if (!curvar->isnull)
+ {
+ curname = DatumGetCString(DirectFunctionCall1(textout, curvar->value));
+ if (SPI_cursor_find(curname) != NULL)
+ elog(ERROR, "cursor \"%s\" already in use", curname);
+ }
+
+ /* ----------
+ * Process the OPEN according to it's type.
+ * ----------
+ */
+ if (stmt->query != NULL)
+ {
+ /* ----------
+ * This is an OPEN refcursor FOR SELECT ...
+ *
+ * We just make sure the query is planned. The real work is
+ * done downstairs.
+ * ----------
*/
- rc = exec_stmts(estate, stmt->body);
+ query = stmt->query;
+ if (query->plan == NULL)
+ exec_prepare_plan(estate, query);
+ }
+ else if (stmt->dynquery != NULL)
+ {
+ /* ----------
+ * This is an OPEN refcursor FOR EXECUTE ...
+ * ----------
+ */
+ Datum queryD;
+ Oid restype;
+ char *querystr;
+ HeapTuple typetup;
+ Form_pg_type typeStruct;
+ FmgrInfo finfo_output;
+ void *curplan = NULL;
+
+ /* ----------
+ * We evaluate the string expression after the
+ * EXECUTE keyword. It's result is the querystring we have
+ * to execute.
+ * ----------
+ */
+ queryD = exec_eval_expr(estate, stmt->dynquery, &isnull, &restype);
+ if (isnull)
+ elog(ERROR, "cannot EXECUTE NULL query");
- /*
- * Check returncode
+ /* ----------
+ * Get the C-String representation.
+ * ----------
*/
- switch (rc)
+ typetup = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(restype),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(typetup))
+ elog(ERROR, "cache lookup for type %u failed (1)", restype);
+ typeStruct = (Form_pg_type) GETSTRUCT(typetup);
+
+ fmgr_info(typeStruct->typoutput, &finfo_output);
+ querystr = DatumGetCString(FunctionCall3(&finfo_output,
+ queryD,
+ ObjectIdGetDatum(typeStruct->typelem),
+ Int32GetDatum(-1)));
+
+ SPI_freetuptable(SPI_tuptable);
+ ReleaseSysCache(typetup);
+
+ /* ----------
+ * Now we prepare a query plan for it and open a cursor
+ * ----------
+ */
+ curplan = SPI_prepare(querystr, 0, NULL);
+ portal = SPI_cursor_open(curname, curplan, NULL, NULL);
+ if (portal == NULL)
+ elog(ERROR, "Failed to open cursor");
+ pfree(querystr);
+
+ /* ----------
+ * Store the eventually assigned cursor name in the cursor variable
+ * ----------
+ */
+ if (curvar->freeval)
+ pfree((void *)(curvar->value));
+
+ curvar->value = DirectFunctionCall1(textin, CStringGetDatum(portal->name));
+ curvar->isnull = false;
+ curvar->freeval = true;
+
+ return PLPGSQL_RC_OK;
+ }
+ else
+ {
+ /* ----------
+ * This is an OPEN cursor
+ * ----------
+ */
+ if (stmt->argquery != NULL)
{
- case PLPGSQL_RC_OK:
+ /* ----------
+ * Er - OPEN CURSOR (args). We fake a SELECT ... INTO ...
+ * statement to evaluate the args and put 'em into the
+ * internal row.
+ * ----------
+ */
+ PLpgSQL_stmt_select set_args;
+
+ memset(&set_args, 0, sizeof(set_args));
+ set_args.cmd_type = PLPGSQL_STMT_SELECT;
+ set_args.lineno = stmt->lineno;
+ set_args.row = (PLpgSQL_row *)
+ (estate->datums[curvar->cursor_explicit_argrow]);
+ set_args.query = stmt->argquery;
+
+ if (exec_stmt_select(estate, &set_args) != PLPGSQL_RC_OK)
+ elog(ERROR, "open cursor failed during argument processing");
+ }
+
+ query = curvar->cursor_explicit_expr;
+ if (query->plan == NULL)
+ exec_prepare_plan(estate, query);
+ }
+
+ /* ----------
+ * Here we go if we have a saved plan where we have to put
+ * values into, either from an explicit cursor or from a
+ * refcursor opened with OPEN ... FOR SELECT ...;
+ * ----------
+ */
+ values = palloc(sizeof(Datum) * (query->nparams + 1));
+ nulls = palloc(query->nparams + 1);
+
+ for (i = 0; i < query->nparams; i++)
+ {
+ switch (estate->datums[query->params[i]]->dtype)
+ {
+ case PLPGSQL_DTYPE_VAR:
+ var = (PLpgSQL_var *) (estate->datums[query->params[i]]);
+ values[i] = var->value;
+ if (var->isnull)
+ nulls[i] = 'n';
+ else
+ nulls[i] = ' ';
break;
- case PLPGSQL_RC_EXIT:
- if (estate->exitlabel == NULL)
- return PLPGSQL_RC_OK;
- if (stmt->label == NULL)
- return PLPGSQL_RC_EXIT;
- if (strcmp(stmt->label, estate->exitlabel))
- return PLPGSQL_RC_EXIT;
- estate->exitlabel = NULL;
- return PLPGSQL_RC_OK;
+ case PLPGSQL_DTYPE_RECFIELD:
+ recfield = (PLpgSQL_recfield *) (estate->datums[query->params[i]]);
+ rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]);
- case PLPGSQL_RC_RETURN:
- return PLPGSQL_RC_RETURN;
+ if (!HeapTupleIsValid(rec->tup))
+ elog(ERROR, "record %s is unassigned yet", rec->refname);
+ fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
+ if (fno == SPI_ERROR_NOATTRIBUTE)
+ elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname);
+
+ if (query->plan_argtypes[i] != SPI_gettypeid(rec->tupdesc, fno))
+ elog(ERROR, "type of %s.%s doesn't match that when preparing the plan", rec->refname, recfield->fieldname);
+
+ values[i] = SPI_getbinval(rec->tup, rec->tupdesc, fno, &isnull);
+ if (isnull)
+ nulls[i] = 'n';
+ else
+ nulls[i] = ' ';
+ break;
+
+ case PLPGSQL_DTYPE_TRIGARG:
+ trigarg = (PLpgSQL_trigarg *) (estate->datums[query->params[i]]);
+ tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
+ &isnull, &tgargoid);
+ SPI_freetuptable(SPI_tuptable);
+ if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
+ {
+ values[i] = 0;
+ nulls[i] = 'n';
+ }
+ else
+ {
+ values[i] = estate->trig_argv[tgargno];
+ nulls[i] = ' ';
+ }
+ break;
default:
- elog(ERROR, "unknown rc %d from exec_stmts()", rc);
+ elog(ERROR, "unknown parameter dtype %d in exec_stmt_open()",
+ estate->datums[query->params[i]]->dtype);
}
}
+ nulls[i] = '\0';
+
+ /* ----------
+ * Open the cursor
+ * ----------
+ */
+ portal = SPI_cursor_open(curname, query->plan, values, nulls);
+ if (portal == NULL)
+ elog(ERROR, "Failed to open cursor");
+
+ pfree(values);
+ pfree(nulls);
+
+ /* ----------
+ * Store the eventually assigned portal name in the cursor variable
+ * ----------
+ */
+ if (curvar->freeval)
+ pfree((void *)(curvar->value));
+
+ curvar->value = DirectFunctionCall1(textin, CStringGetDatum(portal->name));
+ curvar->isnull = false;
+ curvar->freeval = true;
+
+ return PLPGSQL_RC_OK;
+}
+
+
+/* ----------
+ * exec_stmt_fetch Fetch from a cursor into a target
+ * ----------
+ */
+static int
+exec_stmt_fetch(PLpgSQL_execstate * estate, PLpgSQL_stmt_fetch * stmt)
+{
+ PLpgSQL_var *curvar = NULL;
+ PLpgSQL_rec *rec = NULL;
+ PLpgSQL_row *row = NULL;
+ Portal portal;
+ char *curname;
+ int n;
+
+ /* ----------
+ * Get the portal of the cursor by name
+ * ----------
+ */
+ curvar = (PLpgSQL_var *) (estate->datums[stmt->curvar]);
+ if (curvar->isnull)
+ elog(ERROR, "cursor variable \"%s\" is NULL", curvar->refname);
+ curname = DatumGetCString(DirectFunctionCall1(textout, curvar->value));
+
+ portal = SPI_cursor_find(curname);
+ if (portal == NULL)
+ elog(ERROR, "cursor \"%s\" is invalid", curname);
+ pfree(curname);
+
+ /* ----------
+ * Initialize the global found variable to false
+ * ----------
+ */
+ exec_set_found(estate, false);
+
+ /* ----------
+ * Determine if we fetch into a record or a row
+ * ----------
+ */
+ if (stmt->rec != NULL)
+ rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
+ else
+ {
+ if (stmt->row != NULL)
+ row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
+ else
+ elog(ERROR, "unsupported target in exec_stmt_select()");
+ }
+
+ /* ----------
+ * Fetch 1 tuple from the cursor
+ * ----------
+ */
+ SPI_cursor_fetch(portal, true, 1);
+ n = SPI_processed;
+
+ /* ----------
+ * If the FETCH didn't return a row, set the target
+ * to NULL and return with FOUND = false.
+ * ----------
+ */
+ if (n == 0)
+ {
+ exec_move_row(estate, rec, row, NULL, NULL);
+ return PLPGSQL_RC_OK;
+ }
+
+ /* ----------
+ * Put the result into the target and set found to true
+ * ----------
+ */
+ exec_move_row(estate, rec, row, SPI_tuptable->vals[0],
+ SPI_tuptable->tupdesc);
+ exec_set_found(estate, true);
+
+ SPI_freetuptable(SPI_tuptable);
+
+ return PLPGSQL_RC_OK;
+}
+
+
+/* ----------
+ * exec_stmt_close Close a cursor
+ * ----------
+ */
+static int
+exec_stmt_close(PLpgSQL_execstate * estate, PLpgSQL_stmt_close * stmt)
+{
+ PLpgSQL_var *curvar = NULL;
+ Portal portal;
+ char *curname;
+
+ /* ----------
+ * Get the portal of the cursor by name
+ * ----------
+ */
+ curvar = (PLpgSQL_var *) (estate->datums[stmt->curvar]);
+ if (curvar->isnull)
+ elog(ERROR, "cursor variable \"%s\" is NULL", curvar->refname);
+ curname = DatumGetCString(DirectFunctionCall1(textout, curvar->value));
+
+ portal = SPI_cursor_find(curname);
+ if (portal == NULL)
+ elog(ERROR, "cursor \"%s\" is invalid", curname);
+ pfree(curname);
+
+ /* ----------
+ * And close it.
+ * ----------
+ */
+ SPI_cursor_close(portal);
return PLPGSQL_RC_OK;
}
@@ -2131,6 +2610,8 @@ exec_assign_expr(PLpgSQL_execstate * estate, PLpgSQL_datum * target,
value = exec_eval_expr(estate, expr, &isnull, &valtype);
if (target != NULL)
exec_assign_value(estate, target, value, valtype, &isnull);
+
+ SPI_freetuptable(SPI_tuptable);
}
@@ -2156,6 +2637,7 @@ exec_assign_value(PLpgSQL_execstate * estate,
Oid atttype;
int32 atttypmod;
HeapTuple typetup;
+ HeapTuple newtup;
Form_pg_type typeStruct;
FmgrInfo finfo_input;
@@ -2164,19 +2646,39 @@ exec_assign_value(PLpgSQL_execstate * estate,
case PLPGSQL_DTYPE_VAR:
/*
- * Target field is a variable - that's easy
+ * Target field is a variable
*/
var = (PLpgSQL_var *) target;
+
+ if (var->freeval)
+ {
+ pfree((void *)(var->value));
+ var->freeval = false;
+ }
+
newvalue = exec_cast_value(value, valtype, var->datatype->typoid,
&(var->datatype->typinput),
var->datatype->typelem,
var->datatype->atttypmod,
isNull);
+ if (!var->datatype->typbyval && newvalue == value && !*isNull)
+ {
+ int len;
+ if (var->datatype->typlen < 0)
+ len = VARSIZE(newvalue);
+ else
+ len = var->datatype->typlen;
+ var->value = (Datum)palloc(len);
+ memcpy((void *)(var->value), (void *)newvalue, len);
+ var->freeval = true;
+ }
+ else
+ var->value = newvalue;
+
if (*isNull && var->notnull)
elog(ERROR, "NULL assignment to variable '%s' declared NOT NULL", var->refname);
- var->value = newvalue;
var->isnull = *isNull;
break;
@@ -2263,7 +2765,14 @@ exec_assign_value(PLpgSQL_execstate * estate,
* replaces the old one in the record.
*/
nulls[i] = '\0';
- rec->tup = heap_formtuple(rec->tupdesc, values, nulls);
+ newtup = heap_formtuple(rec->tupdesc, values, nulls);
+
+ if (rec->freetup)
+ heap_freetuple(rec->tup);
+
+ rec->tup = newtup;
+ rec->freetup = true;
+
pfree(values);
pfree(nulls);
@@ -2289,6 +2798,9 @@ exec_eval_expr(PLpgSQL_execstate * estate,
{
int rc;
+ SPI_tuptable = NULL;
+ SPI_processed = 0;
+
/*
* If not already done create a plan for this expression
*/
@@ -2302,7 +2814,7 @@ exec_eval_expr(PLpgSQL_execstate * estate,
if (expr->plan_simple_expr != NULL)
return exec_eval_simple_expr(estate, expr, isNull, rettype);
- rc = exec_run_select(estate, expr, 2);
+ rc = exec_run_select(estate, expr, 2, NULL);
if (rc != SPI_OK_SELECT)
elog(ERROR, "query \"%s\" didn't return data", expr->query);
@@ -2321,7 +2833,8 @@ exec_eval_expr(PLpgSQL_execstate * estate,
if (SPI_processed > 1)
elog(ERROR, "query \"%s\" returned more than one row", expr->query);
if (SPI_tuptable->tupdesc->natts != 1)
- elog(ERROR, "query \"%s\" returned more than one column", expr->query);
+ elog(ERROR, "query \"%s\" returned %d columns", expr->query,
+ SPI_tuptable->tupdesc->natts);
/*
* Return the result and its type
@@ -2337,7 +2850,7 @@ exec_eval_expr(PLpgSQL_execstate * estate,
*/
static int
exec_run_select(PLpgSQL_execstate * estate,
- PLpgSQL_expr * expr, int maxtuples)
+ PLpgSQL_expr * expr, int maxtuples, Portal *portalP)
{
PLpgSQL_var *var;
PLpgSQL_rec *rec;
@@ -2401,6 +2914,7 @@ exec_run_select(PLpgSQL_execstate * estate,
trigarg = (PLpgSQL_trigarg *) (estate->datums[expr->params[i]]);
tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
&isnull, &tgargoid);
+ SPI_freetuptable(SPI_tuptable);
if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
{
values[i] = 0;
@@ -2423,6 +2937,16 @@ exec_run_select(PLpgSQL_execstate * estate,
/*
* Execute the query
*/
+ if (portalP != NULL)
+ {
+ *portalP = SPI_cursor_open(NULL, expr->plan, values, nulls);
+ if (*portalP == NULL)
+ elog(ERROR, "failed to open implicit cursor for \"%s\"",
+ expr->query);
+ pfree(values);
+ pfree(nulls);
+ return SPI_OK_CURSOR;
+ }
rc = SPI_execp(expr->plan, values, nulls, maxtuples);
if (rc != SPI_OK_SELECT)
elog(ERROR, "query \"%s\" isn't a SELECT", expr->query);
@@ -2510,6 +3034,7 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
trigarg = (PLpgSQL_trigarg *) (estate->datums[expr->params[i]]);
tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
&isnull, &tgargoid);
+ SPI_freetuptable(SPI_tuptable);
if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
{
paramLI->value = 0;
@@ -2581,10 +3106,23 @@ exec_move_row(PLpgSQL_execstate * estate,
*/
if (rec != NULL)
{
+ if (rec->freetup)
+ {
+ heap_freetuple(rec->tup);
+ rec->freetup = false;
+ }
+ if (rec->freetupdesc)
+ {
+ FreeTupleDesc(rec->tupdesc);
+ rec->freetupdesc = false;
+ }
+
if (HeapTupleIsValid(tup))
{
- rec->tup = tup;
- rec->tupdesc = tupdesc;
+ rec->tup = heap_copytuple(tup);
+ rec->tupdesc = CreateTupleDescCopy(tupdesc);
+ rec->freetup = true;
+ rec->freetupdesc = true;
}
else
{
@@ -2766,6 +3304,12 @@ exec_simple_check_plan(PLpgSQL_expr * expr)
TargetEntry *tle;
expr->plan_simple_expr = NULL;
+ /*
+ * Disabled for now until we can execute simple expressions
+ * without collecting all the memory allocations until procedure
+ * returns. 05/17/2001 Jan
+ */
+ return;
/*
* 1. We can only evaluate queries that resulted in one single
@@ -2833,3 +3377,5 @@ exec_set_found(PLpgSQL_execstate * estate, bool state)
var->value = (Datum) state;
var->isnull = false;
}
+
+
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index 13fd9673767..a657512fda1 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.12 2001/03/22 06:16:21 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.13 2001/05/21 14:22:19 wieck Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -387,6 +387,9 @@ static void dump_execsql(PLpgSQL_stmt_execsql * stmt);
static void dump_dynexecute(PLpgSQL_stmt_dynexecute * stmt);
static void dump_dynfors(PLpgSQL_stmt_dynfors * stmt);
static void dump_getdiag(PLpgSQL_stmt_getdiag * stmt);
+static void dump_open(PLpgSQL_stmt_open * stmt);
+static void dump_fetch(PLpgSQL_stmt_fetch * stmt);
+static void dump_close(PLpgSQL_stmt_close * stmt);
static void dump_expr(PLpgSQL_expr * expr);
@@ -450,6 +453,15 @@ dump_stmt(PLpgSQL_stmt * stmt)
case PLPGSQL_STMT_GETDIAG:
dump_getdiag((PLpgSQL_stmt_getdiag *) stmt);
break;
+ case PLPGSQL_STMT_OPEN:
+ dump_open((PLpgSQL_stmt_open *) stmt);
+ break;
+ case PLPGSQL_STMT_FETCH:
+ dump_fetch((PLpgSQL_stmt_fetch *) stmt);
+ break;
+ case PLPGSQL_STMT_CLOSE:
+ dump_close((PLpgSQL_stmt_close *) stmt);
+ break;
default:
elog(ERROR, "plpgsql_dump: unknown cmd_type %d\n", stmt->cmd_type);
break;
@@ -620,6 +632,66 @@ dump_select(PLpgSQL_stmt_select * stmt)
}
static void
+dump_open(PLpgSQL_stmt_open * stmt)
+{
+ dump_ind();
+ printf("OPEN curvar=%d\n", stmt->curvar);
+
+ dump_indent += 2;
+ if (stmt->argquery != NULL)
+ {
+ dump_ind();
+ printf(" arguments = '");
+ dump_expr(stmt->argquery);
+ printf("'\n");
+ }
+ if (stmt->query != NULL)
+ {
+ dump_ind();
+ printf(" query = '");
+ dump_expr(stmt->query);
+ printf("'\n");
+ }
+ if (stmt->dynquery != NULL)
+ {
+ dump_ind();
+ printf(" execute = '");
+ dump_expr(stmt->dynquery);
+ printf("'\n");
+ }
+ dump_indent -= 2;
+
+}
+
+static void
+dump_fetch(PLpgSQL_stmt_fetch * stmt)
+{
+ dump_ind();
+ printf("FETCH curvar=%d\n", stmt->curvar);
+
+ dump_indent += 2;
+ if (stmt->rec != NULL)
+ {
+ dump_ind();
+ printf(" target = %d %s\n", stmt->rec->recno, stmt->rec->refname);
+ }
+ if (stmt->row != NULL)
+ {
+ dump_ind();
+ printf(" target = %d %s\n", stmt->row->rowno, stmt->row->refname);
+ }
+ dump_indent -= 2;
+
+}
+
+static void
+dump_close(PLpgSQL_stmt_close * stmt)
+{
+ dump_ind();
+ printf("CLOSE curvar=%d\n", stmt->curvar);
+}
+
+static void
dump_exit(PLpgSQL_stmt_exit * stmt)
{
dump_ind();
@@ -777,6 +849,25 @@ plpgsql_dumptree(PLpgSQL_function * func)
var->refname, var->datatype->typname,
var->datatype->typoid,
var->datatype->atttypmod);
+ if (var->isconst)
+ printf(" CONSTANT\n");
+ if (var->notnull)
+ printf(" NOT NULL\n");
+ if (var->default_val != NULL)
+ {
+ printf(" DEFAULT ");
+ dump_expr(var->default_val);
+ printf("\n");
+ }
+ if (var->cursor_explicit_expr != NULL)
+ {
+ if (var->cursor_explicit_argrow >= 0)
+ printf(" CURSOR argument row %d\n", var->cursor_explicit_argrow);
+
+ printf(" CURSOR IS ");
+ dump_expr(var->cursor_explicit_expr);
+ printf("\n");
+ }
}
break;
case PLPGSQL_DTYPE_ROW:
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index f364b39e12c..7089144988b 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.13 2001/03/22 04:01:42 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.14 2001/05/21 14:22:19 wieck Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -94,7 +94,10 @@ enum
PLPGSQL_STMT_EXECSQL,
PLPGSQL_STMT_DYNEXECUTE,
PLPGSQL_STMT_DYNFORS,
- PLPGSQL_STMT_GETDIAG
+ PLPGSQL_STMT_GETDIAG,
+ PLPGSQL_STMT_OPEN,
+ PLPGSQL_STMT_FETCH,
+ PLPGSQL_STMT_CLOSE
};
@@ -139,6 +142,7 @@ typedef struct
Oid typoid;
FmgrInfo typinput;
Oid typelem;
+ int16 typlen;
bool typbyval;
int32 atttypmod;
} PLpgSQL_type;
@@ -176,10 +180,12 @@ typedef struct
int isconst;
int notnull;
PLpgSQL_expr *default_val;
+ PLpgSQL_expr *cursor_explicit_expr;
+ int cursor_explicit_argrow;
Datum value;
bool isnull;
- int shouldfree;
+ bool freeval;
} PLpgSQL_var;
@@ -206,6 +212,8 @@ typedef struct
HeapTuple tup;
TupleDesc tupdesc;
+ bool freetup;
+ bool freetupdesc;
} PLpgSQL_rec;
@@ -370,6 +378,36 @@ typedef struct
typedef struct
+{ /* OPEN a curvar */
+ int cmd_type;
+ int lineno;
+ int curvar;
+ PLpgSQL_row *returntype;
+ PLpgSQL_expr *argquery;
+ PLpgSQL_expr *query;
+ PLpgSQL_expr *dynquery;
+} PLpgSQL_stmt_open;
+
+
+typedef struct
+{ /* FETCH curvar INTO statement */
+ int cmd_type;
+ int lineno;
+ PLpgSQL_rec *rec;
+ PLpgSQL_row *row;
+ int curvar;
+} PLpgSQL_stmt_fetch;
+
+
+typedef struct
+{ /* CLOSE curvar */
+ int cmd_type;
+ int lineno;
+ int curvar;
+} PLpgSQL_stmt_close;
+
+
+typedef struct
{ /* EXIT statement */
int cmd_type;
int lineno;
diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l
index 65c5c75faaa..08f9fb9d06f 100644
--- a/src/pl/plpgsql/src/scan.l
+++ b/src/pl/plpgsql/src/scan.l
@@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.11 2001/05/18 21:16:59 wieck Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.12 2001/05/21 14:22:19 wieck Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -93,7 +93,9 @@ alias { return K_ALIAS; }
begin { return K_BEGIN; }
bpchar { return T_BPCHAR; }
char { return T_CHAR; }
+close { return K_CLOSE; }
constant { return K_CONSTANT; }
+cursor { return K_CURSOR; }
debug { return K_DEBUG; }
declare { return K_DECLARE; }
default { return K_DEFAULT; }
@@ -104,16 +106,19 @@ end { return K_END; }
exception { return K_EXCEPTION; }
execute { return K_EXECUTE; }
exit { return K_EXIT; }
+fetch { return K_FETCH; }
for { return K_FOR; }
from { return K_FROM; }
get { return K_GET; }
if { return K_IF; }
in { return K_IN; }
into { return K_INTO; }
+is { return K_IS; }
loop { return K_LOOP; }
not { return K_NOT; }
notice { return K_NOTICE; }
null { return K_NULL; }
+open { return K_OPEN; }
perform { return K_PERFORM; }
raise { return K_RAISE; }
record { return K_RECORD; }