Struggling with set-returning functions, seeking advice

From: Jason Topaz <topaz(at)panix(dot)com>
To: pgsql-novice(at)postgresql(dot)org
Subject: Struggling with set-returning functions, seeking advice
Date: 2003-07-23 08:57:45
Message-ID: 1058950663.2979.6.camel@localhost.localdomain
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-novice

Greetings. I've successfully gotten some code to work, but wondering
whether anybody can offer advice on a better way to do this. I've
read everything I can think of, including the recent paper on
PostgreSQL 7.3 set-returning functions. Also, can anybody comment
whether I have found a PostgreSQL bug? (though I highly doubt
it... I'm a newbie!). Here's the story...

I have data in a table:

db=> SELECT * FROM ranges;
id | startindex | endindex | otherdata
----+------------+----------+-----------
1 | 5 | 9 | 42
2 | 4 | 6 | 93
(2 rows)

I'm trying to create a view that transforms this table into multiple
rows
per source row - one for each "index" between startindex and last
index. It also needs to do calculations based on the index and
otherdata. (For sample purposes, I am just saying the calculation is
index+otherdata). So the view I would like to see is:

db=> SELECT * FROM ranges_setview;
id | index | calc
----+-------+------
1 | 5 | 47
1 | 6 | 48
1 | 7 | 49
1 | 8 | 50
1 | 9 | 51
2 | 4 | 97
2 | 5 | 98
2 | 6 | 99
(8 rows)

I did get this to work with the enclosed code using a set-returning
function in plpgsql. But I have quite a few questions. Can anybody help
out?

1) Note that my first attempt (view "ranges_setview_broken", referring
to a pgplsql set function "make_rows") fails. But when I make a
second function with identical signature, but in language 'sql'
(it's just a passthrough to my original pgplsql function), suddenly
the server error goes away. This seems strange to me. Is there
a reason my pgplsql function should behave differently than the
sql function? Or is this a PostgreSQL bug? version() is:

PostgreSQL 7.3.2 on i386-redhat-linux-gnu, compiled by GCC
i386-redhat-linux-gcc (GCC) 3.2.2 20030213 (Red Hat Linux 8.0 3.2.2-1)

2) Any advice on efficiency? When using the view I create, my
function make_rows gets called twice - with the same parameters -
for every source row in table "ranges", since I extract two fields
from the returned data. Confirmed this with "RAISE INFO". It
seems like there should be a way to call make_rows only once per
source row, but I can't figure out how to organize the query to
do this.

I would love for the make_rows() call to appear in the FROM clause,
but each time I try, PostgreSQL seems to be unhappy with the fact
that the input parameters need to come from another target table.
Hmm... any way to rig this up with a correlated subquery?

3) Any recommendations for a completely different way to approach this
table transformation? Incidentally, I have simplified a lot for
purposes of this posting, but the real application involves
transforming
time-series data (stored in an array field) into a expanded table
where there is one row per original element in the array.

4) The approach I'm using - putting set-value functions as target
columns of the select statement - is described in the manual as
deprecated and likely to go away. What other way can I accomplish
my task? (maybe somebody's clever answer to quetion #2 or #3 will
solve this problem anyway).

CREATE TABLE ranges (
id INTEGER PRIMARY KEY,
startindex INTEGER NOT NULL,
endindex INTEGER NOT NULL,
otherdata INTEGER NOT NULL
);

INSERT INTO ranges VALUES(1, 5, 9, 42);
INSERT INTO ranges VALUES(2, 4, 6, 93);

CREATE TYPE myrecord_type AS (
index INTEGER,
calc INTEGER
);

-- Generate a table with rows having 'index' numbered
-- from $1 to $2, and 'calc' set to 'index' + $3
CREATE OR REPLACE FUNCTION make_rows(INTEGER, INTEGER, INTEGER)
RETURNS SETOF myrecord_type
LANGUAGE 'plpgsql'
AS '
DECLARE
rec myrecord_type%ROWTYPE;
BEGIN
FOR index IN $1..$2 LOOP
SELECT index, index + $3 INTO rec;
RETURN NEXT rec;
END LOOP;
RETURN;
END
';

-- Using this view generates an error:
-- "Set-valued function called in context that cannot accept a set"

CREATE OR REPLACE VIEW ranges_setview_broken AS (
SELECT
id,
(make_rows(startindex, endindex, otherdata)).index,
(make_rows(startindex, endindex, otherdata)).calc
FROM ranges
);

-- SQL wrapper function passing through to plpgsql function

CREATE OR REPLACE FUNCTION make_rows_sql(INTEGER, INTEGER, INTEGER)
RETURNS SETOF myrecord_type
LANGUAGE 'sql'
AS 'select * from make_rows($1, $2, $3)';

-- Identical view to last time, but using wrapper
-- instead of original function. Works fine.

CREATE OR REPLACE VIEW ranges_setview_notbroken AS (
SELECT
id,
(make_rows_sql(startindex, endindex, otherdata)).index,
(make_rows_sql(startindex, endindex, otherdata)).calc
FROM ranges
);

Thanks in advance for any pointers!

--
Jason Topaz
topaz(at)panix(dot)com

Responses

Browse pgsql-novice by date

  From Date Subject
Next Message A.Bhuvaneswaran 2003-07-23 09:38:51 Re: Need clarifications......
Previous Message Wolfgang Drotschmann 2003-07-23 08:21:33 Re: CREATE TYPE with array