counterintuitive behaviour in pl/pgsql
Hi !
I'm running "PostgreSQL 9.0.2, compiled by Visual C++ build 1500, 32-bit"
I have found an odd behaviour in pl/pgsql when using 'return query execute'
The function produce the dynamic query 'select * from tbl1 where col1 < 4'
and executes it.
I would have expected to have 3 rows back with the values 1,2,3 or maybe
3,3,3 but it returns all rows in the table ??
Here is a self contained test case that shows the behaviour.
And yes I do know that I can fix the problem by renaming the output column
to something else than i , I'm just curious about the behaviour and if it
should work like this and why.
create table tbl1 ( col1 int, constraint pk_tb1 primary key (col1));
insert into tbl1 values (1),(2),(3),(4),(5),(6);
CREATE OR REPLACE FUNCTION dynamic_query(i int) RETURNS TABLE (i int) as $$
DECLARE
stmt text;
cond text;
BEGIN
stmt := 'select * from tbl1 ';
IF (i IS NOT NULL) THEN cond := ' col1 < $1 '; END IF;
IF (cond IS NOT NULL) THEN stmt := stmt || 'where ' || cond; END IF;
RETURN QUERY EXECUTE stmt USING i;
RETURN;
END;
$$ language plpgsql;
select * from dynamic_query(4);
Best Regards
Dan S
Hello
yes, this behave is strange, and should be fixed
Regards
Pavel Stehule
2011/5/21 Dan S <strd911@gmail.com>:
Show quoted text
Hi !
I'm running "PostgreSQL 9.0.2, compiled by Visual C++ build 1500, 32-bit"
I have found an odd behaviour in pl/pgsql when using 'return query execute'
The function produce the dynamic query 'select * from tbl1 where col1 < 4'
and executes it.
I would have expected to have 3 rows back with the values 1,2,3 or maybe
3,3,3 but it returns all rows in the table ??
Here is a self contained test case that shows the behaviour.
And yes I do know that I can fix the problem by renaming the output column
to something else than i , I'm just curious about the behaviour and if it
should work like this and why.create table tbl1 ( col1 int, constraint pk_tb1 primary key (col1));
insert into tbl1 values (1),(2),(3),(4),(5),(6);
CREATE OR REPLACE FUNCTION dynamic_query(i int) RETURNS TABLE (i int) as $$
DECLARE
stmt text;
cond text;
BEGIN
stmt := 'select * from tbl1 ';IF (i IS NOT NULL) THEN cond := ' col1 < $1 '; END IF;
IF (cond IS NOT NULL) THEN stmt := stmt || 'where ' || cond; END IF;
RETURN QUERY EXECUTE stmt USING i;
RETURN;
END;
$$ language plpgsql;select * from dynamic_query(4);
Best Regards
Dan S
Hello,
seems like you cannot name your input parameters the same as your
tableoutputcolumns? Rename one of them and it works.
Something like:
RETURNS TABLE (j int)
Regards
Am 21.05.11 16:25, schrieb Pavel Stehule:
Show quoted text
Hello
yes, this behave is strange, and should be fixed
Regards
Pavel Stehule
2011/5/21 Dan S<strd911@gmail.com>:
Hi !
I'm running "PostgreSQL 9.0.2, compiled by Visual C++ build 1500, 32-bit"
I have found an odd behaviour in pl/pgsql when using 'return query execute'
The function produce the dynamic query 'select * from tbl1 where col1< 4'
and executes it.
I would have expected to have 3 rows back with the values 1,2,3 or maybe
3,3,3 but it returns all rows in the table ??
Here is a self contained test case that shows the behaviour.
And yes I do know that I can fix the problem by renaming the output column
to something else than i , I'm just curious about the behaviour and if it
should work like this and why.create table tbl1 ( col1 int, constraint pk_tb1 primary key (col1));
insert into tbl1 values (1),(2),(3),(4),(5),(6);
CREATE OR REPLACE FUNCTION dynamic_query(i int) RETURNS TABLE (i int) as $$
DECLARE
stmt text;
cond text;
BEGIN
stmt := 'select * from tbl1 ';IF (i IS NOT NULL) THEN cond := ' col1< $1 '; END IF;
IF (cond IS NOT NULL) THEN stmt := stmt || 'where ' || cond; END IF;
RETURN QUERY EXECUTE stmt USING i;
RETURN;
END;
$$ language plpgsql;select * from dynamic_query(4);
Best Regards
Dan S
Dan S <strd911@gmail.com> writes:
And yes I do know that I can fix the problem by renaming the output column
to something else than i , I'm just curious about the behaviour and if it
should work like this and why.
CREATE OR REPLACE FUNCTION dynamic_query(i int) RETURNS TABLE (i int) as $$
This should probably throw an error. There is a check that disallows
having two input or two output parameters named the same, but the
comment about it says:
/*
* As of Postgres 9.0 we disallow using the same name for two
* input or two output function parameters. Depending on the
* function's language, conflicting input and output names might
* be bad too, but we leave it to the PL to complain if so.
*/
It looks like plpgsql didn't get the memo about checking this.
regards, tom lane
On May 21, 2011, at 9:41, Dan S wrote:
Hi !
I'm running "PostgreSQL 9.0.2, compiled by Visual C++ build 1500, 32-bit"
I have found an odd behaviour in pl/pgsql when using 'return query execute'
The function produce the dynamic query 'select * from tbl1 where col1 < 4'
and executes it.
I would have expected to have 3 rows back with the values 1,2,3 or maybe
3,3,3 but it returns all rows in the table ??
Here is a self contained test case that shows the behaviour.
And yes I do know that I can fix the problem by renaming the output column
to something else than i , I'm just curious about the behaviour and if it
should work like this and why.create table tbl1 ( col1 int, constraint pk_tb1 primary key (col1));
insert into tbl1 values (1),(2),(3),(4),(5),(6);
CREATE OR REPLACE FUNCTION dynamic_query(i int) RETURNS TABLE (i int) as $$
DECLARE
stmt text;
cond text;
BEGIN
stmt := 'select * from tbl1 ';IF (i IS NOT NULL) THEN cond := ' col1 < $1 '; END IF;
IF (cond IS NOT NULL) THEN stmt := stmt || 'where ' || cond; END IF;
RETURN QUERY EXECUTE stmt USING i;
RETURN;
END;
$$ language plpgsql;select * from dynamic_query(4);
I couldn't see immediately what the issue was from the description as the example, so I came up with a couple of additional examples that helped me see what was going on:
CREATE OR REPLACE FUNCTION dynamic_query_4(i int)
RETURNS TABLE (i int)
LANGUAGE PLPGSQL
AS $body$
DECLARE
v_sql TEXT := 'SELECT col1 FROM tbl1 WHERE col1 < $1';
BEGIN
RETURN QUERY EXECUTE v_sql USING i;
END;
$body$;
SELECT * FROM dynamic_query_4(4);
i
---
(0 rows)
CREATE OR REPLACE FUNCTION dynamic_query_5(i int)
RETURNS TABLE (i int)
LANGUAGE PLPGSQL
AS $body$
DECLARE
v_sql TEXT := 'SELECT col1 FROM tbl1';
BEGIN
RAISE NOTICE 'i IS NULL => %', i IS NULL;
IF i IS NOT NULL THEN
v_sql := v_sql || ' WHERE col1 < $1';
END IF;
RAISE NOTICE 'v_sql: %', v_sql;
RETURN QUERY EXECUTE v_sql USING i;
END;
$body$;
SELECT * FROM dynamic_query_5(4);
NOTICE: i IS NULL => t
NOTICE: v_sql: SELECT col1 FROM tbl1
i
---
1
2
3
4
5
6
(6 rows)
It looks like it's just column names stomping on variable names, which is a known issue. This is why a lot of developers (including myself) have conventions of prefixing parameters and variable names (I use in_ for input parameters, v_ for internally defined variables).
Michael Glaesemann
grzm seespotcode net
Yes throwing an error would probably be good to catch these kind of mistakes
which silently gives you the wrong answer otherwise.
Best Regards
Dan S
2011/5/21 Tom Lane <tgl@sss.pgh.pa.us>
Show quoted text
Dan S <strd911@gmail.com> writes:
And yes I do know that I can fix the problem by renaming the output
column
to something else than i , I'm just curious about the behaviour and if it
should work like this and why.CREATE OR REPLACE FUNCTION dynamic_query(i int) RETURNS TABLE (i int) as
$$
This should probably throw an error. There is a check that disallows
having two input or two output parameters named the same, but the
comment about it says:/*
* As of Postgres 9.0 we disallow using the same name for two
* input or two output function parameters. Depending on the
* function's language, conflicting input and output names might
* be bad too, but we leave it to the PL to complain if so.
*/It looks like plpgsql didn't get the memo about checking this.
regards, tom lane
2011/5/21 Michael Glaesemann <grzm@seespotcode.net>:
On May 21, 2011, at 9:41, Dan S wrote:
Hi !
I'm running "PostgreSQL 9.0.2, compiled by Visual C++ build 1500, 32-bit"
I have found an odd behaviour in pl/pgsql when using 'return query execute'
The function produce the dynamic query 'select * from tbl1 where col1 < 4'
and executes it.
I would have expected to have 3 rows back with the values 1,2,3 or maybe
3,3,3 but it returns all rows in the table ??
Here is a self contained test case that shows the behaviour.
And yes I do know that I can fix the problem by renaming the output column
to something else than i , I'm just curious about the behaviour and if it
should work like this and why.create table tbl1 ( col1 int, constraint pk_tb1 primary key (col1));
insert into tbl1 values (1),(2),(3),(4),(5),(6);
CREATE OR REPLACE FUNCTION dynamic_query(i int) RETURNS TABLE (i int) as $$
DECLARE
stmt text;
cond text;
BEGIN
stmt := 'select * from tbl1 ';IF (i IS NOT NULL) THEN cond := ' col1 < $1 '; END IF;
IF (cond IS NOT NULL) THEN stmt := stmt || 'where ' || cond; END IF;
RETURN QUERY EXECUTE stmt USING i;
RETURN;
END;
$$ language plpgsql;select * from dynamic_query(4);
I couldn't see immediately what the issue was from the description as the example, so I came up with a couple of additional examples that helped me see what was going on:
CREATE OR REPLACE FUNCTION dynamic_query_4(i int)
RETURNS TABLE (i int)
LANGUAGE PLPGSQL
AS $body$
DECLARE
v_sql TEXT := 'SELECT col1 FROM tbl1 WHERE col1 < $1';
BEGIN
RETURN QUERY EXECUTE v_sql USING i;
END;
$body$;SELECT * FROM dynamic_query_4(4);
i
---
(0 rows)CREATE OR REPLACE FUNCTION dynamic_query_5(i int)
RETURNS TABLE (i int)
LANGUAGE PLPGSQL
AS $body$
DECLARE
v_sql TEXT := 'SELECT col1 FROM tbl1';
BEGIN
RAISE NOTICE 'i IS NULL => %', i IS NULL;
IF i IS NOT NULL THEN
v_sql := v_sql || ' WHERE col1 < $1';
END IF;
RAISE NOTICE 'v_sql: %', v_sql;
RETURN QUERY EXECUTE v_sql USING i;
END;
$body$;SELECT * FROM dynamic_query_5(4);
NOTICE: i IS NULL => t
NOTICE: v_sql: SELECT col1 FROM tbl1
i
---
1
2
3
4
5
6
(6 rows)It looks like it's just column names stomping on variable names, which is a known issue. This is why a lot of developers (including myself) have conventions of prefixing parameters and variable names (I use in_ for input parameters, v_ for internally defined variables).
It is not this case. There is two plpgsql variables with same name in
one namespace - the last OUT variable has higher priority.
Regards
Pavel
Show quoted text
Michael Glaesemann
grzm seespotcode net--
Sent via pgsql-general mailing list (pgsql-general@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-general
On May 21, 2011, at 13:44, Pavel Stehule wrote:
2011/5/21 Michael Glaesemann <grzm@seespotcode.net>:
It looks like it's just column names stomping on variable names, which is a known issue. This is why a lot of developers (including myself) have conventions of prefixing parameters and variable names (I use in_ for input parameters, v_ for internally defined variables).
It is not this case. There is two plpgsql variables with same name in
one namespace - the last OUT variable has higher priority.
Yeah, I see that now (after seeing Tom's post). Thanks for the confirmation.
Michael Glaesemann
grzm seespotcode net