Hello, I am returning back to message http://archives.postgresql.org/pgsql-general/2010-02/msg00119.php
Our implementation of variadic parameters missing correct handling test on NULL, and test on non constant value. This patch add correct tests for variadic parameters. Probably Gen_fmgrtab.pl have needs some work - there are magic constants for InvalidOid and ANYOID. Regards Pavel Stehule
*** ./src/backend/executor/execQual.c.orig 2010-01-11 16:31:04.000000000 +0100 --- ./src/backend/executor/execQual.c 2010-02-09 16:08:07.000000000 +0100 *************** *** 1601,1606 **** --- 1601,1612 ---- break; } } + + /* + * If function is variadic, then check a variadic array argument. + */ + if (fcache->func.fn_vararg && ARR_HASNULL(DatumGetArrayTypeP(fcinfo->arg[fcinfo->nargs - 1]))) + callit = false; } if (callit) *************** *** 1738,1743 **** --- 1744,1759 ---- return (Datum) 0; } } + + /* + * Check variadic argument. If array with variadic arguments + * contains NULLs, then return NULL. + */ + if (fcache->func.fn_vararg && ARR_HASNULL(DatumGetArrayTypeP(fcinfo->arg[fcinfo->nargs - 1]))) + { + *isNull = true; + return (Datum) 0; + } } pgstat_init_function_usage(fcinfo, &fcusage); *************** *** 1805,1810 **** --- 1821,1836 ---- return (Datum) 0; } } + + /* + * If function has variadic argument, skip calling + * when variadic array contains NULL values. + */ + if (fcache->func.fn_vararg && ARR_HASNULL(DatumGetArrayTypeP(fcinfo.arg[fcinfo.nargs - 1]))) + { + *isNull = true; + return (Datum) 0; + } } pgstat_init_function_usage(&fcinfo, &fcusage); *** ./src/backend/optimizer/util/clauses.c.orig 2010-01-19 17:33:33.000000000 +0100 --- ./src/backend/optimizer/util/clauses.c 2010-02-09 12:04:57.000000000 +0100 *************** *** 3588,3599 **** /* * Check for constant inputs and especially constant-NULL inputs. */ ! foreach(arg, args) { ! if (IsA(lfirst(arg), Const)) ! has_null_input |= ((Const *) lfirst(arg))->constisnull; ! else ! has_nonconst_input = true; } /* --- 3588,3643 ---- /* * Check for constant inputs and especially constant-NULL inputs. */ ! if (OidIsValid(funcform->provariadic) && funcform->provariadic != ANYOID) { ! foreach(arg, args) ! { ! if (lnext(arg) != NULL) ! { ! /* Check non variriadic arguments. */ ! if (IsA(lfirst(arg), Const)) ! has_null_input |= ((Const *) lfirst(arg))->constisnull; ! else ! has_nonconst_input = true; ! } ! else ! { ! Node *vararg = lfirst(arg); ! ListCell *lc; ! ! /* Variadic argument is array. Check fields */ ! if (IsA(vararg, ArrayExpr)) ! { ! foreach(lc, ((ArrayExpr *) vararg)->elements) ! { ! if (IsA(lfirst(lc), Const)) ! has_null_input |= ((Const *) lfirst(lc))->constisnull; ! else ! has_nonconst_input = true; ! } ! } ! else ! { ! Assert(IsA(vararg, Const)); ! ! if (((Const *) vararg)->constisnull) ! has_null_input = true; ! else ! has_null_input |= ARR_HASNULL(DatumGetArrayTypeP(((Const *) vararg)->constvalue)); ! } ! } ! } ! } ! else ! { ! /* simpler and faster form for nonvariadic function or variadic "any" */ ! foreach(arg, args) ! { ! if (IsA(lfirst(arg), Const)) ! has_null_input |= ((Const *) lfirst(arg))->constisnull; ! else ! has_nonconst_input = true; ! } } /* *** ./src/backend/utils/fmgr/fmgr.c.orig 2010-01-07 05:53:34.000000000 +0100 --- ./src/backend/utils/fmgr/fmgr.c 2010-02-09 14:42:42.000000000 +0100 *************** *** 18,23 **** --- 18,24 ---- #include "access/tuptoaster.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" + #include "catalog/pg_type.h" #include "executor/functions.h" #include "executor/spi.h" #include "lib/stringinfo.h" *************** *** 199,204 **** --- 200,206 ---- finfo->fn_nargs = fbp->nargs; finfo->fn_strict = fbp->strict; finfo->fn_retset = fbp->retset; + finfo->fn_vararg = fbp->vararg; finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ finfo->fn_addr = fbp->func; finfo->fn_oid = functionId; *************** *** 216,221 **** --- 218,224 ---- finfo->fn_nargs = procedureStruct->pronargs; finfo->fn_strict = procedureStruct->proisstrict; finfo->fn_retset = procedureStruct->proretset; + finfo->fn_vararg = OidIsValid(procedureStruct->provariadic) && procedureStruct->provariadic != ANYOID; /* * If it has prosecdef set, or non-null proconfig, use *** ./src/backend/utils/Gen_fmgrtab.pl.orig 2010-01-05 21:23:32.000000000 +0100 --- ./src/backend/utils/Gen_fmgrtab.pl 2010-02-09 14:23:04.000000000 +0100 *************** *** 77,82 **** --- 77,83 ---- oid => $row->{oid}, strict => $row->{proisstrict}, retset => $row->{proretset}, + vararg => ($row->{provariadic} != 0 && $row->{provariadic} != 2276) ? 't' : 'f', nargs => $row->{pronargs}, prosrc => $row->{prosrc}, }; *************** *** 176,182 **** foreach my $s (sort {$a->{oid} <=> $b->{oid}} @fmgr) { print T ! " { $s->{oid}, \"$s->{prosrc}\", $s->{nargs}, $bmap{$s->{strict}}, $bmap{$s->{retset}}, $s->{prosrc} },\n"; } # And add the file footers. --- 177,183 ---- foreach my $s (sort {$a->{oid} <=> $b->{oid}} @fmgr) { print T ! " { $s->{oid}, \"$s->{prosrc}\", $s->{nargs}, $bmap{$s->{strict}}, $bmap{$s->{retset}}, $bmap{$s->{vararg}}, $s->{prosrc} },\n"; } # And add the file footers. *** ./src/include/fmgr.h.orig 2010-02-08 21:39:52.000000000 +0100 --- ./src/include/fmgr.h 2010-02-09 14:30:55.000000000 +0100 *************** *** 49,54 **** --- 49,55 ---- * count */ bool fn_strict; /* function is "strict" (NULL in => NULL out) */ bool fn_retset; /* function returns a set */ + bool fn_vararg; /* function has a variadic (packed) parameter */ unsigned char fn_stats; /* collect stats if track_functions > this */ void *fn_extra; /* extra space for use by handler */ MemoryContext fn_mcxt; /* memory context to store fn_extra in */ *** ./src/include/utils/fmgrtab.h.orig 2010-01-02 17:58:10.000000000 +0100 --- ./src/include/utils/fmgrtab.h 2010-02-09 13:47:40.000000000 +0100 *************** *** 29,34 **** --- 29,35 ---- short nargs; /* 0..FUNC_MAX_ARGS, or -1 if variable count */ bool strict; /* T if function is "strict" */ bool retset; /* T if function returns a set */ + bool vararg; /* T if function has a packed variadic argument */ PGFunction func; /* pointer to compiled function */ } FmgrBuiltin; *** ./src/test/regress/expected/plpgsql.out.orig 2010-01-26 17:33:40.000000000 +0100 --- ./src/test/regress/expected/plpgsql.out 2010-02-09 16:24:22.000000000 +0100 *************** *** 4107,4109 **** --- 4107,4165 ---- (1 row) drop function unreserved_test(); + -- check strict variadic functions + create or replace function concate(variadic str text[]) + returns text as $$ + declare + s text = ''; + f text; + begin + raise notice 'start concate'; + for f in select unnest(str) + loop + s := s || coalesce(f,''); + end loop; + return s; + end; + $$ language plpgsql strict; + create table strict_test(a text); + insert into strict_test values('some text'),(NULL), ('some text'); + select concate('a','b') from strict_test; + NOTICE: start concate + NOTICE: start concate + NOTICE: start concate + concate + --------- + ab + ab + ab + (3 rows) + + select concate('a','b', null) from strict_test; + concate + --------- + + + + (3 rows) + + select concate(a,'b') from strict_test; + NOTICE: start concate + NOTICE: start concate + concate + ------------ + some textb + + some textb + (3 rows) + + select concate(a, null) from strict_test; + concate + --------- + + + + (3 rows) + + drop function concate(variadic text[]); + drop table strict_test; *** ./src/test/regress/sql/plpgsql.sql.orig 2010-01-26 17:33:40.000000000 +0100 --- ./src/test/regress/sql/plpgsql.sql 2010-02-09 16:23:41.000000000 +0100 *************** *** 3254,3256 **** --- 3254,3284 ---- select unreserved_test(); drop function unreserved_test(); + + -- check strict variadic functions + create or replace function concate(variadic str text[]) + returns text as $$ + declare + s text = ''; + f text; + begin + raise notice 'start concate'; + for f in select unnest(str) + loop + s := s || coalesce(f,''); + end loop; + return s; + end; + $$ language plpgsql strict; + + create table strict_test(a text); + + insert into strict_test values('some text'),(NULL), ('some text'); + + select concate('a','b') from strict_test; + select concate('a','b', null) from strict_test; + select concate(a,'b') from strict_test; + select concate(a, null) from strict_test; + + drop function concate(variadic text[]); + drop table strict_test;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers