Skip to content

Commit a4482f4

Browse files
committed
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.
1 parent f8d8266 commit a4482f4

File tree

5 files changed

+105
-63
lines changed

5 files changed

+105
-63
lines changed

doc/src/sgml/spi.sgml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$Header: /cvsroot/pgsql/doc/src/sgml/spi.sgml,v 1.24 2002/12/30 22:10:53 tgl Exp $
2+
$Header: /cvsroot/pgsql/doc/src/sgml/spi.sgml,v 1.25 2003/01/21 22:06:11 tgl Exp $
33
-->
44

55
<Chapter id="spi">
@@ -476,7 +476,7 @@ You may pass multiple queries in one string or query string may be
476476
The actual number of tuples for which the (last) query was executed is
477477
returned in the global variable SPI_processed (if not <ReturnValue>SPI_OK_UTILITY</ReturnValue>).
478478

479-
If <ReturnValue>SPI_OK_SELECT</ReturnValue> is returned and SPI_processed &gt; 0 then you may use global
479+
If <ReturnValue>SPI_OK_SELECT</ReturnValue> is returned then you may use global
480480
pointer SPITupleTable *SPI_tuptable to access the result tuples.
481481
</Para>
482482

@@ -517,7 +517,7 @@ You may pass multiple queries in one string or query string may be
517517
<TITLE>Structures
518518
</TITLE>
519519
<Para>
520-
If <ReturnValue>SPI_OK_SELECT</ReturnValue> is returned and SPI_processed &gt; 0 then you may use the global
520+
If <ReturnValue>SPI_OK_SELECT</ReturnValue> is returned then you may use the global
521521
pointer SPITupleTable *SPI_tuptable to access the selected tuples.
522522
</Para>
523523

src/backend/executor/spi.c

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.83 2002/12/30 22:10:54 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.84 2003/01/21 22:06:12 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -873,18 +873,60 @@ SPI_cursor_close(Portal portal)
873873

874874
/* =================== private functions =================== */
875875

876+
/*
877+
* spi_dest_setup
878+
* Initialize to receive tuples from Executor into SPITupleTable
879+
* of current SPI procedure
880+
*/
881+
void
882+
spi_dest_setup(DestReceiver *self, int operation,
883+
const char *portalName, TupleDesc typeinfo)
884+
{
885+
SPITupleTable *tuptable;
886+
MemoryContext oldcxt;
887+
MemoryContext tuptabcxt;
888+
889+
/*
890+
* When called by Executor _SPI_curid expected to be equal to
891+
* _SPI_connected
892+
*/
893+
if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
894+
elog(FATAL, "SPI: improper call to spi_dest_setup");
895+
if (_SPI_current != &(_SPI_stack[_SPI_curid]))
896+
elog(FATAL, "SPI: stack corrupted in spi_dest_setup");
897+
898+
if (_SPI_current->tuptable != NULL)
899+
elog(FATAL, "SPI: improper call to spi_dest_setup");
900+
901+
oldcxt = _SPI_procmem(); /* switch to procedure memory context */
902+
903+
tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
904+
"SPI TupTable",
905+
ALLOCSET_DEFAULT_MINSIZE,
906+
ALLOCSET_DEFAULT_INITSIZE,
907+
ALLOCSET_DEFAULT_MAXSIZE);
908+
MemoryContextSwitchTo(tuptabcxt);
909+
910+
_SPI_current->tuptable = tuptable = (SPITupleTable *)
911+
palloc(sizeof(SPITupleTable));
912+
tuptable->tuptabcxt = tuptabcxt;
913+
tuptable->alloced = tuptable->free = 128;
914+
tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
915+
tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
916+
917+
MemoryContextSwitchTo(oldcxt);
918+
}
919+
876920
/*
877921
* spi_printtup
878922
* store tuple retrieved by Executor into SPITupleTable
879923
* of current SPI procedure
880-
*
881924
*/
882925
void
883926
spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
884927
{
885928
SPITupleTable *tuptable;
886929
MemoryContext oldcxt;
887-
MemoryContext tuptabcxt;
888930

889931
/*
890932
* When called by Executor _SPI_curid expected to be equal to
@@ -895,43 +937,24 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
895937
if (_SPI_current != &(_SPI_stack[_SPI_curid]))
896938
elog(FATAL, "SPI: stack corrupted in spi_printtup");
897939

898-
oldcxt = _SPI_procmem(); /* switch to procedure memory context */
899-
900940
tuptable = _SPI_current->tuptable;
901941
if (tuptable == NULL)
902-
{
903-
tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
904-
"SPI TupTable",
905-
ALLOCSET_DEFAULT_MINSIZE,
906-
ALLOCSET_DEFAULT_INITSIZE,
907-
ALLOCSET_DEFAULT_MAXSIZE);
908-
MemoryContextSwitchTo(tuptabcxt);
909-
910-
_SPI_current->tuptable = tuptable = (SPITupleTable *)
911-
palloc(sizeof(SPITupleTable));
912-
tuptable->tuptabcxt = tuptabcxt;
913-
tuptable->alloced = tuptable->free = 128;
914-
tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
915-
tuptable->tupdesc = CreateTupleDescCopy(tupdesc);
916-
}
917-
else
918-
{
919-
MemoryContextSwitchTo(tuptable->tuptabcxt);
942+
elog(FATAL, "SPI: improper call to spi_printtup");
920943

921-
if (tuptable->free == 0)
922-
{
923-
tuptable->free = 256;
924-
tuptable->alloced += tuptable->free;
925-
tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
944+
oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
945+
946+
if (tuptable->free == 0)
947+
{
948+
tuptable->free = 256;
949+
tuptable->alloced += tuptable->free;
950+
tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
926951
tuptable->alloced * sizeof(HeapTuple));
927-
}
928952
}
929953

930954
tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
931955
(tuptable->free)--;
932956

933957
MemoryContextSwitchTo(oldcxt);
934-
return;
935958
}
936959

937960
/*
@@ -1448,19 +1471,10 @@ _SPI_checktuples(void)
14481471
SPITupleTable *tuptable = _SPI_current->tuptable;
14491472
bool failed = false;
14501473

1451-
if (processed == 0)
1452-
{
1453-
if (tuptable != NULL)
1454-
failed = true;
1455-
}
1456-
else
1457-
{
1458-
/* some tuples were processed */
1459-
if (tuptable == NULL) /* spi_printtup was not called */
1460-
failed = true;
1461-
else if (processed != (tuptable->alloced - tuptable->free))
1462-
failed = true;
1463-
}
1474+
if (tuptable == NULL) /* spi_dest_setup was not called */
1475+
failed = true;
1476+
else if (processed != (tuptable->alloced - tuptable->free))
1477+
failed = true;
14641478

14651479
return failed;
14661480
}

src/backend/tcop/dest.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.49 2002/06/20 20:29:36 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.50 2003/01/21 22:06:12 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -64,7 +64,7 @@ static DestReceiver debugtupDR = {
6464
debugtup, debugSetup, donothingCleanup
6565
};
6666
static DestReceiver spi_printtupDR = {
67-
spi_printtup, donothingSetup, donothingCleanup
67+
spi_printtup, spi_dest_setup, donothingCleanup
6868
};
6969

7070
/* ----------------

src/include/access/printtup.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $Id: printtup.h,v 1.22 2002/09/04 20:31:37 momjian Exp $
10+
* $Id: printtup.h,v 1.23 2003/01/21 22:06:12 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -23,7 +23,9 @@ extern void debugSetup(DestReceiver *self, int operation,
2323
extern void debugtup(HeapTuple tuple, TupleDesc typeinfo,
2424
DestReceiver *self);
2525

26-
/* XXX this one is really in executor/spi.c */
26+
/* XXX these are really in executor/spi.c */
27+
extern void spi_dest_setup(DestReceiver *self, int operation,
28+
const char *portalName, TupleDesc typeinfo);
2729
extern void spi_printtup(HeapTuple tuple, TupleDesc tupdesc,
2830
DestReceiver *self);
2931

src/pl/plpgsql/src/pl_exec.c

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* procedural language
44
*
55
* IDENTIFICATION
6-
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.76 2002/12/17 15:45:01 tgl Exp $
6+
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.77 2003/01/21 22:06:12 tgl Exp $
77
*
88
* This software is copyrighted by Jan Wieck - Hamburg.
99
*
@@ -1356,15 +1356,15 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
13561356
exec_run_select(estate, stmt->query, 0, &portal);
13571357

13581358
SPI_cursor_fetch(portal, true, 10);
1359-
n = SPI_processed;
13601359
tuptab = SPI_tuptable;
1360+
n = SPI_processed;
13611361

13621362
/*
13631363
* If the query didn't return any rows, set the target to NULL and
13641364
* return with FOUND = false.
13651365
*/
13661366
if (n == 0)
1367-
exec_move_row(estate, rec, row, NULL, NULL);
1367+
exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
13681368
else
13691369
found = true; /* processed at least one tuple */
13701370

@@ -1478,6 +1478,7 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
14781478
* Run the query
14791479
*/
14801480
exec_run_select(estate, stmt->query, 1, NULL);
1481+
tuptab = estate->eval_tuptable;
14811482
n = estate->eval_processed;
14821483

14831484
/*
@@ -1486,15 +1487,14 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
14861487
*/
14871488
if (n == 0)
14881489
{
1489-
exec_move_row(estate, rec, row, NULL, NULL);
1490+
exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
14901491
exec_eval_cleanup(estate);
14911492
return PLPGSQL_RC_OK;
14921493
}
14931494

14941495
/*
14951496
* Put the result into the target and set found to true
14961497
*/
1497-
tuptab = estate->eval_tuptable;
14981498
exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc);
14991499
exec_set_found(estate, true);
15001500

@@ -1627,6 +1627,8 @@ exec_stmt_return_next(PLpgSQL_execstate * estate,
16271627
{
16281628
PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
16291629

1630+
if (!HeapTupleIsValid(rec->tup))
1631+
elog(ERROR, "record \"%s\" is unassigned yet", rec->refname);
16301632
if (!compatible_tupdesc(tupdesc, rec->tupdesc))
16311633
elog(ERROR, "Wrong record type supplied in RETURN NEXT");
16321634
tuple = rec->tup;
@@ -2369,15 +2371,15 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
23692371
* Fetch the initial 10 tuples
23702372
*/
23712373
SPI_cursor_fetch(portal, true, 10);
2372-
n = SPI_processed;
23732374
tuptab = SPI_tuptable;
2375+
n = SPI_processed;
23742376

23752377
/*
23762378
* If the query didn't return any rows, set the target to NULL and
23772379
* return with FOUND = false.
23782380
*/
23792381
if (n == 0)
2380-
exec_move_row(estate, rec, row, NULL, NULL);
2382+
exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
23812383
else
23822384
found = true;
23832385

@@ -2776,8 +2778,8 @@ exec_stmt_fetch(PLpgSQL_execstate * estate, PLpgSQL_stmt_fetch * stmt)
27762778
* ----------
27772779
*/
27782780
SPI_cursor_fetch(portal, true, 1);
2779-
n = SPI_processed;
27802781
tuptab = SPI_tuptable;
2782+
n = SPI_processed;
27812783

27822784
/* ----------
27832785
* 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)
27862788
*/
27872789
if (n == 0)
27882790
{
2789-
exec_move_row(estate, rec, row, NULL, NULL);
2791+
exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
27902792
return PLPGSQL_RC_OK;
27912793
}
27922794

@@ -3353,8 +3355,8 @@ exec_move_row(PLpgSQL_execstate * estate,
33533355
HeapTuple tup, TupleDesc tupdesc)
33543356
{
33553357
/*
3356-
* Record is simple - just put the tuple and its descriptor into the
3357-
* record
3358+
* Record is simple - just copy the tuple and its descriptor into the
3359+
* record variable
33583360
*/
33593361
if (rec != NULL)
33603362
{
@@ -3372,13 +3374,34 @@ exec_move_row(PLpgSQL_execstate * estate,
33723374
if (HeapTupleIsValid(tup))
33733375
{
33743376
rec->tup = heap_copytuple(tup);
3375-
rec->tupdesc = CreateTupleDescCopy(tupdesc);
33763377
rec->freetup = true;
3377-
rec->freetupdesc = true;
3378+
}
3379+
else if (tupdesc)
3380+
{
3381+
/* If we have a tupdesc but no data, form an all-nulls tuple */
3382+
char *nulls;
3383+
3384+
/* +1 to avoid possible palloc(0) if no attributes */
3385+
nulls = (char *) palloc(tupdesc->natts * sizeof(char) + 1);
3386+
memset(nulls, 'n', tupdesc->natts * sizeof(char));
3387+
3388+
rec->tup = heap_formtuple(tupdesc, NULL, nulls);
3389+
rec->freetup = true;
3390+
3391+
pfree(nulls);
33783392
}
33793393
else
33803394
{
33813395
rec->tup = NULL;
3396+
}
3397+
3398+
if (tupdesc)
3399+
{
3400+
rec->tupdesc = CreateTupleDescCopy(tupdesc);
3401+
rec->freetupdesc = true;
3402+
}
3403+
else
3404+
{
33823405
rec->tupdesc = NULL;
33833406
}
33843407

@@ -3395,6 +3418,9 @@ exec_move_row(PLpgSQL_execstate * estate,
33953418
* table, or it might have fewer if the table has had columns added by
33963419
* ALTER TABLE. Ignore extra columns and assume NULL for missing
33973420
* columns, the same as heap_getattr would do.
3421+
*
3422+
* If we have no tuple data at all, we'll assign NULL to all columns
3423+
* of the row variable.
33983424
*/
33993425
if (row != NULL)
34003426
{

0 commit comments

Comments
 (0)