Commit a4482f4c authored by Tom Lane's avatar Tom Lane

Fix coredump problem in plpgsql's RETURN NEXT. When a SELECT INTO

that's selecting into a RECORD variable returns zero rows, make it
assign an all-nulls row to the RECORD; this is consistent with what
happens when the SELECT INTO target is not a RECORD.  In support of
this, tweak the SPI code so that a valid tuple descriptor is returned
even when a SPI select returns no rows.
parent f8d8266a
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/spi.sgml,v 1.24 2002/12/30 22:10:53 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/spi.sgml,v 1.25 2003/01/21 22:06:11 tgl Exp $
--> -->
<Chapter id="spi"> <Chapter id="spi">
...@@ -476,7 +476,7 @@ You may pass multiple queries in one string or query string may be ...@@ -476,7 +476,7 @@ You may pass multiple queries in one string or query string may be
The actual number of tuples for which the (last) query was executed is The actual number of tuples for which the (last) query was executed is
returned in the global variable SPI_processed (if not <ReturnValue>SPI_OK_UTILITY</ReturnValue>). returned in the global variable SPI_processed (if not <ReturnValue>SPI_OK_UTILITY</ReturnValue>).
If <ReturnValue>SPI_OK_SELECT</ReturnValue> is returned and SPI_processed &gt; 0 then you may use global If <ReturnValue>SPI_OK_SELECT</ReturnValue> is returned then you may use global
pointer SPITupleTable *SPI_tuptable to access the result tuples. pointer SPITupleTable *SPI_tuptable to access the result tuples.
</Para> </Para>
...@@ -517,7 +517,7 @@ You may pass multiple queries in one string or query string may be ...@@ -517,7 +517,7 @@ You may pass multiple queries in one string or query string may be
<TITLE>Structures <TITLE>Structures
</TITLE> </TITLE>
<Para> <Para>
If <ReturnValue>SPI_OK_SELECT</ReturnValue> is returned and SPI_processed &gt; 0 then you may use the global If <ReturnValue>SPI_OK_SELECT</ReturnValue> is returned then you may use the global
pointer SPITupleTable *SPI_tuptable to access the selected tuples. pointer SPITupleTable *SPI_tuptable to access the selected tuples.
</Para> </Para>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.83 2002/12/30 22:10:54 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.84 2003/01/21 22:06:12 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -874,13 +874,13 @@ SPI_cursor_close(Portal portal) ...@@ -874,13 +874,13 @@ SPI_cursor_close(Portal portal)
/* =================== private functions =================== */ /* =================== private functions =================== */
/* /*
* spi_printtup * spi_dest_setup
* store tuple retrieved by Executor into SPITupleTable * Initialize to receive tuples from Executor into SPITupleTable
* of current SPI procedure * of current SPI procedure
*
*/ */
void void
spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self) spi_dest_setup(DestReceiver *self, int operation,
const char *portalName, TupleDesc typeinfo)
{ {
SPITupleTable *tuptable; SPITupleTable *tuptable;
MemoryContext oldcxt; MemoryContext oldcxt;
...@@ -891,15 +891,15 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self) ...@@ -891,15 +891,15 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
* _SPI_connected * _SPI_connected
*/ */
if (_SPI_curid != _SPI_connected || _SPI_connected < 0) if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
elog(FATAL, "SPI: improper call to spi_printtup"); elog(FATAL, "SPI: improper call to spi_dest_setup");
if (_SPI_current != &(_SPI_stack[_SPI_curid])) if (_SPI_current != &(_SPI_stack[_SPI_curid]))
elog(FATAL, "SPI: stack corrupted in spi_printtup"); elog(FATAL, "SPI: stack corrupted in spi_dest_setup");
if (_SPI_current->tuptable != NULL)
elog(FATAL, "SPI: improper call to spi_dest_setup");
oldcxt = _SPI_procmem(); /* switch to procedure memory context */ oldcxt = _SPI_procmem(); /* switch to procedure memory context */
tuptable = _SPI_current->tuptable;
if (tuptable == NULL)
{
tuptabcxt = AllocSetContextCreate(CurrentMemoryContext, tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
"SPI TupTable", "SPI TupTable",
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_MINSIZE,
...@@ -912,11 +912,36 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self) ...@@ -912,11 +912,36 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
tuptable->tuptabcxt = tuptabcxt; tuptable->tuptabcxt = tuptabcxt;
tuptable->alloced = tuptable->free = 128; tuptable->alloced = tuptable->free = 128;
tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple)); tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
tuptable->tupdesc = CreateTupleDescCopy(tupdesc); tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
}
else MemoryContextSwitchTo(oldcxt);
{ }
MemoryContextSwitchTo(tuptable->tuptabcxt);
/*
* spi_printtup
* store tuple retrieved by Executor into SPITupleTable
* of current SPI procedure
*/
void
spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
{
SPITupleTable *tuptable;
MemoryContext oldcxt;
/*
* When called by Executor _SPI_curid expected to be equal to
* _SPI_connected
*/
if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
elog(FATAL, "SPI: improper call to spi_printtup");
if (_SPI_current != &(_SPI_stack[_SPI_curid]))
elog(FATAL, "SPI: stack corrupted in spi_printtup");
tuptable = _SPI_current->tuptable;
if (tuptable == NULL)
elog(FATAL, "SPI: improper call to spi_printtup");
oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
if (tuptable->free == 0) if (tuptable->free == 0)
{ {
...@@ -925,13 +950,11 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self) ...@@ -925,13 +950,11 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
tuptable->vals = (HeapTuple *) repalloc(tuptable->vals, tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
tuptable->alloced * sizeof(HeapTuple)); tuptable->alloced * sizeof(HeapTuple));
} }
}
tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple); tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
(tuptable->free)--; (tuptable->free)--;
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
return;
} }
/* /*
...@@ -1448,19 +1471,10 @@ _SPI_checktuples(void) ...@@ -1448,19 +1471,10 @@ _SPI_checktuples(void)
SPITupleTable *tuptable = _SPI_current->tuptable; SPITupleTable *tuptable = _SPI_current->tuptable;
bool failed = false; bool failed = false;
if (processed == 0) if (tuptable == NULL) /* spi_dest_setup was not called */
{
if (tuptable != NULL)
failed = true;
}
else
{
/* some tuples were processed */
if (tuptable == NULL) /* spi_printtup was not called */
failed = true; failed = true;
else if (processed != (tuptable->alloced - tuptable->free)) else if (processed != (tuptable->alloced - tuptable->free))
failed = true; failed = true;
}
return failed; return failed;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.49 2002/06/20 20:29:36 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.50 2003/01/21 22:06:12 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -64,7 +64,7 @@ static DestReceiver debugtupDR = { ...@@ -64,7 +64,7 @@ static DestReceiver debugtupDR = {
debugtup, debugSetup, donothingCleanup debugtup, debugSetup, donothingCleanup
}; };
static DestReceiver spi_printtupDR = { static DestReceiver spi_printtupDR = {
spi_printtup, donothingSetup, donothingCleanup spi_printtup, spi_dest_setup, donothingCleanup
}; };
/* ---------------- /* ----------------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: printtup.h,v 1.22 2002/09/04 20:31:37 momjian Exp $ * $Id: printtup.h,v 1.23 2003/01/21 22:06:12 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -23,7 +23,9 @@ extern void debugSetup(DestReceiver *self, int operation, ...@@ -23,7 +23,9 @@ extern void debugSetup(DestReceiver *self, int operation,
extern void debugtup(HeapTuple tuple, TupleDesc typeinfo, extern void debugtup(HeapTuple tuple, TupleDesc typeinfo,
DestReceiver *self); DestReceiver *self);
/* XXX this one is really in executor/spi.c */ /* XXX these are really in executor/spi.c */
extern void spi_dest_setup(DestReceiver *self, int operation,
const char *portalName, TupleDesc typeinfo);
extern void spi_printtup(HeapTuple tuple, TupleDesc tupdesc, extern void spi_printtup(HeapTuple tuple, TupleDesc tupdesc,
DestReceiver *self); DestReceiver *self);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.76 2002/12/17 15:45:01 tgl Exp $ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.77 2003/01/21 22:06:12 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -1356,15 +1356,15 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt) ...@@ -1356,15 +1356,15 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
exec_run_select(estate, stmt->query, 0, &portal); exec_run_select(estate, stmt->query, 0, &portal);
SPI_cursor_fetch(portal, true, 10); SPI_cursor_fetch(portal, true, 10);
n = SPI_processed;
tuptab = SPI_tuptable; tuptab = SPI_tuptable;
n = SPI_processed;
/* /*
* If the query didn't return any rows, set the target to NULL and * If the query didn't return any rows, set the target to NULL and
* return with FOUND = false. * return with FOUND = false.
*/ */
if (n == 0) if (n == 0)
exec_move_row(estate, rec, row, NULL, NULL); exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
else else
found = true; /* processed at least one tuple */ found = true; /* processed at least one tuple */
...@@ -1478,6 +1478,7 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt) ...@@ -1478,6 +1478,7 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
* Run the query * Run the query
*/ */
exec_run_select(estate, stmt->query, 1, NULL); exec_run_select(estate, stmt->query, 1, NULL);
tuptab = estate->eval_tuptable;
n = estate->eval_processed; n = estate->eval_processed;
/* /*
...@@ -1486,7 +1487,7 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt) ...@@ -1486,7 +1487,7 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
*/ */
if (n == 0) if (n == 0)
{ {
exec_move_row(estate, rec, row, NULL, NULL); exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
exec_eval_cleanup(estate); exec_eval_cleanup(estate);
return PLPGSQL_RC_OK; return PLPGSQL_RC_OK;
} }
...@@ -1494,7 +1495,6 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt) ...@@ -1494,7 +1495,6 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
/* /*
* Put the result into the target and set found to true * Put the result into the target and set found to true
*/ */
tuptab = estate->eval_tuptable;
exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc); exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc);
exec_set_found(estate, true); exec_set_found(estate, true);
...@@ -1627,6 +1627,8 @@ exec_stmt_return_next(PLpgSQL_execstate * estate, ...@@ -1627,6 +1627,8 @@ exec_stmt_return_next(PLpgSQL_execstate * estate,
{ {
PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]); PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
if (!HeapTupleIsValid(rec->tup))
elog(ERROR, "record \"%s\" is unassigned yet", rec->refname);
if (!compatible_tupdesc(tupdesc, rec->tupdesc)) if (!compatible_tupdesc(tupdesc, rec->tupdesc))
elog(ERROR, "Wrong record type supplied in RETURN NEXT"); elog(ERROR, "Wrong record type supplied in RETURN NEXT");
tuple = rec->tup; tuple = rec->tup;
...@@ -2369,15 +2371,15 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt) ...@@ -2369,15 +2371,15 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
* Fetch the initial 10 tuples * Fetch the initial 10 tuples
*/ */
SPI_cursor_fetch(portal, true, 10); SPI_cursor_fetch(portal, true, 10);
n = SPI_processed;
tuptab = SPI_tuptable; tuptab = SPI_tuptable;
n = SPI_processed;
/* /*
* If the query didn't return any rows, set the target to NULL and * If the query didn't return any rows, set the target to NULL and
* return with FOUND = false. * return with FOUND = false.
*/ */
if (n == 0) if (n == 0)
exec_move_row(estate, rec, row, NULL, NULL); exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
else else
found = true; found = true;
...@@ -2776,8 +2778,8 @@ exec_stmt_fetch(PLpgSQL_execstate * estate, PLpgSQL_stmt_fetch * stmt) ...@@ -2776,8 +2778,8 @@ exec_stmt_fetch(PLpgSQL_execstate * estate, PLpgSQL_stmt_fetch * stmt)
* ---------- * ----------
*/ */
SPI_cursor_fetch(portal, true, 1); SPI_cursor_fetch(portal, true, 1);
n = SPI_processed;
tuptab = SPI_tuptable; tuptab = SPI_tuptable;
n = SPI_processed;
/* ---------- /* ----------
* If the FETCH didn't return a row, set the target * If the FETCH didn't return a row, set the target
...@@ -2786,7 +2788,7 @@ exec_stmt_fetch(PLpgSQL_execstate * estate, PLpgSQL_stmt_fetch * stmt) ...@@ -2786,7 +2788,7 @@ exec_stmt_fetch(PLpgSQL_execstate * estate, PLpgSQL_stmt_fetch * stmt)
*/ */
if (n == 0) if (n == 0)
{ {
exec_move_row(estate, rec, row, NULL, NULL); exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
return PLPGSQL_RC_OK; return PLPGSQL_RC_OK;
} }
...@@ -3353,8 +3355,8 @@ exec_move_row(PLpgSQL_execstate * estate, ...@@ -3353,8 +3355,8 @@ exec_move_row(PLpgSQL_execstate * estate,
HeapTuple tup, TupleDesc tupdesc) HeapTuple tup, TupleDesc tupdesc)
{ {
/* /*
* Record is simple - just put the tuple and its descriptor into the * Record is simple - just copy the tuple and its descriptor into the
* record * record variable
*/ */
if (rec != NULL) if (rec != NULL)
{ {
...@@ -3372,13 +3374,34 @@ exec_move_row(PLpgSQL_execstate * estate, ...@@ -3372,13 +3374,34 @@ exec_move_row(PLpgSQL_execstate * estate,
if (HeapTupleIsValid(tup)) if (HeapTupleIsValid(tup))
{ {
rec->tup = heap_copytuple(tup); rec->tup = heap_copytuple(tup);
rec->tupdesc = CreateTupleDescCopy(tupdesc);
rec->freetup = true; rec->freetup = true;
rec->freetupdesc = true; }
else if (tupdesc)
{
/* If we have a tupdesc but no data, form an all-nulls tuple */
char *nulls;
/* +1 to avoid possible palloc(0) if no attributes */
nulls = (char *) palloc(tupdesc->natts * sizeof(char) + 1);
memset(nulls, 'n', tupdesc->natts * sizeof(char));
rec->tup = heap_formtuple(tupdesc, NULL, nulls);
rec->freetup = true;
pfree(nulls);
} }
else else
{ {
rec->tup = NULL; rec->tup = NULL;
}
if (tupdesc)
{
rec->tupdesc = CreateTupleDescCopy(tupdesc);
rec->freetupdesc = true;
}
else
{
rec->tupdesc = NULL; rec->tupdesc = NULL;
} }
...@@ -3395,6 +3418,9 @@ exec_move_row(PLpgSQL_execstate * estate, ...@@ -3395,6 +3418,9 @@ exec_move_row(PLpgSQL_execstate * estate,
* table, or it might have fewer if the table has had columns added by * table, or it might have fewer if the table has had columns added by
* ALTER TABLE. Ignore extra columns and assume NULL for missing * ALTER TABLE. Ignore extra columns and assume NULL for missing
* columns, the same as heap_getattr would do. * columns, the same as heap_getattr would do.
*
* If we have no tuple data at all, we'll assign NULL to all columns
* of the row variable.
*/ */
if (row != NULL) if (row != NULL)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment