Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit bebaf70

Browse files
committed
Adjust ExecMakeTableFunctionResult to produce a single all-nulls row
when a function that returns a single tuple (not a setof tuple) returns NULL. This seems to be the most consistent behavior. It would have taken a bit less code to make it return an empty table (zero rows) but ISTM a non-SETOF function ought always return exactly one row. Per bug report from Ivan-Sun1.
1 parent b84788d commit bebaf70

File tree

2 files changed

+78
-50
lines changed

2 files changed

+78
-50
lines changed

src/backend/executor/execQual.c

+74-39
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.168 2004/08/29 05:06:42 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.169 2004/09/22 17:41:50 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1115,7 +1115,8 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
11151115
* ExecMakeTableFunctionResult
11161116
*
11171117
* Evaluate a table function, producing a materialized result in a Tuplestore
1118-
* object. (If function returns an empty set, we just return NULL instead.)
1118+
* object. *returnDesc is set to the tupledesc actually returned by the
1119+
* function, or NULL if it didn't provide one.
11191120
*/
11201121
Tuplestorestate *
11211122
ExecMakeTableFunctionResult(ExprState *funcexpr,
@@ -1127,6 +1128,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
11271128
TupleDesc tupdesc = NULL;
11281129
Oid funcrettype;
11291130
bool returnsTuple;
1131+
bool returnsSet = false;
11301132
FunctionCallInfoData fcinfo;
11311133
ReturnSetInfo rsinfo;
11321134
HeapTupleData tmptup;
@@ -1135,6 +1137,31 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
11351137
bool direct_function_call;
11361138
bool first_time = true;
11371139

1140+
callerContext = CurrentMemoryContext;
1141+
1142+
funcrettype = exprType((Node *) funcexpr->expr);
1143+
1144+
returnsTuple = (funcrettype == RECORDOID ||
1145+
get_typtype(funcrettype) == 'c');
1146+
1147+
/*
1148+
* Prepare a resultinfo node for communication. We always do this
1149+
* even if not expecting a set result, so that we can pass
1150+
* expectedDesc. In the generic-expression case, the expression
1151+
* doesn't actually get to see the resultinfo, but set it up anyway
1152+
* because we use some of the fields as our own state variables.
1153+
*/
1154+
MemSet(&fcinfo, 0, sizeof(fcinfo));
1155+
fcinfo.resultinfo = (Node *) &rsinfo;
1156+
rsinfo.type = T_ReturnSetInfo;
1157+
rsinfo.econtext = econtext;
1158+
rsinfo.expectedDesc = expectedDesc;
1159+
rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
1160+
rsinfo.returnMode = SFRM_ValuePerCall;
1161+
/* isDone is filled below */
1162+
rsinfo.setResult = NULL;
1163+
rsinfo.setDesc = NULL;
1164+
11381165
/*
11391166
* Normally the passed expression tree will be a FuncExprState, since
11401167
* the grammar only allows a function call at the top level of a table
@@ -1165,6 +1192,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
11651192

11661193
init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory);
11671194
}
1195+
returnsSet = fcache->func.fn_retset;
11681196

11691197
/*
11701198
* Evaluate the function's argument list.
@@ -1174,7 +1202,6 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
11741202
* the inner loop. So do it in caller context. Perhaps we should
11751203
* make a separate context just to hold the evaluated arguments?
11761204
*/
1177-
MemSet(&fcinfo, 0, sizeof(fcinfo));
11781205
fcinfo.flinfo = &(fcache->func);
11791206
argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
11801207
/* We don't allow sets in the arguments of the table function */
@@ -1185,7 +1212,8 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
11851212

11861213
/*
11871214
* If function is strict, and there are any NULL arguments, skip
1188-
* calling the function and return NULL (actually an empty set).
1215+
* calling the function and act like it returned NULL (or an empty
1216+
* set, in the returns-set case).
11891217
*/
11901218
if (fcache->func.fn_strict)
11911219
{
@@ -1194,10 +1222,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
11941222
for (i = 0; i < fcinfo.nargs; i++)
11951223
{
11961224
if (fcinfo.argnull[i])
1197-
{
1198-
*returnDesc = NULL;
1199-
return NULL;
1200-
}
1225+
goto no_function_result;
12011226
}
12021227
}
12031228
}
@@ -1207,33 +1232,11 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
12071232
direct_function_call = false;
12081233
}
12091234

1210-
funcrettype = exprType((Node *) funcexpr->expr);
1211-
1212-
returnsTuple = (funcrettype == RECORDOID ||
1213-
get_typtype(funcrettype) == 'c');
1214-
1215-
/*
1216-
* Prepare a resultinfo node for communication. We always do this
1217-
* even if not expecting a set result, so that we can pass
1218-
* expectedDesc. In the generic-expression case, the expression
1219-
* doesn't actually get to see the resultinfo, but set it up anyway
1220-
* because we use some of the fields as our own state variables.
1221-
*/
1222-
fcinfo.resultinfo = (Node *) &rsinfo;
1223-
rsinfo.type = T_ReturnSetInfo;
1224-
rsinfo.econtext = econtext;
1225-
rsinfo.expectedDesc = expectedDesc;
1226-
rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
1227-
rsinfo.returnMode = SFRM_ValuePerCall;
1228-
/* isDone is filled below */
1229-
rsinfo.setResult = NULL;
1230-
rsinfo.setDesc = NULL;
1231-
12321235
/*
12331236
* Switch to short-lived context for calling the function or
12341237
* expression.
12351238
*/
1236-
callerContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
1239+
MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
12371240

12381241
/*
12391242
* Loop to handle the ValuePerCall protocol (which is also the same
@@ -1269,23 +1272,26 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
12691272
{
12701273
/*
12711274
* Check for end of result set.
1272-
*
1273-
* Note: if function returns an empty set, we don't build a
1274-
* tupdesc or tuplestore (since we can't get a tupdesc in the
1275-
* function-returning-tuple case)
12761275
*/
12771276
if (rsinfo.isDone == ExprEndResult)
12781277
break;
12791278

12801279
/*
1281-
* Can't do anything useful with NULL rowtype values.
1282-
* Currently we raise an error, but another alternative is to
1283-
* just ignore the result and "continue" to get another row.
1280+
* Can't do anything very useful with NULL rowtype values.
1281+
* For a function returning set, we consider this a protocol
1282+
* violation (but another alternative would be to just ignore
1283+
* the result and "continue" to get another row). For a function
1284+
* not returning set, we fall out of the loop; we'll cons up
1285+
* an all-nulls result row below.
12841286
*/
12851287
if (returnsTuple && fcinfo.isnull)
1288+
{
1289+
if (!returnsSet)
1290+
break;
12861291
ereport(ERROR,
12871292
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1288-
errmsg("function returning row cannot return null value")));
1293+
errmsg("function returning set of rows cannot return null value")));
1294+
}
12891295

12901296
/*
12911297
* If first time through, build tupdesc and tuplestore for
@@ -1381,6 +1387,35 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
13811387
first_time = false;
13821388
}
13831389

1390+
no_function_result:
1391+
1392+
/*
1393+
* If we got nothing from the function (ie, an empty-set or NULL result),
1394+
* we have to create the tuplestore to return, and if it's a
1395+
* non-set-returning function then insert a single all-nulls row.
1396+
*/
1397+
if (rsinfo.setResult == NULL)
1398+
{
1399+
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
1400+
tupstore = tuplestore_begin_heap(true, false, work_mem);
1401+
rsinfo.setResult = tupstore;
1402+
if (!returnsSet)
1403+
{
1404+
int natts = expectedDesc->natts;
1405+
Datum *nulldatums;
1406+
char *nullflags;
1407+
HeapTuple tuple;
1408+
1409+
MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
1410+
nulldatums = (Datum *) palloc0(natts * sizeof(Datum));
1411+
nullflags = (char *) palloc(natts * sizeof(char));
1412+
memset(nullflags, 'n', natts * sizeof(char));
1413+
tuple = heap_formtuple(expectedDesc, nulldatums, nullflags);
1414+
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
1415+
tuplestore_puttuple(tupstore, tuple);
1416+
}
1417+
}
1418+
13841419
MemoryContextSwitchTo(callerContext);
13851420

13861421
/* The returned pointers are those in rsinfo */

src/backend/executor/nodeFunctionscan.c

+4-11
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.26 2004/08/29 04:12:31 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.27 2004/09/22 17:41:51 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -96,17 +96,10 @@ FunctionNext(FunctionScanState *node)
9696
/*
9797
* Get the next tuple from tuplestore. Return NULL if no more tuples.
9898
*/
99+
heapTuple = tuplestore_getheaptuple(tuplestorestate,
100+
ScanDirectionIsForward(direction),
101+
&should_free);
99102
slot = node->ss.ss_ScanTupleSlot;
100-
if (tuplestorestate)
101-
heapTuple = tuplestore_getheaptuple(tuplestorestate,
102-
ScanDirectionIsForward(direction),
103-
&should_free);
104-
else
105-
{
106-
heapTuple = NULL;
107-
should_free = false;
108-
}
109-
110103
return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
111104
}
112105

0 commit comments

Comments
 (0)