Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbebaf70

Browse files
committed
Adjust ExecMakeTableFunctionResult to produce a single all-nulls row
when a function that returns a single tuple (not a setof tuple) returnsNULL. This seems to be the most consistent behavior. It would havetaken a bit less code to make it return an empty table (zero rows) butISTM a non-SETOF function ought always return exactly one row. Perbug report from Ivan-Sun1.
1 parentb84788d commitbebaf70

File tree

2 files changed

+78
-50
lines changed

2 files changed

+78
-50
lines changed

‎src/backend/executor/execQual.c

Lines changed: 74 additions & 39 deletions
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
TupleDesctupdesc=NULL;
11281129
Oidfuncrettype;
11291130
boolreturnsTuple;
1131+
boolreturnsSet= false;
11301132
FunctionCallInfoDatafcinfo;
11311133
ReturnSetInforsinfo;
11321134
HeapTupleDatatmptup;
@@ -1135,6 +1137,31 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
11351137
booldirect_function_call;
11361138
boolfirst_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-
returnNULL;
1200-
}
1225+
gotono_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+
intnatts=expectedDesc->natts;
1405+
Datum*nulldatums;
1406+
char*nullflags;
1407+
HeapTupletuple;
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

Lines changed: 4 additions & 11 deletions
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
returnExecStoreTuple(heapTuple,slot,InvalidBuffer,should_free);
111104
}
112105

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp