mirror of
https://github.com/postgres/postgres.git
synced 2025-11-29 23:43:17 +03:00
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.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$Header: /cvsroot/pgsql/doc/src/sgml/spi.sgml,v 1.23 2002/09/02 06:11:42 momjian Exp $
|
$Header: /cvsroot/pgsql/doc/src/sgml/spi.sgml,v 1.23.2.1 2003/01/21 22:06:36 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
|
|||||||
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 > 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
|
|||||||
<TITLE>Structures
|
<TITLE>Structures
|
||||||
</TITLE>
|
</TITLE>
|
||||||
<Para>
|
<Para>
|
||||||
If <ReturnValue>SPI_OK_SELECT</ReturnValue> is returned and SPI_processed > 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.75 2002/10/14 23:49:20 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.75.2.1 2003/01/21 22:06:36 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@@ -882,13 +882,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;
|
||||||
@@ -899,15 +899,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,
|
||||||
@@ -920,11 +920,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)
|
||||||
{
|
{
|
||||||
@@ -933,13 +958,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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1457,19 +1480,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 @@
|
|||||||
* 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.49.2.1 2003/01/21 22:06:36 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@@ -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 @@
|
|||||||
* 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.22.2.1 2003/01/21 22:06:36 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@@ -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 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.65 2002/10/19 22:10:58 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.65.2.1 2003/01/21 22:06:36 tgl Exp $
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@@ -1337,15 +1337,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 */
|
||||||
|
|
||||||
@@ -1459,6 +1459,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;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1467,7 +1468,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;
|
||||||
}
|
}
|
||||||
@@ -1475,7 +1476,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);
|
||||||
|
|
||||||
@@ -1609,6 +1609,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;
|
||||||
@@ -2351,15 +2353,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;
|
||||||
|
|
||||||
@@ -2758,8 +2760,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
|
||||||
@@ -2768,7 +2770,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3339,8 +3341,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)
|
||||||
{
|
{
|
||||||
@@ -3358,13 +3360,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3381,6 +3404,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)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user