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,9 @@ 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
+ check_pure_expr (cstate , query , expr -> query );
307
312
}
308
313
309
314
/*
@@ -384,6 +389,65 @@ plpgsql_check_get_plan_source(PLpgSQL_checkstate *cstate, SPIPlanPtr plan)
384
389
return plansource ;
385
390
}
386
391
392
+ /*
393
+ * Check if query holds just an expression
394
+ *
395
+ */
396
+ static bool
397
+ is_pure_expr (PLpgSQL_checkstate * cstate , Query * query )
398
+ {
399
+ Node * n ;
400
+
401
+ /* only restarget can be assigned in pure expression */
402
+ if (query -> rtable )
403
+ return false;
404
+
405
+ if (query -> distinctClause )
406
+ return false;
407
+
408
+ if (query -> groupClause )
409
+ return false;
410
+
411
+ if (query -> havingQual )
412
+ return false;
413
+
414
+ if (!query -> targetList )
415
+ return false;
416
+
417
+ if (list_length (query -> targetList ) > 1 )
418
+ return false;
419
+
420
+ n = (Node * ) linitial (query -> targetList );
421
+
422
+ if (!IsA (n , TargetEntry ))
423
+ return false;
424
+
425
+ /*
426
+ * unfortunately, the resname should not be checked,
427
+ * postgres uses ?column?, varname, or type names, ...
428
+ */
429
+
430
+ return true;
431
+ }
432
+
433
+ static void
434
+ check_pure_expr (PLpgSQL_checkstate * cstate , Query * query , char * query_str )
435
+ {
436
+ if (!cstate -> cinfo -> extra_warnings )
437
+ return ;
438
+
439
+ if (!is_pure_expr (cstate , query ))
440
+ {
441
+ plpgsql_check_put_error (cstate ,
442
+ 0 , 0 ,
443
+ "expression is not pure expression" ,
444
+ "there is a possibility of unwanted behave" ,
445
+ NULL ,
446
+ PLPGSQL_CHECK_WARNING_EXTRA ,
447
+ 0 , query_str , NULL );
448
+ }
449
+ }
450
+
387
451
/*
388
452
* Returns Query node for expression
389
453
*
@@ -912,7 +976,7 @@ force_plan_checks(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr)
912
976
void
913
977
plpgsql_check_expr_generic (PLpgSQL_checkstate * cstate , PLpgSQL_expr * expr )
914
978
{
915
- prepare_plan (cstate , expr , 0 , NULL , NULL );
979
+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
916
980
force_plan_checks (cstate , expr );
917
981
}
918
982
@@ -922,7 +986,7 @@ plpgsql_check_expr_generic_with_parser_setup(PLpgSQL_checkstate *cstate,
922
986
ParserSetupHook parser_setup ,
923
987
void * arg )
924
988
{
925
- prepare_plan (cstate , expr , 0 , parser_setup , arg );
989
+ prepare_plan (cstate , expr , 0 , parser_setup , arg , false );
926
990
force_plan_checks (cstate , expr );
927
991
}
928
992
@@ -963,7 +1027,7 @@ plpgsql_check_expr_with_scalar_type(PLpgSQL_checkstate *cstate,
963
1027
TupleDesc tupdesc ;
964
1028
bool is_immutable_null ;
965
1029
966
- prepare_plan (cstate , expr , 0 , NULL , NULL );
1030
+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
967
1031
/* record all variables used by the query */
968
1032
cstate -> used_variables = bms_add_members (cstate -> used_variables , expr -> paramnos );
969
1033
@@ -1036,7 +1100,7 @@ plpgsql_check_returned_expr(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr, bool
1036
1100
bool is_immutable_null ;
1037
1101
Oid first_level_typ = InvalidOid ;
1038
1102
1039
- prepare_plan (cstate , expr , 0 , NULL , NULL );
1103
+ prepare_plan (cstate , expr , 0 , NULL , NULL , is_expression );
1040
1104
1041
1105
/* record all variables used by the query, should be after prepare_plan */
1042
1106
cstate -> used_variables = bms_add_members (cstate -> used_variables , expr -> paramnos );
@@ -1255,7 +1319,7 @@ plpgsql_check_expr_as_rvalue(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr,
1255
1319
1256
1320
PG_TRY ();
1257
1321
{
1258
- prepare_plan (cstate , expr , 0 , NULL , NULL );
1322
+ prepare_plan (cstate , expr , 0 , NULL , NULL , is_expression );
1259
1323
/* record all variables used by the query */
1260
1324
1261
1325
#if PG_VERSION_NUM >= 140000
@@ -1633,7 +1697,7 @@ plpgsql_check_expr_as_sqlstmt(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr)
1633
1697
{
1634
1698
TupleDesc tupdesc ;
1635
1699
1636
- prepare_plan (cstate , expr , 0 , NULL , NULL );
1700
+ prepare_plan (cstate , expr , 0 , NULL , NULL , false );
1637
1701
/* record all variables used by the query */
1638
1702
cstate -> used_variables = bms_add_members (cstate -> used_variables , expr -> paramnos );
1639
1703
force_plan_checks (cstate , expr );
0 commit comments