Skip to content

Commit 31ed772

Browse files
authored
Merge pull request #184 from okbob/strict-expr
Strict expr
2 parents 112a932 + 0c723eb commit 31ed772

File tree

2 files changed

+73
-9
lines changed

2 files changed

+73
-9
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ endif
1212
REGRESS_OPTS = --dbname=$(PL_TESTDB)
1313
REGRESS = plpgsql_check_passive plpgsql_check_active plpgsql_check_active-$(MAJORVERSION) plpgsql_check_passive-$(MAJORVERSION)
1414

15-
override CFLAGS += -g
15+
override CFLAGS +="-Wno-error=incompatible-pointer-types"
1616

1717
ifdef NO_PGXS
1818
subdir = contrib/plpgsql_check
@@ -25,4 +25,4 @@ PGXS := $(shell $(PG_CONFIG) --pgxs)
2525
include $(PGXS)
2626
endif
2727

28-
override CFLAGS += -I$(top_builddir)/src/pl/plpgsql/src -Wall
28+
override CFLAGS += -Wno-error=incompatible-pointer-types -I$(top_builddir)/src/pl/plpgsql/src -Wall -g

src/check_expr.c

Lines changed: 71 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,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)
912976
void
913977
plpgsql_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

Comments
 (0)