3636
3737static void collect_volatility (PLpgSQL_checkstate * cstate , Query * query );
3838static Query * ExprGetQuery (PLpgSQL_checkstate * cstate , PLpgSQL_expr * expr , CachedPlanSource * plansource );
39+ static void check_pure_expr (PLpgSQL_checkstate * cstate , Query * query , char * query_str );
3940
4041static CachedPlan * get_cached_plan (PLpgSQL_checkstate * cstate , PLpgSQL_expr * expr , bool * has_result_desc );
4142static void plan_checks (PLpgSQL_checkstate * cstate , CachedPlan * cplan , char * query_str );
@@ -280,7 +281,8 @@ prepare_plan(PLpgSQL_checkstate *cstate,
280281 PLpgSQL_expr * expr ,
281282 int cursorOptions ,
282283 ParserSetupHook parser_setup ,
283- void * arg )
284+ void * arg ,
285+ bool pure_expr_check )
284286{
285287 Query * query ;
286288 CachedPlanSource * plansource = NULL ;
@@ -304,6 +306,9 @@ prepare_plan(PLpgSQL_checkstate *cstate,
304306 plpgsql_check_funcexpr (cstate , query , expr -> query );
305307 collect_volatility (cstate , query );
306308 plpgsql_check_detect_dependency (cstate , query );
309+
310+ if (pure_expr_check )
311+ check_pure_expr (cstate , query , expr -> query );
307312}
308313
309314/*
@@ -384,6 +389,65 @@ plpgsql_check_get_plan_source(PLpgSQL_checkstate *cstate, SPIPlanPtr plan)
384389 return plansource ;
385390}
386391
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+
387451/*
388452 * Returns Query node for expression
389453 *
@@ -912,7 +976,7 @@ force_plan_checks(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr)
912976void
913977plpgsql_check_expr_generic (PLpgSQL_checkstate * cstate , PLpgSQL_expr * expr )
914978{
915- prepare_plan (cstate , expr , 0 , NULL , NULL );
979+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
916980 force_plan_checks (cstate , expr );
917981}
918982
@@ -922,7 +986,7 @@ plpgsql_check_expr_generic_with_parser_setup(PLpgSQL_checkstate *cstate,
922986 ParserSetupHook parser_setup ,
923987 void * arg )
924988{
925- prepare_plan (cstate , expr , 0 , parser_setup , arg );
989+ prepare_plan (cstate , expr , 0 , parser_setup , arg , false );
926990 force_plan_checks (cstate , expr );
927991}
928992
@@ -963,7 +1027,7 @@ plpgsql_check_expr_with_scalar_type(PLpgSQL_checkstate *cstate,
9631027 TupleDesc tupdesc ;
9641028 bool is_immutable_null ;
9651029
966- prepare_plan (cstate , expr , 0 , NULL , NULL );
1030+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
9671031 /* record all variables used by the query */
9681032 cstate -> used_variables = bms_add_members (cstate -> used_variables , expr -> paramnos );
9691033
@@ -1036,7 +1100,7 @@ plpgsql_check_returned_expr(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr, bool
10361100 bool is_immutable_null ;
10371101 Oid first_level_typ = InvalidOid ;
10381102
1039- prepare_plan (cstate , expr , 0 , NULL , NULL );
1103+ prepare_plan (cstate , expr , 0 , NULL , NULL , is_expression );
10401104
10411105 /* record all variables used by the query, should be after prepare_plan */
10421106 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,
12551319
12561320 PG_TRY ();
12571321 {
1258- prepare_plan (cstate , expr , 0 , NULL , NULL );
1322+ prepare_plan (cstate , expr , 0 , NULL , NULL , is_expression );
12591323 /* record all variables used by the query */
12601324
12611325#if PG_VERSION_NUM >= 140000
@@ -1633,7 +1697,7 @@ plpgsql_check_expr_as_sqlstmt(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr)
16331697 {
16341698 TupleDesc tupdesc ;
16351699
1636- prepare_plan (cstate , expr , 0 , NULL , NULL );
1700+ prepare_plan (cstate , expr , 0 , NULL , NULL , false );
16371701 /* record all variables used by the query */
16381702 cstate -> used_variables = bms_add_members (cstate -> used_variables , expr -> paramnos );
16391703 force_plan_checks (cstate , expr );
0 commit comments