Skip to content

Commit 725f43b

Browse files
committed
initial
1 parent 112a932 commit 725f43b

File tree

1 file changed

+86
-7
lines changed

1 file changed

+86
-7
lines changed

src/check_expr.c

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
static void collect_volatility(PLpgSQL_checkstate *cstate, Query *query);
3838
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);
3940

4041
static CachedPlan * get_cached_plan(PLpgSQL_checkstate *cstate, PLpgSQL_expr *expr, bool *has_result_desc);
4142
static 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)
912991
void
913992
plpgsql_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

Comments
 (0)