@@ -2965,13 +2965,22 @@ select testseq();
2965
2965
ERROR: "test_table" is not a sequence
2966
2966
CONTEXT: SQL statement "SELECT nextval('test_table')"
2967
2967
PL/pgSQL function testseq() line 3 at PERFORM
2968
- select * from plpgsql_check_function('testseq()');
2968
+ select * from plpgsql_check_function('testseq()', fatal_errors := false );
2969
2969
plpgsql_check_function
2970
2970
------------------------------------------------------
2971
2971
error:42809:3:PERFORM:"test_table" is not a sequence
2972
2972
Query: SELECT nextval('test_table')
2973
2973
-- ^
2974
- (3 rows)
2974
+ error:42809:4:PERFORM:"test_table" is not a sequence
2975
+ Query: SELECT currval('test_table')
2976
+ -- ^
2977
+ error:42809:5:PERFORM:"test_table" is not a sequence
2978
+ Query: SELECT setval('test_table', 10)
2979
+ -- ^
2980
+ error:42809:6:PERFORM:"test_table" is not a sequence
2981
+ Query: SELECT setval('test_table', 10, true)
2982
+ -- ^
2983
+ (12 rows)
2975
2984
2976
2985
drop function testseq();
2977
2986
drop table test_table;
@@ -3550,6 +3559,61 @@ select * from plpgsql_check_function('test_crash', fatal_errors := true);
3550
3559
(5 rows)
3551
3560
3552
3561
drop function test_crash();
3562
+ -- fix false alarm reported by Piotr Stepniewski
3563
+ create or replace function public.fx()
3564
+ returns void
3565
+ language plpgsql
3566
+ as $function$
3567
+ begin
3568
+ raise exception 'xxx';
3569
+ end;
3570
+ $function$;
3571
+ -- show raise nothing
3572
+ select * from plpgsql_check_function('fx()');
3573
+ plpgsql_check_function
3574
+ ------------------------
3575
+ (0 rows)
3576
+
3577
+ create table errtab(
3578
+ message text,
3579
+ code character(5)
3580
+ );
3581
+ create or replace function public.fx()
3582
+ returns void
3583
+ language plpgsql
3584
+ as $function$
3585
+ declare
3586
+ var errtab%rowtype;
3587
+ begin
3588
+ raise exception using message = var.message, errcode = var.code;
3589
+ end;
3590
+ $function$;
3591
+ -- should not to crash
3592
+ select * from plpgsql_check_function('fx()');
3593
+ plpgsql_check_function
3594
+ ------------------------
3595
+ (0 rows)
3596
+
3597
+ create or replace function public.fx()
3598
+ returns void
3599
+ language plpgsql
3600
+ as $function$
3601
+ declare
3602
+ var errtab%rowtype;
3603
+ begin
3604
+ raise exception using message = var.message, errcode = var.code, hint = var.hint;
3605
+ end;
3606
+ $function$;
3607
+ -- should not to crash
3608
+ select * from plpgsql_check_function('fx()');
3609
+ plpgsql_check_function
3610
+ ------------------------------------------------------
3611
+ error:42703:5:RAISE:record "var" has no field "hint"
3612
+ Query: SELECT var.hint
3613
+ -- ^
3614
+ (3 rows)
3615
+
3616
+ drop function fx();
3553
3617
create or replace function foo_format(a text, b text)
3554
3618
returns void as $$
3555
3619
declare s text;
@@ -3610,16 +3674,17 @@ select * from plpgsql_check_function('dyn_sql_1', security_warnings := true, fat
3610
3674
Detail: The EXECUTE expression is SQL injection vulnerable.
3611
3675
Hint: Use quote_ident, quote_literal or format function to secure variable.
3612
3676
warning:00000:13:EXECUTE:values passed to EXECUTE statement by USING clause was not used
3613
- error:XX000:14:EXECUTE:there is no parameter $1
3614
- Context: SQL statement "select $1"
3615
- (13 rows)
3677
+ error:42P02:14:EXECUTE:there is no parameter $1
3678
+ Query: select $1
3679
+ -- ^
3680
+ (14 rows)
3616
3681
3617
3682
drop function dyn_sql_1();
3618
3683
create type tp as (a int, b int);
3619
3684
create or replace function dyn_sql_2()
3620
3685
returns void as $$
3621
3686
declare
3622
- r tp;
3687
+ r tp;
3623
3688
result int;
3624
3689
begin
3625
3690
select 10 a, 20 b into r;
@@ -3634,7 +3699,189 @@ select * from plpgsql_check_function('dyn_sql_2', security_warnings := true);
3634
3699
------------------------------------------------------------
3635
3700
error:42703:9:EXECUTE:column "c" not found in data type tp
3636
3701
Query: select $1.c
3637
- -- ^
3702
+ -- ^
3703
+ (3 rows)
3704
+
3705
+ drop function dyn_sql_2();
3706
+ drop type tp;
3707
+ /*
3708
+ * Should not to work
3709
+ *
3710
+ * note: plpgsql doesn't support passing some necessary details for record
3711
+ * type. The parser setup for dynamic SQL column doesn't use ref hooks, and
3712
+ * then it cannot to pass TupleDesc info to query anyway.
3713
+ */
3714
+ create or replace function dyn_sql_2()
3715
+ returns void as $$
3716
+ declare
3717
+ r record;
3718
+ result int;
3719
+ begin
3720
+ select 10 a, 20 b into r;
3721
+ raise notice '%', r.a;
3722
+ execute 'select $1.a + $1.b' into result using r;
3723
+ raise notice '%', result;
3724
+ end;
3725
+ $$ language plpgsql;
3726
+ select dyn_sql_2(); --should to fail
3727
+ NOTICE: 10
3728
+ ERROR: could not identify column "a" in record data type
3729
+ LINE 1: select $1.a + $1.b
3730
+ ^
3731
+ QUERY: select $1.a + $1.b
3732
+ CONTEXT: PL/pgSQL function dyn_sql_2() line 8 at EXECUTE
3733
+ select * from plpgsql_check_function('dyn_sql_2', security_warnings := true);
3734
+ plpgsql_check_function
3735
+ -------------------------------------------------------------------------
3736
+ error:42703:8:EXECUTE:could not identify column "a" in record data type
3737
+ Query: select $1.a + $1.b
3738
+ -- ^
3638
3739
(3 rows)
3639
3740
3640
3741
drop function dyn_sql_2();
3742
+ create or replace function dyn_sql_3()
3743
+ returns void as $$
3744
+ declare r int;
3745
+ begin
3746
+ execute 'select $1' into r using 1;
3747
+ raise notice '%', r;
3748
+ end
3749
+ $$ language plpgsql;
3750
+ select dyn_sql_3();
3751
+ NOTICE: 1
3752
+ dyn_sql_3
3753
+ -----------
3754
+
3755
+ (1 row)
3756
+
3757
+ -- should be ok
3758
+ select * from plpgsql_check_function('dyn_sql_3');
3759
+ plpgsql_check_function
3760
+ ------------------------
3761
+ (0 rows)
3762
+
3763
+ create or replace function dyn_sql_3()
3764
+ returns void as $$
3765
+ declare r record;
3766
+ begin
3767
+ execute 'select $1 as a, $2 as b' into r using 1, 2;
3768
+ raise notice '% %', r.a, r.b;
3769
+ end
3770
+ $$ language plpgsql;
3771
+ select dyn_sql_3();
3772
+ NOTICE: 1 2
3773
+ dyn_sql_3
3774
+ -----------
3775
+
3776
+ (1 row)
3777
+
3778
+ -- should be ok
3779
+ select * from plpgsql_check_function('dyn_sql_3');
3780
+ plpgsql_check_function
3781
+ ------------------------
3782
+ (0 rows)
3783
+
3784
+ create or replace function dyn_sql_3()
3785
+ returns void as $$
3786
+ declare r record;
3787
+ begin
3788
+ execute 'create table foo(a int)' into r using 1, 2;
3789
+ raise notice '% %', r.a, r.b;
3790
+ end
3791
+ $$ language plpgsql;
3792
+ -- raise a error
3793
+ select * from plpgsql_check_function('dyn_sql_3');
3794
+ plpgsql_check_function
3795
+ -----------------------------------------------------------------------------------------
3796
+ warning:00000:4:EXECUTE:values passed to EXECUTE statement by USING clause was not used
3797
+ error:XX000:4:EXECUTE:expression does not return data
3798
+ (2 rows)
3799
+
3800
+ create or replace function dyn_sql_3()
3801
+ returns void as $$
3802
+ declare r1 int; r2 int;
3803
+ begin
3804
+ execute 'select 1' into r1, r2 using 1, 2;
3805
+ raise notice '% %', r1, r2;
3806
+ end
3807
+ $$ language plpgsql;
3808
+ -- raise a error
3809
+ select * from plpgsql_check_function('dyn_sql_3');
3810
+ plpgsql_check_function
3811
+ -----------------------------------------------------------------------------------------
3812
+ warning:00000:4:EXECUTE:values passed to EXECUTE statement by USING clause was not used
3813
+ warning:00000:4:EXECUTE:too few attributes for target variables
3814
+ Detail: There are more target variables than output columns in query.
3815
+ Hint: Check target variables in SELECT INTO statement.
3816
+ (4 rows)
3817
+
3818
+ drop function dyn_sql_3();
3819
+ create or replace function dyn_sql_3()
3820
+ returns void as $$
3821
+ declare r record;
3822
+ begin
3823
+ for r in execute 'select 1 as a, 2 as b'
3824
+ loop
3825
+ raise notice '%', r.a;
3826
+ end loop;
3827
+ end
3828
+ $$ language plpgsql;
3829
+ -- should be ok
3830
+ select * from plpgsql_check_function('dyn_sql_3');
3831
+ plpgsql_check_function
3832
+ ------------------------
3833
+ (0 rows)
3834
+
3835
+ drop function dyn_sql_3();
3836
+ create or replace function dyn_sql_3()
3837
+ returns void as $$
3838
+ declare r record;
3839
+ begin
3840
+ for r in execute 'select 1 as a, 2 as b'
3841
+ loop
3842
+ raise notice '%', r.c;
3843
+ end loop;
3844
+ end
3845
+ $$ language plpgsql;
3846
+ -- should be error
3847
+ select * from plpgsql_check_function('dyn_sql_3');
3848
+ plpgsql_check_function
3849
+ -------------------------------------------------
3850
+ error:42703:6:RAISE:record "r" has no field "c"
3851
+ Context: SQL statement "SELECT r.c"
3852
+ (2 rows)
3853
+
3854
+ drop function dyn_sql_3();
3855
+ create or replace function dyn_sql_4()
3856
+ returns table(ax int, bx int) as $$
3857
+ begin
3858
+ return query execute 'select 10, 20';
3859
+ return;
3860
+ end;
3861
+ $$ language plpgsql;
3862
+ -- should be ok
3863
+ select * from plpgsql_check_function('dyn_sql_4()');
3864
+ plpgsql_check_function
3865
+ ------------------------
3866
+ (0 rows)
3867
+
3868
+ create or replace function dyn_sql_4()
3869
+ returns table(ax int, bx int) as $$
3870
+ begin
3871
+ return query execute 'select 10, 20, 30';
3872
+ return;
3873
+ end;
3874
+ $$ language plpgsql;
3875
+ select * from dyn_sql_4();
3876
+ ERROR: structure of query does not match function result type
3877
+ DETAIL: Number of returned columns (3) does not match expected column count (2).
3878
+ CONTEXT: PL/pgSQL function dyn_sql_4() line 3 at RETURN QUERY
3879
+ -- should be error
3880
+ select * from plpgsql_check_function('dyn_sql_4()');
3881
+ plpgsql_check_function
3882
+ -----------------------------------------------------------------------------------
3883
+ error:42804:3:RETURN QUERY:structure of query does not match function result type
3884
+ Detail: Number of returned columns (3) does not match expected column count (2).
3885
+ (2 rows)
3886
+
3887
+ drop function dyn_sql_4();
0 commit comments