Skip to content

Commit 95ce1a2

Browse files
committed
check target for dynamic sql
1 parent 1811e42 commit 95ce1a2

File tree

3 files changed

+172
-2
lines changed

3 files changed

+172
-2
lines changed

expected/plpgsql_check_active.out

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3729,3 +3729,80 @@ select * from plpgsql_check_function('dyn_sql_2', security_warnings := true);
37293729
(3 rows)
37303730

37313731
drop function dyn_sql_2();
3732+
create or replace function dyn_sql_3()
3733+
returns void as $$
3734+
declare r int;
3735+
begin
3736+
execute 'select $1' into r using 1;
3737+
raise notice '%', r;
3738+
end
3739+
$$ language plpgsql;
3740+
select dyn_sql_3();
3741+
NOTICE: 1
3742+
dyn_sql_3
3743+
-----------
3744+
3745+
(1 row)
3746+
3747+
-- should be ok
3748+
select * from plpgsql_check_function('dyn_sql_3');
3749+
plpgsql_check_function
3750+
------------------------
3751+
(0 rows)
3752+
3753+
create or replace function dyn_sql_3()
3754+
returns void as $$
3755+
declare r record;
3756+
begin
3757+
execute 'select $1 as a, $2 as b' into r using 1, 2;
3758+
raise notice '% %', r.a, r.b;
3759+
end
3760+
$$ language plpgsql;
3761+
select dyn_sql_3();
3762+
NOTICE: 1 2
3763+
dyn_sql_3
3764+
-----------
3765+
3766+
(1 row)
3767+
3768+
-- should be ok
3769+
select * from plpgsql_check_function('dyn_sql_3');
3770+
plpgsql_check_function
3771+
------------------------
3772+
(0 rows)
3773+
3774+
create or replace function dyn_sql_3()
3775+
returns void as $$
3776+
declare r record;
3777+
begin
3778+
execute 'create table foo(a int)' into r using 1, 2;
3779+
raise notice '% %', r.a, r.b;
3780+
end
3781+
$$ language plpgsql;
3782+
-- raise a error
3783+
select * from plpgsql_check_function('dyn_sql_3');
3784+
plpgsql_check_function
3785+
-----------------------------------------------------------------------------------------
3786+
warning:00000:4:EXECUTE:values passed to EXECUTE statement by USING clause was not used
3787+
error:XX000:4:EXECUTE:expression does not return data
3788+
(2 rows)
3789+
3790+
create or replace function dyn_sql_3()
3791+
returns void as $$
3792+
declare r1 int; r2 int;
3793+
begin
3794+
execute 'select 1' into r1, r2 using 1, 2;
3795+
raise notice '% %', r1, r2;
3796+
end
3797+
$$ language plpgsql;
3798+
-- raise a error
3799+
select * from plpgsql_check_function('dyn_sql_3');
3800+
plpgsql_check_function
3801+
-----------------------------------------------------------------------------------------
3802+
warning:00000:4:EXECUTE:values passed to EXECUTE statement by USING clause was not used
3803+
warning:00000:4:EXECUTE:too few attributes for target variables
3804+
Detail: There are more target variables than output columns in query.
3805+
Hint: Check target variables in SELECT INTO statement.
3806+
(4 rows)
3807+
3808+
drop function dyn_sql_3();

sql/plpgsql_check_active.sql

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,3 +2713,57 @@ select dyn_sql_2(); --should to fail
27132713
select * from plpgsql_check_function('dyn_sql_2', security_warnings := true);
27142714

27152715
drop function dyn_sql_2();
2716+
2717+
create or replace function dyn_sql_3()
2718+
returns void as $$
2719+
declare r int;
2720+
begin
2721+
execute 'select $1' into r using 1;
2722+
raise notice '%', r;
2723+
end
2724+
$$ language plpgsql;
2725+
2726+
select dyn_sql_3();
2727+
2728+
-- should be ok
2729+
select * from plpgsql_check_function('dyn_sql_3');
2730+
2731+
create or replace function dyn_sql_3()
2732+
returns void as $$
2733+
declare r record;
2734+
begin
2735+
execute 'select $1 as a, $2 as b' into r using 1, 2;
2736+
raise notice '% %', r.a, r.b;
2737+
end
2738+
$$ language plpgsql;
2739+
2740+
select dyn_sql_3();
2741+
2742+
-- should be ok
2743+
select * from plpgsql_check_function('dyn_sql_3');
2744+
2745+
create or replace function dyn_sql_3()
2746+
returns void as $$
2747+
declare r record;
2748+
begin
2749+
execute 'create table foo(a int)' into r using 1, 2;
2750+
raise notice '% %', r.a, r.b;
2751+
end
2752+
$$ language plpgsql;
2753+
2754+
-- raise a error
2755+
select * from plpgsql_check_function('dyn_sql_3');
2756+
2757+
create or replace function dyn_sql_3()
2758+
returns void as $$
2759+
declare r1 int; r2 int;
2760+
begin
2761+
execute 'select 1' into r1, r2 using 1, 2;
2762+
raise notice '% %', r1, r2;
2763+
end
2764+
$$ language plpgsql;
2765+
2766+
-- raise a error
2767+
select * from plpgsql_check_function('dyn_sql_3');
2768+
2769+
drop function dyn_sql_3();

src/stmtwalk.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,22 @@ dynsql_parser_setup(struct ParseState *pstate, DynSQLParams *params)
104104
pstate->p_ref_hook_state = (void *) params;
105105
}
106106

107+
/*
108+
* Returns true if record variable has assigned some type
109+
*/
110+
static bool
111+
has_assigned_tupdesc(PLpgSQL_checkstate *cstate, PLpgSQL_rec *rec)
112+
{
113+
PLpgSQL_rec *target = (PLpgSQL_rec *) (cstate->estate->datums[rec->dno]);
114+
115+
Assert(rec->dtype == PLPGSQL_DTYPE_REC);
116+
117+
if (recvar_tupdesc(target))
118+
return true;
119+
120+
return false;
121+
}
122+
107123
#if PG_VERSION_NUM >= 110000
108124

109125
static void
@@ -1273,6 +1289,28 @@ plpgsql_check_stmt(PLpgSQL_checkstate *cstate, PLpgSQL_stmt *stmt, int *closing,
12731289

12741290
if (dynexpr.plan)
12751291
{
1292+
if (stmt_dynexecute->into)
1293+
{
1294+
1295+
#if PG_VERSION_NUM >= 110000
1296+
1297+
check_variable(cstate, stmt_dynexecute->target);
1298+
plpgsql_check_assignment_to_variable(cstate,
1299+
&dynexpr,
1300+
stmt_dynexecute->target, -1);
1301+
1302+
#else
1303+
1304+
plpgsql_check_row_or_rec(cstate, stmt_dynexecute->row, stmt_dynexecute->rec);
1305+
plpgsql_check_assignment(cstate,
1306+
&dynexpr,
1307+
stmt_dynexecute->rec,
1308+
stmt_dynexecute, -1);
1309+
1310+
#endif
1311+
1312+
}
1313+
12761314
SPI_freeplan(dynexpr.plan);
12771315
cstate->exprs = list_delete_ptr(cstate->exprs, &dynexpr);
12781316
}
@@ -1309,13 +1347,14 @@ plpgsql_check_stmt(PLpgSQL_checkstate *cstate, PLpgSQL_stmt *stmt, int *closing,
13091347

13101348
check_variable(cstate, stmt_dynexecute->target);
13111349

1312-
if (stmt_dynexecute->target->dtype == PLPGSQL_DTYPE_REC)
1350+
if (stmt_dynexecute->target->dtype == PLPGSQL_DTYPE_REC &&
1351+
!has_assigned_tupdesc(cstate, (PLpgSQL_rec *) stmt_dynexecute->target))
13131352

13141353
#else
13151354

13161355
plpgsql_check_row_or_rec(cstate, stmt_dynexecute->row, stmt_dynexecute->rec);
13171356

1318-
if (stmt_dynexecute->rec != NULL)
1357+
if (stmt_dynexecute->rec != NULL && !has_assigned_tupdesc(stmt_dynexecute->rec))
13191358

13201359
#endif
13211360

0 commit comments

Comments
 (0)