Returning Composite Types from C functions

Started by John Hansenover 20 years ago8 messages
#1John Hansen
john@geeknet.com.au

Hi all,

CREATE TYPE my_type AS (
a int,
b int,
c int,
d int,
e int
);

CREATE FUNCTION text_to_my_type(text)
RETURNS my_type
AS 'my_lib.so'
LANGUAGE 'C' IMMUTABLE STRICT;

CREATE CAST (text AS my_type) WITH FUNCTION text_to_my_type (text);

SELECT ('1:2:3:4:5'::text::my_type).*;

This results in the text_to_my_type(text) function being called no less
than 5 times. Once for each element.

Is this the desired behaviour, or a bug?

#2Michael Fuhr
mike@fuhr.org
In reply to: John Hansen (#1)
Re: Returning Composite Types from C functions

On Sat, Jun 18, 2005 at 09:18:34PM +1000, John Hansen wrote:

SELECT ('1:2:3:4:5'::text::my_type).*;

This results in the text_to_my_type(text) function being called no less
than 5 times. Once for each element.

Is this the desired behaviour, or a bug?

It's a known behavior with functions that return composite types.
Apparently it's not easy to fix:

http://archives.postgresql.org/pgsql-hackers/2005-04/msg00971.php

--
Michael Fuhr
http://www.fuhr.org/~mfuhr/

#3John Hansen
john@geeknet.com.au
In reply to: Michael Fuhr (#2)
Re: Returning Composite Types from C functions

Michael Fuhr [mailto:mike@fuhr.org] Wrote:

Sent: Saturday, June 18, 2005 9:56 PM
To: John Hansen
Cc: pgsql-hackers@postgresql.org
Subject: Re: [HACKERS] Returning Composite Types from C functions

On Sat, Jun 18, 2005 at 09:18:34PM +1000, John Hansen wrote:

SELECT ('1:2:3:4:5'::text::my_type).*;

This results in the text_to_my_type(text) function being called no
less than 5 times. Once for each element.

Is this the desired behaviour, or a bug?

It's a known behavior with functions that return composite types.
Apparently it's not easy to fix:

http://archives.postgresql.org/pgsql-hackers/2005-04/msg00971.php

There is a workaround tho, so should be fixable:

SELECT (a.b).* FROM (SELECT ('1:2:3:4:5'::text::my_type) AS b) AS a;

Or am I missing something?

--
Michael Fuhr
http://www.fuhr.org/~mfuhr/

... John

#4Michael Fuhr
mike@fuhr.org
In reply to: John Hansen (#3)
Re: Returning Composite Types from C functions

On Sat, Jun 18, 2005 at 10:03:38PM +1000, John Hansen wrote:

There is a workaround tho, so should be fixable:

SELECT (a.b).* FROM (SELECT ('1:2:3:4:5'::text::my_type) AS b) AS a;

Or am I missing something?

I don't know enough about PostgreSQL internals so I'll have to defer
to the developers. But here's a case where the above workaround
doesn't work -- in my tests, the cast function is called once per
column per row, or ten times:

CREATE TABLE foo (t text);
INSERT INTO foo VALUES ('1:2:3:4:5');
INSERT INTO foo VALUES ('6:7:8:9:10');
SELECT (a.b).* FROM (SELECT t::my_type AS b FROM foo) AS a;

What do you get? Can you think of a workaround for this case?
Maybe one of the developers can comment on why your example calls
the function only once and mine calls it multiple times per row,
even though they look similar.

--
Michael Fuhr
http://www.fuhr.org/~mfuhr/

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: John Hansen (#3)
Re: Returning Composite Types from C functions

"John Hansen" <john@geeknet.com.au> writes:

There is a workaround tho, so should be fixable:
SELECT (a.b).* FROM (SELECT ('1:2:3:4:5'::text::my_type) AS b) AS a;
Or am I missing something?

Try it ;-)

regards, tom lane

#6John Hansen
john@geeknet.com.au
In reply to: Tom Lane (#5)
Re: Returning Composite Types from C functions

Yes, it worked for me,...

But my point is the workaround shouldn't be nescessary....

Show quoted text

-----Original Message-----
From: Tom Lane [mailto:tgl@sss.pgh.pa.us]
Sent: Saturday, June 18, 2005 11:36 PM
To: John Hansen
Cc: Michael Fuhr; pgsql-hackers@postgresql.org
Subject: Re: [HACKERS] Returning Composite Types from C functions

"John Hansen" <john@geeknet.com.au> writes:

There is a workaround tho, so should be fixable:
SELECT (a.b).* FROM (SELECT ('1:2:3:4:5'::text::my_type) AS

b) AS a;

Or am I missing something?

Try it ;-)

regards, tom lane

#7Tom Lane
tgl@sss.pgh.pa.us
In reply to: John Hansen (#6)
Re: Returning Composite Types from C functions

"John Hansen" <john@geeknet.com.au> writes:

SELECT (a.b).* FROM (SELECT ('1:2:3:4:5'::text::my_type) AS
b) AS a;
Or am I missing something?

Try it ;-)

Yes, it worked for me,...

It depends on your test case, but in many situations the planner will
flatten that into the same result as the other. The basic problem is
that "(foo).*" is expanded to "(foo).f1, (foo).f2, ..." which is OK if
foo is just a variable referring to a subquery output --- but if the
subquery gets flattened into the parent then your function appears
textually multiple times in the resulting query.

There's been some discussion of disabling flattening when the subquery
output targetlist contains any volatile functions, but that seems like
rather a performance-losing answer. It doesn't completely address the
complaint anyway since even a non-volatile function might be expensive
to compute.

regards, tom lane

#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Michael Fuhr (#4)
Re: Returning Composite Types from C functions

Michael Fuhr <mike@fuhr.org> writes:

Maybe one of the developers can comment on why your example calls
the function only once and mine calls it multiple times per row,
even though they look similar.

Look at the EXPLAIN results --- one case gets flattened into a single
plan node and the other doesn't. I know exactly where that particular
skeleton is buried, too:

/*
* Hack: don't try to pull up a subquery with an empty jointree.
* query_planner() will correctly generate a Result plan for a
* jointree that's totally empty, but I don't think the right things
* happen if an empty FromExpr appears lower down in a jointree. Not
* worth working hard on this, just to collapse SubqueryScan/Result
* into Result...
*/
if (subquery->jointree->fromlist == NIL)
return false;

regards, tom lane