36
36
37
37
static void collect_volatility (PLpgSQL_checkstate * cstate , Query * query );
38
38
static Query * ExprGetQuery (PLpgSQL_checkstate * cstate , PLpgSQL_expr * expr , CachedPlanSource * plansource );
39
+ static void check_pure_expr (PLpgSQL_checkstate * cstate , Query * query , char * query_str );
39
40
40
41
static CachedPlan * get_cached_plan (PLpgSQL_checkstate * cstate , PLpgSQL_expr * expr , bool * has_result_desc );
41
42
static void plan_checks (PLpgSQL_checkstate * cstate , CachedPlan * cplan , char * query_str );
@@ -280,7 +281,8 @@ prepare_plan(PLpgSQL_checkstate *cstate,
280
281
PLpgSQL_expr * expr ,
281
282
int cursorOptions ,
282
283
ParserSetupHook parser_setup ,
283
- void * arg )
284
+ void * arg ,
285
+ bool pure_expr_check )
284
286
{
285
287
Query * query ;
286
288
CachedPlanSource * plansource = NULL ;
@@ -304,6 +306,24 @@ prepare_plan(PLpgSQL_checkstate *cstate,
304
306
plpgsql_check_funcexpr (cstate , query , expr -> query );
305
307
collect_volatility (cstate , query );
306
308
plpgsql_check_detect_dependency (cstate , query );
309
+
310
+ if (!pure_expr_check )
311
+ return ;
312
+
313
+ #if PG_VERSION_NUM < 140000
314
+
315
+ check_pure_expr (cstate , query , expr -> query );
316
+
317
+ #else
318
+
319
+ if (expr -> parseMode == RAW_PARSE_PLPGSQL_EXPR ||
320
+ expr -> parseMode == RAW_PARSE_PLPGSQL_ASSIGN1 ||
321
+ expr -> parseMode == RAW_PARSE_PLPGSQL_ASSIGN2 ||
322
+ expr -> parseMode == RAW_PARSE_PLPGSQL_ASSIGN3 )
323
+ check_pure_expr (cstate , query , expr -> query );
324
+
325
+ #endif
326
+
307
327
}
308
328
309
329
/*
@@ -384,6 +404,65 @@ plpgsql_check_get_plan_source(PLpgSQL_checkstate *cstate, SPIPlanPtr plan)
384
404
return plansource ;
385
405
}
386
406
407
+ /*
408
+ * Check if query holds just an expression
409
+ *
410
+ */
411
+ static bool
412
+ is_pure_expr (PLpgSQL_checkstate * cstate , Query * query )
413
+ {
414
+ Node * n ;
415
+
416
+ /* only restarget can be assigned in pure expression */
417
+ if (query -> rtable )
418
+ return false;
419
+
420
+ if (query -> distinctClause )
421
+ return false;
422
+
423
+ if (query -> groupClause )
424
+ return false;
425
+
426
+ if (query -> havingQual )
427
+ return false;
428
+
429
+ if (!query -> targetList )
430
+ return false;
431
+
432
+ if (list_length (query -> targetList ) > 1 )
433
+ return false;
434
+
435
+ n = (Node * ) linitial (query -> targetList );
436
+
437
+ if (!IsA (n , TargetEntry ))
438
+ return false;
439
+
440
+ /*
441
+ * unfortunately, the resname should not be checked,
442
+ * postgres uses ?column?, varname, or type names, ...
443
+ */
444
+
445
+ return true;
446
+ }
447
+
448
+ static void
449
+ check_pure_expr (PLpgSQL_checkstate * cstate , Query * query , char * query_str )
450
+ {
451
+ if (!cstate -> cinfo -> extra_warnings )
452
+ return ;
453
+
454
+ if (!is_pure_expr (cstate , query ))
455
+ {
456
+ plpgsql_check_put_error (cstate ,
457
+ 0 , 0 ,
458
+ "expression is not pure expression" ,
459
+ "there is a possibility of unwanted behave" ,
460
+ NULL ,
461
+ PLPGSQL_CHECK_WARNING_EXTRA ,
462
+ 0 , query_str , NULL );
463
+ }
464
+ }
465
+
387
466
/*
388
467
* Returns Query node for expression
389
468
*
@@ -912,7 +991,7 @@ force_plan_checks(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr)
912
991
void
913
992
plpgsql_check_expr_generic (PLpgSQL_checkstate * cstate , PLpgSQL_expr * expr )
914
993
{
915
- prepare_plan (cstate , expr , 0 , NULL , NULL );
994
+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
916
995
force_plan_checks (cstate , expr );
917
996
}
918
997
@@ -922,7 +1001,7 @@ plpgsql_check_expr_generic_with_parser_setup(PLpgSQL_checkstate *cstate,
922
1001
ParserSetupHook parser_setup ,
923
1002
void * arg )
924
1003
{
925
- prepare_plan (cstate , expr , 0 , parser_setup , arg );
1004
+ prepare_plan (cstate , expr , 0 , parser_setup , arg , true );
926
1005
force_plan_checks (cstate , expr );
927
1006
}
928
1007
@@ -963,7 +1042,7 @@ plpgsql_check_expr_with_scalar_type(PLpgSQL_checkstate *cstate,
963
1042
TupleDesc tupdesc ;
964
1043
bool is_immutable_null ;
965
1044
966
- prepare_plan (cstate , expr , 0 , NULL , NULL );
1045
+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
967
1046
/* record all variables used by the query */
968
1047
cstate -> used_variables = bms_add_members (cstate -> used_variables , expr -> paramnos );
969
1048
@@ -1036,7 +1115,7 @@ plpgsql_check_returned_expr(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr, bool
1036
1115
bool is_immutable_null ;
1037
1116
Oid first_level_typ = InvalidOid ;
1038
1117
1039
- prepare_plan (cstate , expr , 0 , NULL , NULL );
1118
+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
1040
1119
1041
1120
/* record all variables used by the query, should be after prepare_plan */
1042
1121
cstate -> used_variables = bms_add_members (cstate -> used_variables , expr -> paramnos );
@@ -1255,7 +1334,7 @@ plpgsql_check_expr_as_rvalue(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr,
1255
1334
1256
1335
PG_TRY ();
1257
1336
{
1258
- prepare_plan (cstate , expr , 0 , NULL , NULL );
1337
+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
1259
1338
/* record all variables used by the query */
1260
1339
1261
1340
#if PG_VERSION_NUM >= 140000
@@ -1633,7 +1712,7 @@ plpgsql_check_expr_as_sqlstmt(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr)
1633
1712
{
1634
1713
TupleDesc tupdesc ;
1635
1714
1636
- prepare_plan (cstate , expr , 0 , NULL , NULL );
1715
+ prepare_plan (cstate , expr , 0 , NULL , NULL , false );
1637
1716
/* record all variables used by the query */
1638
1717
cstate -> used_variables = bms_add_members (cstate -> used_variables , expr -> paramnos );
1639
1718
force_plan_checks (cstate , expr );
0 commit comments