@@ -5146,36 +5146,21 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
5146
5146
5147
5147
5148
5148
/*
5149
- * inline_set_returning_function
5150
- * Attempt to "inline" a set-returning function in the FROM clause.
5151
- *
5152
- * "rte" is an RTE_FUNCTION rangetable entry. If it represents a call of a
5153
- * set-returning SQL function that can safely be inlined, expand the function
5154
- * and return the substitute Query structure. Otherwise, return NULL.
5149
+ * inline_sql_set_returning_function
5155
5150
*
5156
- * We assume that the RTE's expression has already been put through
5157
- * eval_const_expressions(), which among other things will take care of
5158
- * default arguments and named-argument notation.
5151
+ * This implements inline_set_returning_function for sql-language functions.
5152
+ * It parses the body (or uses the pre-parsed body if available).
5159
5153
*
5160
- * This has a good deal of similarity to inline_function(), but that's
5161
- * for the non-set-returning case, and there are enough differences to
5162
- * justify separate functions.
5154
+ * Returns NULL if the function couldn't be inlined.
5163
5155
*/
5164
- Query *
5165
- inline_set_returning_function (PlannerInfo * root , RangeTblEntry * rte )
5156
+ static Query *
5157
+ inline_sql_set_returning_function (PlannerInfo * root , RangeTblEntry * rte ,
5158
+ RangeTblFunction * rtfunc ,
5159
+ FuncExpr * fexpr , Oid func_oid , HeapTuple func_tuple ,
5160
+ Form_pg_proc funcform , char * src )
5166
5161
{
5167
- RangeTblFunction * rtfunc ;
5168
- FuncExpr * fexpr ;
5169
- Oid func_oid ;
5170
- HeapTuple func_tuple ;
5171
- Form_pg_proc funcform ;
5172
- char * src ;
5173
- Datum tmp ;
5162
+ Datum sqlbody ;
5174
5163
bool isNull ;
5175
- MemoryContext oldcxt ;
5176
- MemoryContext mycxt ;
5177
- inline_error_callback_arg callback_arg ;
5178
- ErrorContextCallback sqlerrcontext ;
5179
5164
SQLFunctionParseInfoPtr pinfo ;
5180
5165
TypeFuncClass functypclass ;
5181
5166
TupleDesc rettupdesc ;
@@ -5185,29 +5170,6 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
5185
5170
5186
5171
Assert (rte -> rtekind == RTE_FUNCTION );
5187
5172
5188
- /*
5189
- * It doesn't make a lot of sense for a SQL SRF to refer to itself in its
5190
- * own FROM clause, since that must cause infinite recursion at runtime.
5191
- * It will cause this code to recurse too, so check for stack overflow.
5192
- * (There's no need to do more.)
5193
- */
5194
- check_stack_depth ();
5195
-
5196
- /* Fail if the RTE has ORDINALITY - we don't implement that here. */
5197
- if (rte -> funcordinality )
5198
- return NULL ;
5199
-
5200
- /* Fail if RTE isn't a single, simple FuncExpr */
5201
- if (list_length (rte -> functions ) != 1 )
5202
- return NULL ;
5203
- rtfunc = (RangeTblFunction * ) linitial (rte -> functions );
5204
-
5205
- if (!IsA (rtfunc -> funcexpr , FuncExpr ))
5206
- return NULL ;
5207
- fexpr = (FuncExpr * ) rtfunc -> funcexpr ;
5208
-
5209
- func_oid = fexpr -> funcid ;
5210
-
5211
5173
/*
5212
5174
* The function must be declared to return a set, else inlining would
5213
5175
* change the results if the contained SELECT didn't return exactly one
@@ -5216,35 +5178,6 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
5216
5178
if (!fexpr -> funcretset )
5217
5179
return NULL ;
5218
5180
5219
- /*
5220
- * Refuse to inline if the arguments contain any volatile functions or
5221
- * sub-selects. Volatile functions are rejected because inlining may
5222
- * result in the arguments being evaluated multiple times, risking a
5223
- * change in behavior. Sub-selects are rejected partly for implementation
5224
- * reasons (pushing them down another level might change their behavior)
5225
- * and partly because they're likely to be expensive and so multiple
5226
- * evaluation would be bad.
5227
- */
5228
- if (contain_volatile_functions ((Node * ) fexpr -> args ) ||
5229
- contain_subplans ((Node * ) fexpr -> args ))
5230
- return NULL ;
5231
-
5232
- /* Check permission to call function (fail later, if not) */
5233
- if (object_aclcheck (ProcedureRelationId , func_oid , GetUserId (), ACL_EXECUTE ) != ACLCHECK_OK )
5234
- return NULL ;
5235
-
5236
- /* Check whether a plugin wants to hook function entry/exit */
5237
- if (FmgrHookIsNeeded (func_oid ))
5238
- return NULL ;
5239
-
5240
- /*
5241
- * OK, let's take a look at the function's pg_proc entry.
5242
- */
5243
- func_tuple = SearchSysCache1 (PROCOID , ObjectIdGetDatum (func_oid ));
5244
- if (!HeapTupleIsValid (func_tuple ))
5245
- elog (ERROR , "cache lookup failed for function %u" , func_oid );
5246
- funcform = (Form_pg_proc ) GETSTRUCT (func_tuple );
5247
-
5248
5181
/*
5249
5182
* Forget it if the function is not SQL-language or has other showstopper
5250
5183
* properties. In particular it mustn't be declared STRICT, since we
@@ -5262,61 +5195,34 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
5262
5195
funcform -> prorettype == VOIDOID ||
5263
5196
funcform -> prosecdef ||
5264
5197
!funcform -> proretset ||
5265
- list_length (fexpr -> args ) != funcform -> pronargs ||
5266
- !heap_attisnull (func_tuple , Anum_pg_proc_proconfig , NULL ))
5198
+ list_length (fexpr -> args ) != funcform -> pronargs )
5267
5199
{
5268
- ReleaseSysCache (func_tuple );
5269
5200
return NULL ;
5270
5201
}
5271
5202
5272
- /*
5273
- * Make a temporary memory context, so that we don't leak all the stuff
5274
- * that parsing might create.
5275
- */
5276
- mycxt = AllocSetContextCreate (CurrentMemoryContext ,
5277
- "inline_set_returning_function" ,
5278
- ALLOCSET_DEFAULT_SIZES );
5279
- oldcxt = MemoryContextSwitchTo (mycxt );
5280
-
5281
- /* Fetch the function body */
5282
- tmp = SysCacheGetAttrNotNull (PROCOID , func_tuple , Anum_pg_proc_prosrc );
5283
- src = TextDatumGetCString (tmp );
5284
-
5285
- /*
5286
- * Setup error traceback support for ereport(). This is so that we can
5287
- * finger the function that bad information came from.
5288
- */
5289
- callback_arg .proname = NameStr (funcform -> proname );
5290
- callback_arg .prosrc = src ;
5291
-
5292
- sqlerrcontext .callback = sql_inline_error_callback ;
5293
- sqlerrcontext .arg = & callback_arg ;
5294
- sqlerrcontext .previous = error_context_stack ;
5295
- error_context_stack = & sqlerrcontext ;
5296
-
5297
5203
/* If we have prosqlbody, pay attention to that not prosrc */
5298
- tmp = SysCacheGetAttr (PROCOID ,
5299
- func_tuple ,
5300
- Anum_pg_proc_prosqlbody ,
5301
- & isNull );
5204
+ sqlbody = SysCacheGetAttr (PROCOID ,
5205
+ func_tuple ,
5206
+ Anum_pg_proc_prosqlbody ,
5207
+ & isNull );
5302
5208
if (!isNull )
5303
5209
{
5304
5210
Node * n ;
5305
5211
5306
- n = stringToNode (TextDatumGetCString (tmp ));
5212
+ n = stringToNode (TextDatumGetCString (sqlbody ));
5307
5213
if (IsA (n , List ))
5308
5214
querytree_list = linitial_node (List , castNode (List , n ));
5309
5215
else
5310
5216
querytree_list = list_make1 (n );
5311
5217
if (list_length (querytree_list ) != 1 )
5312
- goto fail ;
5218
+ return NULL ;
5313
5219
querytree = linitial (querytree_list );
5314
5220
5315
5221
/* Acquire necessary locks, then apply rewriter. */
5316
5222
AcquireRewriteLocks (querytree , true, false);
5317
5223
querytree_list = pg_rewrite_query (querytree );
5318
5224
if (list_length (querytree_list ) != 1 )
5319
- goto fail ;
5225
+ return NULL ;
5320
5226
querytree = linitial (querytree_list );
5321
5227
}
5322
5228
else
@@ -5337,14 +5243,14 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
5337
5243
*/
5338
5244
raw_parsetree_list = pg_parse_query (src );
5339
5245
if (list_length (raw_parsetree_list ) != 1 )
5340
- goto fail ;
5246
+ return NULL ;
5341
5247
5342
5248
querytree_list = pg_analyze_and_rewrite_withcb (linitial (raw_parsetree_list ),
5343
5249
src ,
5344
5250
(ParserSetupHook ) sql_fn_parser_setup ,
5345
5251
pinfo , NULL );
5346
5252
if (list_length (querytree_list ) != 1 )
5347
- goto fail ;
5253
+ return NULL ;
5348
5254
querytree = linitial (querytree_list );
5349
5255
}
5350
5256
@@ -5369,7 +5275,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
5369
5275
*/
5370
5276
if (!IsA (querytree , Query ) ||
5371
5277
querytree -> commandType != CMD_SELECT )
5372
- goto fail ;
5278
+ return NULL ;
5373
5279
5374
5280
/*
5375
5281
* Make sure the function (still) returns what it's declared to. This
@@ -5391,14 +5297,149 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
5391
5297
(functypclass == TYPEFUNC_COMPOSITE ||
5392
5298
functypclass == TYPEFUNC_COMPOSITE_DOMAIN ||
5393
5299
functypclass == TYPEFUNC_RECORD ))
5394
- goto fail ; /* reject not-whole-tuple-result cases */
5300
+ return NULL ; /* reject not-whole-tuple-result cases */
5395
5301
5396
5302
/*
5397
5303
* check_sql_fn_retval might've inserted a projection step, but that's
5398
5304
* fine; just make sure we use the upper Query.
5399
5305
*/
5400
5306
querytree = linitial_node (Query , querytree_list );
5401
5307
5308
+ return querytree ;
5309
+ }
5310
+
5311
+ /*
5312
+ * inline_set_returning_function
5313
+ * Attempt to "inline" an SQL set-returning function in the FROM clause.
5314
+ *
5315
+ * "rte" is an RTE_FUNCTION rangetable entry. If it represents a call of a
5316
+ * set-returning SQL function that can safely be inlined, expand the function
5317
+ * and return the substitute Query structure. Otherwise, return NULL.
5318
+ *
5319
+ * We assume that the RTE's expression has already been put through
5320
+ * eval_const_expressions(), which among other things will take care of
5321
+ * default arguments and named-argument notation.
5322
+ *
5323
+ * This has a good deal of similarity to inline_function(), but that's
5324
+ * for the non-set-returning case, and there are enough differences to
5325
+ * justify separate functions.
5326
+ *
5327
+ * It allocates its own temporary MemoryContext for the parsing, then copies
5328
+ * the result into the caller's context.
5329
+ */
5330
+ Query *
5331
+ inline_set_returning_function (PlannerInfo * root , RangeTblEntry * rte )
5332
+ {
5333
+ RangeTblFunction * rtfunc ;
5334
+ FuncExpr * fexpr ;
5335
+ Oid func_oid ;
5336
+ HeapTuple func_tuple ;
5337
+ Form_pg_proc funcform ;
5338
+ Datum tmp ;
5339
+ char * src ;
5340
+ inline_error_callback_arg callback_arg ;
5341
+ ErrorContextCallback sqlerrcontext ;
5342
+ MemoryContext oldcxt ;
5343
+ MemoryContext mycxt ;
5344
+ Query * querytree ;
5345
+
5346
+ Assert (rte -> rtekind == RTE_FUNCTION );
5347
+
5348
+ /*
5349
+ * It doesn't make a lot of sense for a SRF to refer to itself in its own
5350
+ * FROM clause, since that must cause infinite recursion at runtime. It
5351
+ * will cause this code to recurse too, so check for stack overflow.
5352
+ * (There's no need to do more.)
5353
+ */
5354
+ check_stack_depth ();
5355
+
5356
+ /* Fail if the RTE has ORDINALITY - we don't implement that here. */
5357
+ if (rte -> funcordinality )
5358
+ return NULL ;
5359
+
5360
+ /* Fail if RTE isn't a single, simple FuncExpr */
5361
+ if (list_length (rte -> functions ) != 1 )
5362
+ return NULL ;
5363
+ rtfunc = (RangeTblFunction * ) linitial (rte -> functions );
5364
+
5365
+ if (!IsA (rtfunc -> funcexpr , FuncExpr ))
5366
+ return NULL ;
5367
+ fexpr = (FuncExpr * ) rtfunc -> funcexpr ;
5368
+
5369
+ func_oid = fexpr -> funcid ;
5370
+
5371
+ /*
5372
+ * Refuse to inline if the arguments contain any volatile functions or
5373
+ * sub-selects. Volatile functions are rejected because inlining may
5374
+ * result in the arguments being evaluated multiple times, risking a
5375
+ * change in behavior. Sub-selects are rejected partly for implementation
5376
+ * reasons (pushing them down another level might change their behavior)
5377
+ * and partly because they're likely to be expensive and so multiple
5378
+ * evaluation would be bad.
5379
+ */
5380
+ if (contain_volatile_functions ((Node * ) fexpr -> args ) ||
5381
+ contain_subplans ((Node * ) fexpr -> args ))
5382
+ return NULL ;
5383
+
5384
+ /* Check permission to call function (fail later, if not) */
5385
+ if (object_aclcheck (ProcedureRelationId , func_oid , GetUserId (), ACL_EXECUTE ) != ACLCHECK_OK )
5386
+ return NULL ;
5387
+
5388
+ /* Check whether a plugin wants to hook function entry/exit */
5389
+ if (FmgrHookIsNeeded (func_oid ))
5390
+ return NULL ;
5391
+
5392
+ /*
5393
+ * OK, let's take a look at the function's pg_proc entry.
5394
+ */
5395
+ func_tuple = SearchSysCache1 (PROCOID , ObjectIdGetDatum (func_oid ));
5396
+ if (!HeapTupleIsValid (func_tuple ))
5397
+ elog (ERROR , "cache lookup failed for function %u" , func_oid );
5398
+ funcform = (Form_pg_proc ) GETSTRUCT (func_tuple );
5399
+
5400
+ /*
5401
+ * Make a temporary memory context, so that we don't leak all the stuff
5402
+ * that parsing might create.
5403
+ */
5404
+ mycxt = AllocSetContextCreate (CurrentMemoryContext ,
5405
+ "inline_set_returning_function" ,
5406
+ ALLOCSET_DEFAULT_SIZES );
5407
+ oldcxt = MemoryContextSwitchTo (mycxt );
5408
+
5409
+ /* Fetch the function body */
5410
+ tmp = SysCacheGetAttrNotNull (PROCOID , func_tuple , Anum_pg_proc_prosrc );
5411
+ src = TextDatumGetCString (tmp );
5412
+
5413
+ /*
5414
+ * Setup error traceback support for ereport(). This is so that we can
5415
+ * finger the function that bad information came from.
5416
+ */
5417
+ callback_arg .proname = NameStr (funcform -> proname );
5418
+ callback_arg .prosrc = src ;
5419
+
5420
+ sqlerrcontext .callback = sql_inline_error_callback ;
5421
+ sqlerrcontext .arg = & callback_arg ;
5422
+ sqlerrcontext .previous = error_context_stack ;
5423
+ error_context_stack = & sqlerrcontext ;
5424
+
5425
+ /*
5426
+ * If the function SETs configuration parameters, inlining would cause us
5427
+ * to skip those changes.
5428
+ */
5429
+ if (!heap_attisnull (func_tuple , Anum_pg_proc_proconfig , NULL ))
5430
+ goto fail ;
5431
+
5432
+ querytree = inline_sql_set_returning_function (root , rte , rtfunc , fexpr ,
5433
+ func_oid , func_tuple , funcform ,
5434
+ src );
5435
+
5436
+ if (!querytree )
5437
+ goto fail ;
5438
+
5439
+ /* Only SELECTs are permitted */
5440
+ Assert (IsA (querytree , Query ));
5441
+ Assert (querytree -> commandType == CMD_SELECT );
5442
+
5402
5443
/*
5403
5444
* Looks good --- substitute parameters into the query.
5404
5445
*/
0 commit comments