RfD: more powerful "any" types
Hi,
I'm seeking more powerful "any" pseudotypes. In particular, consider a
function
foo(type1, type2) returns type3
where type1 and type2 can be both any element type, but not necessarily
both of the same type. Currently this cannot be made to work: you could
try to write the function this way:
foo(anyelement, anyelement) returns anyelement
but this will force them to be both of the same type, which is not what
we want. In my opinion this is a missing feature of our type system.
One seemingly trivial idea would be to have anyelement2, anyelement3,
and so on. This is not a very good solution, because we'd have to fill
the catalogs with a large bunch of new pseudotypes, and the code with a
bunch of hardcoded tests -- and there are already 27 cases of
ANYELEMENTOID in our code.
For a practical example, I am trying to write a function that returns
how many NULL arguments it has (this is useful in table CHECK
constraints). One simple idea is to use type "unknown":
CREATE FUNCTION a (unknown, unknown, unknown) RETURNS INT LANGUAGE plpgsql AS $$
DECLARE
count int = 0;
BEGIN
IF $1 IS NULL THEN count = count + 1; END IF;
IF $2 IS NULL THEN count = count + 1; END IF;
if $3 IS NULL THEN count = count + 1; END IF;
RETURN count;
END $$;
The problem is that it doesn't work. This trivial query does:
alvherre=# select a(null, '2', null);
a
---
2
(1 fila)
But this one does not:
alvherre=# select a(null, 2, null);
ERROR: function a(unknown, integer, unknown) does not exist
and you cannot cast the integer:
alvherre=# select a(null, 2::unknown, null);
ERROR: cannot cast type integer to unknown
Before I spend time trying to figure out how this works,
1. is there agreement that this is a problem and needs fixed, and
2. does anybody have an idea how to attack it?
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
On Sep 8, 2009, at 9:12 AM, Alvaro Herrera wrote:
foo(anyelement, anyelement) returns anyelement
but this will force them to be both of the same type, which is not
what
we want. In my opinion this is a missing feature of our type system.
Oh yes, agreed. I've run into this with pgTAP many times.
One seemingly trivial idea would be to have anyelement2, anyelement3,
and so on. This is not a very good solution, because we'd have to
fill
the catalogs with a large bunch of new pseudotypes, and the code
with a
bunch of hardcoded tests -- and there are already 27 cases of
ANYELEMENTOID in our code.
Yes, and foo() might be called with two different data types, or two
of the same, and both should work.
<snip what="other useful information" />
Before I spend time trying to figure out how this works,
1. is there agreement that this is a problem and needs fixed, and
+1
2. does anybody have an idea how to attack it?
Why can't anyelement be freed from this constraint?
Best,
David
David E. Wheeler wrote:
On Sep 8, 2009, at 9:12 AM, Alvaro Herrera wrote:
2. does anybody have an idea how to attack it?
Why can't anyelement be freed from this constraint?
Because it would break other uses of it, I think. IIRC the original use
of anyelement was that it would resolve to the element type of an
anyarray argument-or-return type.
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Alvaro Herrera <alvherre@commandprompt.com> writes:
I'm seeking more powerful "any" pseudotypes.
If you don't want any constraints at all on the argument types, you
can use "any" (with the quotes, because it's a reserved word :-().
If you want some constraints but not "anyelement", please be more
specific about what you want.
regards, tom lane
On Sep 8, 2009, at 9:23 AM, Alvaro Herrera wrote:
2. does anybody have an idea how to attack it?
Why can't anyelement be freed from this constraint?
Because it would break other uses of it, I think. IIRC the original
use
of anyelement was that it would resolve to the element type of an
anyarray argument-or-return type.
Does it still? Need it?
David
On Sep 8, 2009, at 9:25 AM, Tom Lane wrote:
If you don't want any constraints at all on the argument types, you
can use "any" (with the quotes, because it's a reserved word :-().
If you want some constraints but not "anyelement", please be more
specific about what you want.
Oooh, I need to try that. How far back does that behavior go,
compatibility-wise?
Best,
David
Tom Lane wrote:
Alvaro Herrera <alvherre@commandprompt.com> writes:
I'm seeking more powerful "any" pseudotypes.
If you don't want any constraints at all on the argument types, you
can use "any" (with the quotes, because it's a reserved word :-().
If you want some constraints but not "anyelement", please be more
specific about what you want.
That would work, except that plpgsql and SQL don't like it:
ERROR: PL/pgSQL functions cannot accept type "any"
ERROR: SQL functions cannot have arguments of type "any"
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On Sep 8, 2009, at 9:32 AM, Alvaro Herrera wrote:
That would work, except that plpgsql and SQL don't like it:
ERROR: PL/pgSQL functions cannot accept type "any"
ERROR: SQL functions cannot have arguments of type "any"
Seems to go for other PLs, as well:
ERROR: PL/Perl functions cannot accept type "any"
Perhaps that could be changed?
Best,
David
Alvaro Herrera <alvherre@commandprompt.com> writes:
Tom Lane wrote:
If you don't want any constraints at all on the argument types, you
can use "any" (with the quotes, because it's a reserved word :-().
That would work, except that plpgsql and SQL don't like it:
Intentionally so, because there's not a whole lot you can *do* with an
ANY parameter, other than checking it for null. Perhaps the real
question is about what semantics you're expecting for these
unconstrained parameters.
regards, tom lane
On Sep 8, 2009, at 9:57 AM, Tom Lane wrote:
Intentionally so, because there's not a whole lot you can *do* with an
ANY parameter, other than checking it for null. Perhaps the real
question is about what semantics you're expecting for these
unconstrained parameters.
For my purposes, I guess implicit casting for comparing values, as in
arg_a IS DISTINCT FROM arg_b
and
arg_a = arg_b
or
arg_a <> arg_b
It'd work if there was a way to cast one to the type of the other,
such as comparing a TEXT to a VARCHAR. But maybe that'd be too magical…
Best,
David
"David E. Wheeler" <david@kineticode.com> writes:
On Sep 8, 2009, at 9:57 AM, Tom Lane wrote:
Intentionally so, because there's not a whole lot you can *do* with an
ANY parameter, other than checking it for null. Perhaps the real
question is about what semantics you're expecting for these
unconstrained parameters.
For my purposes, I guess implicit casting for comparing values, as in
arg_a IS DISTINCT FROM arg_b
Surely you'd want arg_a and arg_b constrained to the same type,
otherwise there is no certainty that that means anything at all.
regards, tom lane
On Tue, Sep 8, 2009 at 12:12 PM, Alvaro
Herrera<alvherre@commandprompt.com> wrote:
Hi,
I'm seeking more powerful "any" pseudotypes. In particular, consider a
functionfoo(type1, type2) returns type3
where type1 and type2 can be both any element type, but not necessarily
both of the same type. Currently this cannot be made to work: you could
try to write the function this way:foo(anyelement, anyelement) returns anyelement
but this will force them to be both of the same type, which is not what
we want. In my opinion this is a missing feature of our type system.One seemingly trivial idea would be to have anyelement2, anyelement3,
and so on. This is not a very good solution, because we'd have to fill
the catalogs with a large bunch of new pseudotypes, and the code with a
bunch of hardcoded tests -- and there are already 27 cases of
ANYELEMENTOID in our code.For a practical example, I am trying to write a function that returns
how many NULL arguments it has (this is useful in table CHECK
constraints). One simple idea is to use type "unknown":CREATE FUNCTION a (unknown, unknown, unknown) RETURNS INT LANGUAGE plpgsql AS $$
DECLARE
count int = 0;
BEGIN
IF $1 IS NULL THEN count = count + 1; END IF;
IF $2 IS NULL THEN count = count + 1; END IF;
if $3 IS NULL THEN count = count + 1; END IF;
RETURN count;
END $$;The problem is that it doesn't work. This trivial query does:
alvherre=# select a(null, '2', null);
a
---
2
(1 fila)But this one does not:
alvherre=# select a(null, 2, null);
ERROR: function a(unknown, integer, unknown) does not existand you cannot cast the integer:
alvherre=# select a(null, 2::unknown, null);
ERROR: cannot cast type integer to unknownBefore I spend time trying to figure out how this works,
1. is there agreement that this is a problem and needs fixed, and
2. does anybody have an idea how to attack it?
Since you can do all these things and more in C functions, this
becomes a right tool/wrong tool problem? plpgsql would be fairly
hopeless without some reflection capabilities that we don't currently
have...especially if you consider variadic functions which would be
hard to reconcile with any behavior changes.
I think if you continue going down this road you would end up with a
type system along the lines with c++ templates...so you could do:
DECLARE
foo arg1%type;
etc
And maybe have unique generated plans for each unique set of supplied
input types.
merlin
On Sep 8, 2009, at 10:15 AM, Tom Lane wrote:
arg_a IS DISTINCT FROM arg_b
Surely you'd want arg_a and arg_b constrained to the same type,
otherwise there is no certainty that that means anything at all.
Yes, for the purposes of pgTAP perhaps so. Then it's on the user to do
the cast, because she decides that the cast is appropriate. Otherwise,
as I said, perhaps it'd be too magical.
Best,
David
Alvaro Herrera <alvherre@commandprompt.com> writes:
David E. Wheeler wrote:
Why can't anyelement be freed from this constraint?
Because it would break other uses of it, I think.
Specifically, what are you going to do with something like
make_array(anyelement, anyelement) returns anyarray
There's no principled way to determine what anyarray means if the
arguments are not the same type.
I have no objection to adding some other pseudotype with different
behavior, but breaking anyelement is not the path.
regards, tom lane
"David E. Wheeler" <david@kineticode.com> writes:
On Sep 8, 2009, at 9:25 AM, Tom Lane wrote:
If you don't want any constraints at all on the argument types, you
can use "any" (with the quotes, because it's a reserved word :-().
Oooh, I need to try that. How far back does that behavior go,
compatibility-wise?
Further than "anyelement", I think, or at least the same distance.
IIRC it was one of the types we split up "opaque" into.
regards, tom lane
On Tue, Sep 08, 2009 at 12:12:10PM -0400, Alvaro Herrera wrote:
Hi,
I'm seeking more powerful "any" pseudotypes. In particular,
consider a functionfoo(type1, type2) returns type3
where type1 and type2 can be both any element type, but not
necessarily both of the same type. Currently this cannot be made to
work: you could try to write the function this way:foo(anyelement, anyelement) returns anyelement
I'd like to see pseudo-types like ANYNUMERIC, and allow it to take an
array decorator, which would really help for math-ish functions. Not
sure where that fits in this discussion.
Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
On Tue, 2009-09-08 at 10:23 -0700, David E. Wheeler wrote:
On Sep 8, 2009, at 10:15 AM, Tom Lane wrote:
arg_a IS DISTINCT FROM arg_b
Surely you'd want arg_a and arg_b constrained to the same type,
otherwise there is no certainty that that means anything at all.Yes, for the purposes of pgTAP perhaps so. Then it's on the user to do
the cast, because she decides that the cast is appropriate. Otherwise,
as I said, perhaps it'd be too magical.
That's beginning to sound a bit like a generics feature. E.g.,
CREATE FUNCTION the_same<T>(arg_a T, arg_b T) RETURNS bool AS $$
SELECT arg_a IS DISTINCT FROM arg_b;
$$;
Peter Eisentraut wrote:
On Tue, 2009-09-08 at 10:23 -0700, David E. Wheeler wrote:
On Sep 8, 2009, at 10:15 AM, Tom Lane wrote:
arg_a IS DISTINCT FROM arg_b
Surely you'd want arg_a and arg_b constrained to the same type,
otherwise there is no certainty that that means anything at all.Yes, for the purposes of pgTAP perhaps so. Then it's on the user to do
the cast, because she decides that the cast is appropriate. Otherwise,
as I said, perhaps it'd be too magical.That's beginning to sound a bit like a generics feature. E.g.,
CREATE FUNCTION the_same<T>(arg_a T, arg_b T) RETURNS bool AS $$
SELECT arg_a IS DISTINCT FROM arg_b;
$$;
Well, you can write that one with anyelement already.
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On Wed, 2009-09-09 at 07:47 -0400, Alvaro Herrera wrote:
Peter Eisentraut wrote:
On Tue, 2009-09-08 at 10:23 -0700, David E. Wheeler wrote:
On Sep 8, 2009, at 10:15 AM, Tom Lane wrote:
arg_a IS DISTINCT FROM arg_b
Surely you'd want arg_a and arg_b constrained to the same type,
otherwise there is no certainty that that means anything at all.Yes, for the purposes of pgTAP perhaps so. Then it's on the user to do
the cast, because she decides that the cast is appropriate. Otherwise,
as I said, perhaps it'd be too magical.That's beginning to sound a bit like a generics feature. E.g.,
CREATE FUNCTION the_same<T>(arg_a T, arg_b T) RETURNS bool AS $$
SELECT arg_a IS DISTINCT FROM arg_b;
$$;Well, you can write that one with anyelement already.
Well, so far we've only seen use cases in this thread that either
already work or that are not well-defined. ;-)
Peter Eisentraut <peter_e@gmx.net> writes:
Well, so far we've only seen use cases in this thread that either
already work or that are not well-defined. ;-)
Well, yeah, the question is can we extract a clear TODO item here.
I think there are two somewhat orthogonal issues:
1. Is a completely unconstrained argument type (ie "any") of any real
use to PL functions, and if so how can we expose that usefulness?
The only clear thing to do with such an argument is IS NULL/IS NOT NULL
tests, which might or might not be worth the trouble.
2. Is there any use for arguments with type constraints not covered
by the existing ANYFOO rules, and if so what do we add for that?
One comment on point 2 is that it was foreseen from the beginning
that there would be need for ANYELEMENT2 etc, and I'm actually rather
surprised that we've gone this long without adding them. Alvaro made
a good point about not wanting to multiply the various hard-wired
OID references, but perhaps some judicious code refactoring could
prevent a notational disaster.
regards, tom lane