From 07d46fceb4254b00e79f3d06419cbae13b0ecb5a Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 25 Jan 2021 13:03:11 -0500 Subject: [PATCH] Fix broken ruleutils support for function TRANSFORM clauses. I chanced to notice that this dumped core due to a faulty Assert. To add insult to injury, the output has been misformatted since v11. Obviously we need some regression testing here. Discussion: https://postgr.es/m/d1cc628c-3953-4209-957b-29427acc38c8@www.fastmail.com --- contrib/bool_plperl/expected/bool_plperl.out | 17 ++++++++++++++++- contrib/bool_plperl/expected/bool_plperlu.out | 17 ++++++++++++++++- contrib/bool_plperl/sql/bool_plperl.sql | 6 +++++- contrib/bool_plperl/sql/bool_plperlu.sql | 6 +++++- .../expected/hstore_plpython.out | 16 +++++++++++++--- contrib/hstore_plpython/sql/hstore_plpython.sql | 9 ++++++--- src/backend/utils/adt/ruleutils.c | 3 ++- src/backend/utils/fmgr/funcapi.c | 5 +++-- 8 files changed, 66 insertions(+), 13 deletions(-) diff --git a/contrib/bool_plperl/expected/bool_plperl.out b/contrib/bool_plperl/expected/bool_plperl.out index 84c25acdb4..187df8db96 100644 --- a/contrib/bool_plperl/expected/bool_plperl.out +++ b/contrib/bool_plperl/expected/bool_plperl.out @@ -52,7 +52,7 @@ SELECT perl2undef() IS NULL AS p; --- test transforming to perl CREATE FUNCTION bool2perl(bool, bool, bool) RETURNS void LANGUAGE plperl -TRANSFORM FOR TYPE bool +TRANSFORM FOR TYPE bool, for type boolean -- duplicate to test ruleutils AS $$ my ($x, $y, $z) = @_; @@ -68,6 +68,21 @@ SELECT bool2perl (true, false, NULL); (1 row) +--- test ruleutils +\sf bool2perl +CREATE OR REPLACE FUNCTION public.bool2perl(boolean, boolean, boolean) + RETURNS void + TRANSFORM FOR TYPE boolean, FOR TYPE boolean + LANGUAGE plperl +AS $function$ +my ($x, $y, $z) = @_; + +die("NULL mistransformed") if (defined($z)); +die("TRUE mistransformed to UNDEF") if (!defined($x)); +die("FALSE mistransformed to UNDEF") if (!defined($y)); +die("TRUE mistransformed") if (!$x); +die("FALSE mistransformed") if ($y); +$function$ --- test selecting bool through SPI CREATE FUNCTION spi_test() RETURNS void LANGUAGE plperl diff --git a/contrib/bool_plperl/expected/bool_plperlu.out b/contrib/bool_plperl/expected/bool_plperlu.out index 745ba98933..8337d337e9 100644 --- a/contrib/bool_plperl/expected/bool_plperlu.out +++ b/contrib/bool_plperl/expected/bool_plperlu.out @@ -52,7 +52,7 @@ SELECT perl2undef() IS NULL AS p; --- test transforming to perl CREATE FUNCTION bool2perl(bool, bool, bool) RETURNS void LANGUAGE plperlu -TRANSFORM FOR TYPE bool +TRANSFORM FOR TYPE bool, for type boolean -- duplicate to test ruleutils AS $$ my ($x, $y, $z) = @_; @@ -68,6 +68,21 @@ SELECT bool2perl (true, false, NULL); (1 row) +--- test ruleutils +\sf bool2perl +CREATE OR REPLACE FUNCTION public.bool2perl(boolean, boolean, boolean) + RETURNS void + TRANSFORM FOR TYPE boolean, FOR TYPE boolean + LANGUAGE plperlu +AS $function$ +my ($x, $y, $z) = @_; + +die("NULL mistransformed") if (defined($z)); +die("TRUE mistransformed to UNDEF") if (!defined($x)); +die("FALSE mistransformed to UNDEF") if (!defined($y)); +die("TRUE mistransformed") if (!$x); +die("FALSE mistransformed") if ($y); +$function$ --- test selecting bool through SPI CREATE FUNCTION spi_test() RETURNS void LANGUAGE plperlu diff --git a/contrib/bool_plperl/sql/bool_plperl.sql b/contrib/bool_plperl/sql/bool_plperl.sql index dd99f545ea..b7f570862c 100644 --- a/contrib/bool_plperl/sql/bool_plperl.sql +++ b/contrib/bool_plperl/sql/bool_plperl.sql @@ -33,7 +33,7 @@ SELECT perl2undef() IS NULL AS p; CREATE FUNCTION bool2perl(bool, bool, bool) RETURNS void LANGUAGE plperl -TRANSFORM FOR TYPE bool +TRANSFORM FOR TYPE bool, for type boolean -- duplicate to test ruleutils AS $$ my ($x, $y, $z) = @_; @@ -46,6 +46,10 @@ $$; SELECT bool2perl (true, false, NULL); +--- test ruleutils + +\sf bool2perl + --- test selecting bool through SPI CREATE FUNCTION spi_test() RETURNS void diff --git a/contrib/bool_plperl/sql/bool_plperlu.sql b/contrib/bool_plperl/sql/bool_plperlu.sql index b756b0be67..1480a04330 100644 --- a/contrib/bool_plperl/sql/bool_plperlu.sql +++ b/contrib/bool_plperl/sql/bool_plperlu.sql @@ -33,7 +33,7 @@ SELECT perl2undef() IS NULL AS p; CREATE FUNCTION bool2perl(bool, bool, bool) RETURNS void LANGUAGE plperlu -TRANSFORM FOR TYPE bool +TRANSFORM FOR TYPE bool, for type boolean -- duplicate to test ruleutils AS $$ my ($x, $y, $z) = @_; @@ -46,6 +46,10 @@ $$; SELECT bool2perl (true, false, NULL); +--- test ruleutils + +\sf bool2perl + --- test selecting bool through SPI CREATE FUNCTION spi_test() RETURNS void diff --git a/contrib/hstore_plpython/expected/hstore_plpython.out b/contrib/hstore_plpython/expected/hstore_plpython.out index 1ab5feea93..ecf1dd61bc 100644 --- a/contrib/hstore_plpython/expected/hstore_plpython.out +++ b/contrib/hstore_plpython/expected/hstore_plpython.out @@ -47,19 +47,29 @@ SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']); (1 row) -- test python -> hstore -CREATE FUNCTION test2() RETURNS hstore +CREATE FUNCTION test2(a int, b text) RETURNS hstore LANGUAGE plpythonu TRANSFORM FOR TYPE hstore AS $$ -val = {'a': 1, 'b': 'boo', 'c': None} +val = {'a': a, 'b': b, 'c': None} return val $$; -SELECT test2(); +SELECT test2(1, 'boo'); test2 --------------------------------- "a"=>"1", "b"=>"boo", "c"=>NULL (1 row) +--- test ruleutils +\sf test2 +CREATE OR REPLACE FUNCTION public.test2(a integer, b text) + RETURNS hstore + TRANSFORM FOR TYPE hstore + LANGUAGE plpythonu +AS $function$ +val = {'a': a, 'b': b, 'c': None} +return val +$function$ -- test python -> hstore[] CREATE FUNCTION test2arr() RETURNS hstore[] LANGUAGE plpythonu diff --git a/contrib/hstore_plpython/sql/hstore_plpython.sql b/contrib/hstore_plpython/sql/hstore_plpython.sql index 2c54ee6aaa..b6d98b7dd5 100644 --- a/contrib/hstore_plpython/sql/hstore_plpython.sql +++ b/contrib/hstore_plpython/sql/hstore_plpython.sql @@ -40,15 +40,18 @@ SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']); -- test python -> hstore -CREATE FUNCTION test2() RETURNS hstore +CREATE FUNCTION test2(a int, b text) RETURNS hstore LANGUAGE plpythonu TRANSFORM FOR TYPE hstore AS $$ -val = {'a': 1, 'b': 'boo', 'c': None} +val = {'a': a, 'b': b, 'c': None} return val $$; -SELECT test2(); +SELECT test2(1, 'boo'); + +--- test ruleutils +\sf test2 -- test python -> hstore[] diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a1fbda572..1a844bc461 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3126,13 +3126,14 @@ print_function_trftypes(StringInfo buf, HeapTuple proctup) { int i; - appendStringInfoString(buf, "\n TRANSFORM "); + appendStringInfoString(buf, " TRANSFORM "); for (i = 0; i < ntypes; i++) { if (i != 0) appendStringInfoString(buf, ", "); appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i])); } + appendStringInfoChar(buf, '\n'); } } diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index 2fd2e41f99..717b62907c 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -1357,7 +1357,9 @@ get_func_arg_info(HeapTuple procTup, /* * get_func_trftypes * - * Returns the number of transformed types used by function. + * Returns the number of transformed types used by the function. + * If there are any, a palloc'd array of the type OIDs is returned + * into *p_trftypes. */ int get_func_trftypes(HeapTuple procTup, @@ -1386,7 +1388,6 @@ get_func_trftypes(HeapTuple procTup, ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != OIDOID) elog(ERROR, "protrftypes is not a 1-D Oid array or it contains nulls"); - Assert(nelems >= ((Form_pg_proc) GETSTRUCT(procTup))->pronargs); *p_trftypes = (Oid *) palloc(nelems * sizeof(Oid)); memcpy(*p_trftypes, ARR_DATA_PTR(arr), nelems * sizeof(Oid));