@@ -2229,89 +2229,96 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
2229
2229
break ;
2230
2230
2231
2231
/*
2232
- * Can't do anything very useful with NULL rowtype values. For a
2233
- * function returning set, we consider this a protocol violation
2234
- * (but another alternative would be to just ignore the result and
2235
- * "continue" to get another row). For a function not returning
2236
- * set, we fall out of the loop; we'll cons up an all-nulls result
2237
- * row below.
2238
- */
2239
- if (returnsTuple && fcinfo .isnull )
2240
- {
2241
- if (!returnsSet )
2242
- break ;
2243
- ereport (ERROR ,
2244
- (errcode (ERRCODE_NULL_VALUE_NOT_ALLOWED ),
2245
- errmsg ("function returning set of rows cannot return null value" )));
2246
- }
2247
-
2248
- /*
2249
- * If first time through, build tupdesc and tuplestore for result
2232
+ * If first time through, build tuplestore for result. For a
2233
+ * scalar function result type, also make a suitable tupdesc.
2250
2234
*/
2251
2235
if (first_time )
2252
2236
{
2253
2237
oldcontext = MemoryContextSwitchTo (econtext -> ecxt_per_query_memory );
2254
- if (returnsTuple )
2255
- {
2256
- /*
2257
- * Use the type info embedded in the rowtype Datum to look
2258
- * up the needed tupdesc. Make a copy for the query.
2259
- */
2260
- HeapTupleHeader td ;
2261
-
2262
- td = DatumGetHeapTupleHeader (result );
2263
- tupdesc = lookup_rowtype_tupdesc_copy (HeapTupleHeaderGetTypeId (td ),
2264
- HeapTupleHeaderGetTypMod (td ));
2265
- }
2266
- else
2238
+ tupstore = tuplestore_begin_heap (randomAccess , false, work_mem );
2239
+ rsinfo .setResult = tupstore ;
2240
+ if (!returnsTuple )
2267
2241
{
2268
- /*
2269
- * Scalar type, so make a single-column descriptor
2270
- */
2271
2242
tupdesc = CreateTemplateTupleDesc (1 , false);
2272
2243
TupleDescInitEntry (tupdesc ,
2273
2244
(AttrNumber ) 1 ,
2274
2245
"column" ,
2275
2246
funcrettype ,
2276
2247
-1 ,
2277
2248
0 );
2249
+ rsinfo .setDesc = tupdesc ;
2278
2250
}
2279
- tupstore = tuplestore_begin_heap (randomAccess , false, work_mem );
2280
2251
MemoryContextSwitchTo (oldcontext );
2281
- rsinfo .setResult = tupstore ;
2282
- rsinfo .setDesc = tupdesc ;
2283
2252
}
2284
2253
2285
2254
/*
2286
2255
* Store current resultset item.
2287
2256
*/
2288
2257
if (returnsTuple )
2289
2258
{
2290
- HeapTupleHeader td ;
2259
+ if (!fcinfo .isnull )
2260
+ {
2261
+ HeapTupleHeader td = DatumGetHeapTupleHeader (result );
2291
2262
2292
- td = DatumGetHeapTupleHeader (result );
2263
+ if (tupdesc == NULL )
2264
+ {
2265
+ /*
2266
+ * This is the first non-NULL result from the
2267
+ * function. Use the type info embedded in the
2268
+ * rowtype Datum to look up the needed tupdesc. Make
2269
+ * a copy for the query.
2270
+ */
2271
+ oldcontext = MemoryContextSwitchTo (econtext -> ecxt_per_query_memory );
2272
+ tupdesc = lookup_rowtype_tupdesc_copy (HeapTupleHeaderGetTypeId (td ),
2273
+ HeapTupleHeaderGetTypMod (td ));
2274
+ rsinfo .setDesc = tupdesc ;
2275
+ MemoryContextSwitchTo (oldcontext );
2276
+ }
2277
+ else
2278
+ {
2279
+ /*
2280
+ * Verify all later returned rows have same subtype;
2281
+ * necessary in case the type is RECORD.
2282
+ */
2283
+ if (HeapTupleHeaderGetTypeId (td ) != tupdesc -> tdtypeid ||
2284
+ HeapTupleHeaderGetTypMod (td ) != tupdesc -> tdtypmod )
2285
+ ereport (ERROR ,
2286
+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
2287
+ errmsg ("rows returned by function are not all of the same row type" )));
2288
+ }
2293
2289
2294
- /*
2295
- * Verify all returned rows have same subtype; necessary in
2296
- * case the type is RECORD.
2297
- */
2298
- if (HeapTupleHeaderGetTypeId (td ) != tupdesc -> tdtypeid ||
2299
- HeapTupleHeaderGetTypMod (td ) != tupdesc -> tdtypmod )
2300
- ereport (ERROR ,
2301
- (errcode (ERRCODE_DATATYPE_MISMATCH ),
2302
- errmsg ("rows returned by function are not all of the same row type" )));
2290
+ /*
2291
+ * tuplestore_puttuple needs a HeapTuple not a bare
2292
+ * HeapTupleHeader, but it doesn't need all the fields.
2293
+ */
2294
+ tmptup .t_len = HeapTupleHeaderGetDatumLength (td );
2295
+ tmptup .t_data = td ;
2303
2296
2304
- /*
2305
- * tuplestore_puttuple needs a HeapTuple not a bare
2306
- * HeapTupleHeader, but it doesn't need all the fields.
2307
- */
2308
- tmptup .t_len = HeapTupleHeaderGetDatumLength (td );
2309
- tmptup .t_data = td ;
2297
+ tuplestore_puttuple (tupstore , & tmptup );
2298
+ }
2299
+ else
2300
+ {
2301
+ /*
2302
+ * NULL result from a tuple-returning function; expand it
2303
+ * to a row of all nulls. We rely on the expectedDesc to
2304
+ * form such rows. (Note: this would be problematic if
2305
+ * tuplestore_putvalues saved the tdtypeid/tdtypmod from
2306
+ * the provided descriptor, since that might not match
2307
+ * what we get from the function itself. But it doesn't.)
2308
+ */
2309
+ int natts = expectedDesc -> natts ;
2310
+ bool * nullflags ;
2310
2311
2311
- tuplestore_puttuple (tupstore , & tmptup );
2312
+ nullflags = (bool * ) palloc (natts * sizeof (bool ));
2313
+ memset (nullflags , true, natts * sizeof (bool ));
2314
+ tuplestore_putvalues (tupstore , expectedDesc , NULL , nullflags );
2315
+ }
2312
2316
}
2313
2317
else
2318
+ {
2319
+ /* Scalar-type case: just store the function result */
2314
2320
tuplestore_putvalues (tupstore , tupdesc , & result , & fcinfo .isnull );
2321
+ }
2315
2322
2316
2323
/*
2317
2324
* Are we done?
@@ -2343,7 +2350,8 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
2343
2350
/*
2344
2351
* If we got nothing from the function (ie, an empty-set or NULL result),
2345
2352
* we have to create the tuplestore to return, and if it's a
2346
- * non-set-returning function then insert a single all-nulls row.
2353
+ * non-set-returning function then insert a single all-nulls row. As
2354
+ * above, we depend on the expectedDesc to manufacture the dummy row.
2347
2355
*/
2348
2356
if (rsinfo .setResult == NULL )
2349
2357
{
@@ -2353,15 +2361,12 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
2353
2361
if (!returnsSet )
2354
2362
{
2355
2363
int natts = expectedDesc -> natts ;
2356
- Datum * nulldatums ;
2357
2364
bool * nullflags ;
2358
2365
2359
2366
MemoryContextSwitchTo (econtext -> ecxt_per_tuple_memory );
2360
- nulldatums = (Datum * ) palloc0 (natts * sizeof (Datum ));
2361
2367
nullflags = (bool * ) palloc (natts * sizeof (bool ));
2362
2368
memset (nullflags , true, natts * sizeof (bool ));
2363
- MemoryContextSwitchTo (econtext -> ecxt_per_query_memory );
2364
- tuplestore_putvalues (tupstore , expectedDesc , nulldatums , nullflags );
2369
+ tuplestore_putvalues (tupstore , expectedDesc , NULL , nullflags );
2365
2370
}
2366
2371
}
2367
2372
0 commit comments