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,24 @@ 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+ 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+
307327}
308328
309329/*
@@ -384,6 +404,65 @@ plpgsql_check_get_plan_source(PLpgSQL_checkstate *cstate, SPIPlanPtr plan)
384404 return plansource ;
385405}
386406
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+
387466/*
388467 * Returns Query node for expression
389468 *
@@ -912,7 +991,7 @@ force_plan_checks(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr)
912991void
913992plpgsql_check_expr_generic (PLpgSQL_checkstate * cstate , PLpgSQL_expr * expr )
914993{
915- prepare_plan (cstate , expr , 0 , NULL , NULL );
994+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
916995 force_plan_checks (cstate , expr );
917996}
918997
@@ -922,7 +1001,7 @@ plpgsql_check_expr_generic_with_parser_setup(PLpgSQL_checkstate *cstate,
9221001 ParserSetupHook parser_setup ,
9231002 void * arg )
9241003{
925- prepare_plan (cstate , expr , 0 , parser_setup , arg );
1004+ prepare_plan (cstate , expr , 0 , parser_setup , arg , true );
9261005 force_plan_checks (cstate , expr );
9271006}
9281007
@@ -963,7 +1042,7 @@ plpgsql_check_expr_with_scalar_type(PLpgSQL_checkstate *cstate,
9631042 TupleDesc tupdesc ;
9641043 bool is_immutable_null ;
9651044
966- prepare_plan (cstate , expr , 0 , NULL , NULL );
1045+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
9671046 /* record all variables used by the query */
9681047 cstate -> used_variables = bms_add_members (cstate -> used_variables , expr -> paramnos );
9691048
@@ -1036,7 +1115,7 @@ plpgsql_check_returned_expr(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr, bool
10361115 bool is_immutable_null ;
10371116 Oid first_level_typ = InvalidOid ;
10381117
1039- prepare_plan (cstate , expr , 0 , NULL , NULL );
1118+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
10401119
10411120 /* record all variables used by the query, should be after prepare_plan */
10421121 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,
12551334
12561335 PG_TRY ();
12571336 {
1258- prepare_plan (cstate , expr , 0 , NULL , NULL );
1337+ prepare_plan (cstate , expr , 0 , NULL , NULL , true );
12591338 /* record all variables used by the query */
12601339
12611340#if PG_VERSION_NUM >= 140000
@@ -1633,7 +1712,7 @@ plpgsql_check_expr_as_sqlstmt(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr)
16331712 {
16341713 TupleDesc tupdesc ;
16351714
1636- prepare_plan (cstate , expr , 0 , NULL , NULL );
1715+ prepare_plan (cstate , expr , 0 , NULL , NULL , false );
16371716 /* record all variables used by the query */
16381717 cstate -> used_variables = bms_add_members (cstate -> used_variables , expr -> paramnos );
16391718 force_plan_checks (cstate , expr );
0 commit comments