diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index cd7e1d29524ed2d7fae6ecb22ac79042e7d737a7..bc5153b8005417c53434c8d16cc2af48d7ae5672 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 4aa8c475c30922ff7ef4c6003509102f002adeb3..d369827742741ca072c6040ca2297339882a0484 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 e04f97e0ec8322bdd5b833995e95e9cabcb1a360..2616b4af7d44daa12f2b67d9c98cdb9b9747e318 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 1cb9d2a57026ab64918c16a3f0965540989ccb83..5b330d9ea309f21134fb24c1e1d8e4693de4479c 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 b95eaae4e9e19e535111175fd71638dcd5e0b3ed..3a7c7807942ab4376183a35f8ad0b564986a562e 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 00b28a58037f6029cade46ba2f138c2f88459d0e..e56266340017ada91e515d200d224bc7c05a8387 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 1cdd770dfdc0de37f7227d54becd824b4ef1ef96..6a17ec867044a0c4edfee5e8a10441154fa67bae 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 b137367454058ebe2c49773858be81396b010201..9dee73acc97823464d71b0391bb7c5554aff8d67 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 9cfa748241330c4016d3deadc502c29e5f47e0bd..5d93985028672e25de91171e6558fe8f54fe5cc7 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 ba3b1b495fb3bed24a5f437946ae1a66ea772bf0..1ad04739a33faf46362ef414269c374b0985c356 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 13fd96737670ea7e91c314fe961b3c3835ebec59..a657512fda15e3b281e4f729f6456e04ecfb7d65 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; @@ -619,6 +631,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) { @@ -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 f364b39e12c06c95d559b5a319757932bdfe1411..7089144988b98fe6d7bc9fad692169e6cf3370ef 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; @@ -369,6 +377,36 @@ typedef struct } PLpgSQL_stmt_select; +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; diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l index 65c5c75faaa2a992e7a1bcb86a4bb2d2eb556387..08f9fb9d06f6be7d6c19bbdafba4367e9d784590 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; }