Project: YugabyteDB (App Definition and Development / Database)
File: variadic-and-polymorphic-subprograms.md

title: Variadic and polymorphic subprograms [YSQL] headerTitle: Variadic and polymorphic subprograms linkTitle: Variadic and polymorphic subprograms description: Defines Variadic and polymorphic subprograms and explains their purpose [YSQL]. menu: v2.18: identifier: variadic-and-polymorphic-subprograms parent: user-defined-subprograms-and-anon-blocks weight: 50 type: docs A variadic subprogram allows one of its formal arguments to have any number of actual values, provided as a comma-separated list, just like the built-in functions least() and greatest() allow. A polymorphic subprogram allows both its formal arguments and, for a function, its return data type to be declared using so-called polymorphic data types (for example, anyelement or anyarry) so that actual arguments with various different data types can be used, in different invocations of the subprogram, for the same formal arguments. Correspondingly, the data type(s) of the actual arguments will determine the data type of a function's return value. You can declare variadic or polymorphic arguments, or polymorphic return values, for both language sql and language plpgsql subprograms. You mark a subprogram's formal argument with the keyword variadic as an alternative to marking it with in, out, or inout. See the syntax rule. A subprogram can have at most just one variadic formal argument; and it must be last in the list of arguments. Breaking this rule causes this syntax error: ```outout 42P13: VARIADIC parameter must be the last input parameter ``` Though there are built-in least() and greatest() variadic functions (corresponding to the built-in min() and max() aggregate functions), there is no built-in mean() variadic function to correspond to the built-in avg() aggregate function. It therefore provides a nice example of how to implement your own. Try this: ```plpgsql create function mean( arr variadic numeric[] = array[1.1, 2.1, 3.1]) returns numeric immutable set searchpath = pgcatalog, pg_temp language sql as $body$ with c(v) as ( select unnest(arr)) select avg(v) from c; $body$; select to_char(mean(17.1, 6.5, 3.4), '99.99') as "mean"; ``` The result is 9.00, as is expected. You can see immediately that each actual value that you provide for the variadic formal argument must have (or be typecastable to) the data type of the array with which this argument is declared. You can transform the language sql version trivially into a language plpgsql function simply by re-writing the body thus: ```output ... language plpgsql as $body$ begin return ( with c(v) as ( select unnest(arr)) select avg(v) from c); end; $body$; ``` The example defines a default value for the variadic formal argument simply to make the point that this is legal. It's probably unlikely that you'll have a use case that benefits from this. The example makes the point that declaring a formal argument as variadic is simply syntax sugar for what you could achieve without this device by declaring the argument ordinarily as an array. Try this: ```plpgsql create function mean(arr numeric[]) returns numeric immutable set searchpath = pgcatalog, pg_temp language sql as $body$ with c(v) as ( select unnest(arr)) select avg(v) from c; $body$; select to_char(mean(array[17.1, 6.5, 3.4]), '99.99') as "mean"; ``` Its body is identical to that of the variadic language sql versionand it produces the same result. The choice between the two approaches is determined entirely by usability: it's less cluttered to invoke the function with a bare list of values than to surround them with the array[] constructor. On the other hand, if the common case is that the values are already available as an array, then you may just as well not choose the variadic

Notice, though, that syntax is defined that lets you invoke a variadic function using an array of values as the actual argument. Re-create the variadic form of mean(). (It doesn't matter whether you use the language sql or the language plpgsql version.) Now invoke it thus: ```plpgsql select to_char(mean(variadic array[17.1, 6.5, 3.4]), '99.99') as "mean"; ``` This runs without error and produces the expected 9.00 result. These are the polymorphic data types: anyelement anyarray anynonarray anyenum anyrange See the PostgreSQL documentation section . These data types are a subset of the so-called pseudo-Types. See the PostgreSQL documentation section . Notice this from the Polymorphic Types account: Note that anynonarray and anyenum do not represent separate type variables; they are the same type as anyelement, just with an additional constraint. For example, declaring a function as f(anyelement, anyenum) is equivalent to declaring it as f(anyenum, anyenum): both actual arguments have to be the same enum type. This section illustrates the use of just anyelement and anyarray. See the PostgreSQL documentation section for more information on this topic. Try this. It simply shows an example of how to use the feature. ```plpgsql create function my_typeof(v in text) returns text immutable set searchpath = pgcatalog, pg_temp language sql as $body$ select 'non-polymorphic text: '||pg_typeof(v)::text; $body$; create function my_typeof(v in int) returns text immutable set searchpath = pgcatalog, pg_temp language sql as $body$ select 'non-polymorphic int: '||pg_typeof(v)::text; $body$; create function my_typeof(v in anyelement) returns text immutable set searchpath = pgcatalog, pg_temp language sql as $body$ select 'polymorphic: '||pg_typeof(v)::text; $body$; create type s.ct as (b boolean, i int); \x on \t on select (select my_typeof('dog')) as "dog", (select my_typeof(42)) as "42", (select my_typeof(true)) as "true", (select my_typeof((true, 99)::s.ct)) as "(true, 99)::s.ct"; \t off \x off ``` It runs without error and produces this result: ```output dog | non-polymorphic text: text 42 | non-polymorphic int: integer true | polymorphic: boolean (true, 99)::s.ct | polymorphic: s.ct ``` The function mytypeof() adds no functionality beyond what the pgtypeof() built-in function exposes. So the code example has no value beyond this pedagogy: The built-in function pgtypeof() is itself polymorphic. The \\df meta-command shows that its input formal argument has the datatype "any". (The double quotes are used because any is a SQL reserved word.) Notice that the designer of this built-in could just as well have defined it with an input formal argument of data type anyelement_. A user-defined subprogram with an input formal argument of data type anyelement can be among a set of distinguishable overloads where others in the set have input formal arguments of data type, for example, text or int. Try this. It simply shows another example of how to use the feature. ```plpgsql create function arrayfromtwo_elements( e1 in anyelement, e2 in anyelement) returns anyarray immutable set searchpath = pgcatalog, pg_temp language sql as $body$ select array[e1, e2]; $body$; create type s.ct as (k int, t text); create view pg_temp.v(arr) as select arrayfromtwo_elements((17, 'dog')::ct, (42, 'cat')::ct); with c(e) as ( select unnest(arr) from pg_temp.v) select (e).k, (e).t from c; ``` It produces this result: ```plpgsql k | t -+-- 17 | dog 42 | cat ``` Here, too, the function arrayfromtwoelements()_ adds no functionality beyond what the native array constructor exposes. So the code example has no value beyond this pedagogy: It shows that when two elements (i.e. scalar values) of data type ct are listed in an array constructor, the emergent data type of the result is ct[]. In other words, when the input formal arguments have the data type anyelement, the returns anyarray clause automatically accommodates this

This makes the same point more explicitly: ```plpgsql select pgtypeof(arr) from pgtemp.v; ``` This is the result: ```output pg_typeof -- ct[] ``` A variadic function can be polymorphic too. Simply declare its last formal argument as variadic anyarray. Argument matching and the determination of the actual result type behave as you'd expect. Don't be concerned about the artificiality of this example. Its aim is simply to illustrate how the low-level functionality works. If the variadic input is a list of numeric values, then the body code detects this (using the pgtypeof() built-in function) and branches to use the code that the non-polymorphic implementation shown in the section above uses. But if the variadic input is a list of s.onecharacter values (where s.onecharacter is a user-defined domain based on the native text_ data type whose constraint ensures that the length is exactly one character), then the implementation uses a different method: It converts each single character text value to a numeric value with the ascii() built in function. It calculates the mean of these numeric values in the same way that the implementation for the mean of numeric input values does. It uses the round() built in function, together with the ::int typecast to convert this mean to the nearest int value. It converts this int value back to a single character text value using the chr() built in function. Create the constraint function, the domain, and the variadic polymorphic function thus: ```plpgsql create schema s; create function s.isonecharacter(t in text) returns boolean set searchpath = pgcatalog, pg_text language plpgsql as $body$ declare msg constant text not null := '%s is not exactly one character character'; begin assert length(t) = 1, format(msg, t); return true; end; $body$; create domain s.one_character as text constraint isonecharacter check(s.isonecharacter(value)); create function s.mean(arr variadic anyarray) returns anyelement immutable set searchpath = pgcatalog, pg_temp language plpgsql as $body$ declare n numeric not null := 0.0; t s.one_character not null := 'a'; begin assert cardinality(arr) > 0, 'cardinality of arr < 1'; declare dtype constant regtype not null := pgtypeof(arr[1]); begin case d_type when pg_typeof(n) then with c(v) as (select unnest(arr)) select avg(v) from c into n; return n; when pg_typeof(t) then with c(v) as (select unnest(arr)) select chr(round(avg(ascii(v)))::int) from c into t; return t; end case; end; end; $body$; ``` Test it with a list of numeric values. ```plpgsql select to_char(s.mean(17.1, 6.5, 3.4), '99.99') as "mean"; ``` This is the result: ```output mean -- 9.00 ``` Now test it with a list of s.onecharacter_ values: ```plpgsql select s.mean( 'c'::s.one_character, 'e'::s.one_character, 'g'::s.one_character); ``` This is the result: ```output mean e ``` Notice that the argument of the returns keyword in the function header is anyelement. This has a strictly defined meaning. The internal implementation detects the data type of the input array's elements. And then it checks that, however the user-defined implementation manages this, the data type of the computed to-be-returned value matches the data type of the input array's elements. If this requirement isn't met, then you're likely to get a run-time error when the run-time interpretation of your code attempts to typecast the value that you attempt to return to the expected actual data type for anyelement in the present execution. For example, if you simply use this hard-coded return: ```output return true::boolean; ``` then you'll get this run-time error: ```output P0004: true is not exactly one character character ``` because, of course, the text typecast of the boolean value has four

You can experiment by adding a leg to the case statement to handle a variadic input list of some user-defined composite type valuesand then hard-code a non-composite value for the return argument. You'll see this run-time error: ``` 42804: cannot return non-composite value from function returning composite type ``` This example emphasizes the meaning of writing returns anyelement by specifying that the returned mean should always be numericboth when the variadic input is a list of scalar numeric values and when it's a list of rectangle composite type values. Here, you simply write what you want: returns numeric. Create the function thus: ```plpgsql create schema s; create type s.rectangle as (len numeric, wid numeric); create function s.mean(arr variadic anyarray) returns numeric immutable set searchpath = pgcatalog, pg_temp language plpgsql as $body$ declare c constant int not null := cardinality(arr); n numeric not null := 0.0; r s.rectangle not null := (1.0, 1.0); tot numeric not null := 0.0; begin assert c > 0, 'cardinality of arr < 1'; declare dtype constant regtype not null := pgtypeof(arr[1]); begin case d_type when pg_typeof(n) then with c(v) as (select unnest(arr)) select avg(v) from c into n; return n; when pg_typeof(r) then foreach r in array arr loop tot := tot + r.len*r.wid; end loop; return tot/c::numeric; end case; end; end; $body$; ``` Test it with the same list of numeric values that you used to test the previous version of mean(): ```plpgsql select to_char(s.mean(17.1, 6.5, 3.4), '99.99') as "mean"; ``` Of course, the result is the same for this version of mean() as for the previous version: ```output mean -- 9.00 ``` Now test it with a list of rectangle values: ```plpgsql select to_char(s.mean( (2.0, 3.0)::s.rectangle, (3.0, 9.0)::s.rectangle, (5.0, 7.0)::s.rectangle), '99.99') as "mean"; ``` This is the result: ```output mean -- 22.67 ``` Recast the previous polymorphic implementation of the variadic mean() as two separate non-polymorphic implementations, thus: ```plpgsql drop function if exists s.mean(anyarray) cascade; create function s.mean(arr variadic numeric[]) returns numeric immutable set searchpath = pgcatalog, pg_temp language plpgsql as $body$ begin assert cardinality(arr) > 0, 'cardinality of arr < 1'; return ( with c(v) as (select unnest(arr)) select avg(v) from c); end; $body$; create function s.mean(arr variadic s.rectangle[]) returns numeric immutable set searchpath = pgcatalog, pg_temp language plpgsql as $body$ declare c constant int not null := cardinality(arr); r s.rectangle not null := (1.0, 1.0); tot numeric not null := 0.0; begin assert cardinality(arr) > 0, 'cardinality of arr < 1'; foreach r in array arr loop tot := tot + r.len*r.wid; end loop; return tot/c::numeric; end; $body$; ``` Test it with the same two select statements that you used to test the polymorphic version. The results are identical. Choosing between the different approaches can only be a judgement callinformed by intuition born of experience. The total source code size of the two non-polymorphic implementations, here, is greater than that of the single polymorphic implementation. But this is largely explained by the repetition, with the non-polymorphic implementations, of the boilerplate source text of the function headers. The implementation of each non-polymorphic implementation is simpler than that of the polymorphic version because no user-written code is needed to detect the data type of the variadic argument list; rather, this is done behind the scenes by the run-time system when it picks the appropriate overload. The polymorphic implementation encapsulates all the implementation variants in a single code unit. This is beneficial because (short of using dedicated schemas to group functionally related subprogram overloads) you'd have to rely on external documentation to explain that the different name() variants belonged together as a set.