8
8
*
9
9
*
10
10
* 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 $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
@@ -1115,7 +1115,8 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
1115
1115
* ExecMakeTableFunctionResult
1116
1116
*
1117
1117
* 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.
1119
1120
*/
1120
1121
Tuplestorestate *
1121
1122
ExecMakeTableFunctionResult (ExprState * funcexpr ,
@@ -1127,6 +1128,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
1127
1128
TupleDesc tupdesc = NULL ;
1128
1129
Oid funcrettype ;
1129
1130
bool returnsTuple ;
1131
+ bool returnsSet = false;
1130
1132
FunctionCallInfoData fcinfo ;
1131
1133
ReturnSetInfo rsinfo ;
1132
1134
HeapTupleData tmptup ;
@@ -1135,6 +1137,31 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
1135
1137
bool direct_function_call ;
1136
1138
bool first_time = true;
1137
1139
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
+
1138
1165
/*
1139
1166
* Normally the passed expression tree will be a FuncExprState, since
1140
1167
* the grammar only allows a function call at the top level of a table
@@ -1165,6 +1192,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
1165
1192
1166
1193
init_fcache (func -> funcid , fcache , econtext -> ecxt_per_query_memory );
1167
1194
}
1195
+ returnsSet = fcache -> func .fn_retset ;
1168
1196
1169
1197
/*
1170
1198
* Evaluate the function's argument list.
@@ -1174,7 +1202,6 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
1174
1202
* the inner loop. So do it in caller context. Perhaps we should
1175
1203
* make a separate context just to hold the evaluated arguments?
1176
1204
*/
1177
- MemSet (& fcinfo , 0 , sizeof (fcinfo ));
1178
1205
fcinfo .flinfo = & (fcache -> func );
1179
1206
argDone = ExecEvalFuncArgs (& fcinfo , fcache -> args , econtext );
1180
1207
/* We don't allow sets in the arguments of the table function */
@@ -1185,7 +1212,8 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
1185
1212
1186
1213
/*
1187
1214
* 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).
1189
1217
*/
1190
1218
if (fcache -> func .fn_strict )
1191
1219
{
@@ -1194,10 +1222,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
1194
1222
for (i = 0 ; i < fcinfo .nargs ; i ++ )
1195
1223
{
1196
1224
if (fcinfo .argnull [i ])
1197
- {
1198
- * returnDesc = NULL ;
1199
- return NULL ;
1200
- }
1225
+ goto no_function_result ;
1201
1226
}
1202
1227
}
1203
1228
}
@@ -1207,33 +1232,11 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
1207
1232
direct_function_call = false;
1208
1233
}
1209
1234
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
-
1232
1235
/*
1233
1236
* Switch to short-lived context for calling the function or
1234
1237
* expression.
1235
1238
*/
1236
- callerContext = MemoryContextSwitchTo (econtext -> ecxt_per_tuple_memory );
1239
+ MemoryContextSwitchTo (econtext -> ecxt_per_tuple_memory );
1237
1240
1238
1241
/*
1239
1242
* Loop to handle the ValuePerCall protocol (which is also the same
@@ -1269,23 +1272,26 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
1269
1272
{
1270
1273
/*
1271
1274
* 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)
1276
1275
*/
1277
1276
if (rsinfo .isDone == ExprEndResult )
1278
1277
break ;
1279
1278
1280
1279
/*
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.
1284
1286
*/
1285
1287
if (returnsTuple && fcinfo .isnull )
1288
+ {
1289
+ if (!returnsSet )
1290
+ break ;
1286
1291
ereport (ERROR ,
1287
1292
(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
+ }
1289
1295
1290
1296
/*
1291
1297
* If first time through, build tupdesc and tuplestore for
@@ -1381,6 +1387,35 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
1381
1387
first_time = false;
1382
1388
}
1383
1389
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
+
1384
1419
MemoryContextSwitchTo (callerContext );
1385
1420
1386
1421
/* The returned pointers are those in rsinfo */
0 commit comments