Enhancement of SPI to get access to portals

- New functions to create a portal using a prepared/saved
  SPI plan or lookup an existing portal by name.
- Functions to fetch/move from/in portals. Results are placed
  in the usual SPI_processed and SPI_tuptable, so the entire
  set of utility functions can be used to gain attribute access.
- Prepared/saved SPI plans now use their own memory context
  and SPI_freeplan(plan) can remove them.
- Tuple result sets (SPI_tuptable) now uses it's own memory
  context and can be free'd by SPI_freetuptable(tuptab).

Enhancement of PL/pgSQL

- Uses generic named portals internally in FOR ... SELECT
  loops to avoid running out of memory on huge result sets.
- Support for CURSOR and REFCURSOR syntax using the new SPI
  functionality. Cursors used internally only need no explicit
  transaction block. Refcursor variables can be used inside
  of explicit transaction block to pass cursors between main
  application and functions.


Jan
This commit is contained in:
Jan Wieck 2001-05-21 14:22:19 +00:00
parent be03eb25f3
commit d27f363e3f
13 changed files with 1521 additions and 121 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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:

View File

@ -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;

View File

@ -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; }