2009-08-12 12:37:26 -04:00
|
|
|
--
|
|
|
|
|
-- Test returning tuples
|
|
|
|
|
--
|
|
|
|
|
|
2009-08-13 16:50:05 -04:00
|
|
|
CREATE TABLE table_record (
|
|
|
|
|
first text,
|
|
|
|
|
second int4
|
|
|
|
|
) ;
|
|
|
|
|
|
|
|
|
|
CREATE TYPE type_record AS (
|
|
|
|
|
first text,
|
|
|
|
|
second int4
|
|
|
|
|
) ;
|
|
|
|
|
|
|
|
|
|
|
2009-08-12 12:37:26 -04:00
|
|
|
CREATE FUNCTION test_table_record_as(typ text, first text, second integer, retnull boolean) RETURNS table_record AS $$
|
|
|
|
|
if retnull:
|
|
|
|
|
return None
|
|
|
|
|
if typ == 'dict':
|
|
|
|
|
return { 'first': first, 'second': second, 'additionalfield': 'must not cause trouble' }
|
|
|
|
|
elif typ == 'tuple':
|
|
|
|
|
return ( first, second )
|
|
|
|
|
elif typ == 'list':
|
|
|
|
|
return [ first, second ]
|
|
|
|
|
elif typ == 'obj':
|
|
|
|
|
class type_record: pass
|
|
|
|
|
type_record.first = first
|
|
|
|
|
type_record.second = second
|
|
|
|
|
return type_record
|
|
|
|
|
$$ LANGUAGE plpythonu;
|
|
|
|
|
|
|
|
|
|
CREATE FUNCTION test_type_record_as(typ text, first text, second integer, retnull boolean) RETURNS type_record AS $$
|
|
|
|
|
if retnull:
|
|
|
|
|
return None
|
|
|
|
|
if typ == 'dict':
|
|
|
|
|
return { 'first': first, 'second': second, 'additionalfield': 'must not cause trouble' }
|
|
|
|
|
elif typ == 'tuple':
|
|
|
|
|
return ( first, second )
|
|
|
|
|
elif typ == 'list':
|
|
|
|
|
return [ first, second ]
|
|
|
|
|
elif typ == 'obj':
|
|
|
|
|
class type_record: pass
|
|
|
|
|
type_record.first = first
|
|
|
|
|
type_record.second = second
|
|
|
|
|
return type_record
|
PL/Python: Accept strings in functions returning composite types
Before 9.1, PL/Python functions returning composite types could return
a string and it would be parsed using record_in. The 9.1 changes made
PL/Python only expect dictionaries, tuples, or objects supporting
getattr as output of composite functions, resulting in a regression
and a confusing error message, as the strings were interpreted as
sequences and the code for transforming lists to database tuples was
used. Fix this by treating strings separately as before, before
checking for the other types.
The reason why it's important to support string to database tuple
conversion is that trigger functions on tables with composite columns
get the composite row passed in as a string (from record_out).
Without supporting converting this back using record_in, this makes it
impossible to implement pass-through behavior for these columns, as
PL/Python no longer accepts strings for composite values.
A better solution would be to fix the code that transforms composite
inputs into Python objects to produce dictionaries that would then be
correctly interpreted by the Python->PostgreSQL counterpart code. But
that would be too invasive to backpatch to 9.1, and it is too late in
the 9.2 cycle to attempt it. It should be revisited in the future,
though.
Reported as bug #6559 by Kirill Simonov.
Jan Urbański
2012-04-26 14:03:48 -04:00
|
|
|
elif typ == 'str':
|
|
|
|
|
return "('%s',%r)" % (first, second)
|
2009-08-12 12:37:26 -04:00
|
|
|
$$ LANGUAGE plpythonu;
|
|
|
|
|
|
|
|
|
|
CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
|
|
|
|
|
return first + '_in_to_out';
|
|
|
|
|
$$ LANGUAGE plpythonu;
|
|
|
|
|
|
|
|
|
|
CREATE FUNCTION test_in_out_params_multi(first in text,
|
|
|
|
|
second out text, third out text) AS $$
|
2011-02-26 09:53:11 -05:00
|
|
|
return (first + '_record_in_to_out_1', first + '_record_in_to_out_2');
|
2009-08-12 12:37:26 -04:00
|
|
|
$$ LANGUAGE plpythonu;
|
|
|
|
|
|
|
|
|
|
CREATE FUNCTION test_inout_params(first inout text) AS $$
|
|
|
|
|
return first + '_inout';
|
|
|
|
|
$$ LANGUAGE plpythonu;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- Test tuple returning functions
|
|
|
|
|
SELECT * FROM test_table_record_as('dict', null, null, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('dict', 'one', null, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('dict', null, 2, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('dict', 'three', 3, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('dict', null, null, true);
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_table_record_as('tuple', null, null, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('tuple', 'one', null, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('tuple', null, 2, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('tuple', 'three', 3, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('tuple', null, null, true);
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_table_record_as('list', null, null, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('list', 'one', null, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('list', null, 2, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('list', 'three', 3, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('list', null, null, true);
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_table_record_as('obj', null, null, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('obj', 'one', null, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('obj', null, 2, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('obj', 'three', 3, false);
|
|
|
|
|
SELECT * FROM test_table_record_as('obj', null, null, true);
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_type_record_as('dict', null, null, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('dict', 'one', null, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('dict', null, 2, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('dict', 'three', 3, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('dict', null, null, true);
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_type_record_as('tuple', null, null, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('tuple', 'one', null, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('tuple', null, 2, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('tuple', 'three', 3, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('tuple', null, null, true);
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_type_record_as('list', null, null, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('list', 'one', null, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('list', null, 2, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('list', 'three', 3, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('list', null, null, true);
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_type_record_as('obj', null, null, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('obj', 'one', null, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('obj', null, 2, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('obj', 'three', 3, false);
|
|
|
|
|
SELECT * FROM test_type_record_as('obj', null, null, true);
|
|
|
|
|
|
PL/Python: Accept strings in functions returning composite types
Before 9.1, PL/Python functions returning composite types could return
a string and it would be parsed using record_in. The 9.1 changes made
PL/Python only expect dictionaries, tuples, or objects supporting
getattr as output of composite functions, resulting in a regression
and a confusing error message, as the strings were interpreted as
sequences and the code for transforming lists to database tuples was
used. Fix this by treating strings separately as before, before
checking for the other types.
The reason why it's important to support string to database tuple
conversion is that trigger functions on tables with composite columns
get the composite row passed in as a string (from record_out).
Without supporting converting this back using record_in, this makes it
impossible to implement pass-through behavior for these columns, as
PL/Python no longer accepts strings for composite values.
A better solution would be to fix the code that transforms composite
inputs into Python objects to produce dictionaries that would then be
correctly interpreted by the Python->PostgreSQL counterpart code. But
that would be too invasive to backpatch to 9.1, and it is too late in
the 9.2 cycle to attempt it. It should be revisited in the future,
though.
Reported as bug #6559 by Kirill Simonov.
Jan Urbański
2012-04-26 14:03:48 -04:00
|
|
|
SELECT * FROM test_type_record_as('str', 'one', 1, false);
|
|
|
|
|
|
2009-08-12 12:37:26 -04:00
|
|
|
SELECT * FROM test_in_out_params('test_in');
|
|
|
|
|
SELECT * FROM test_in_out_params_multi('test_in');
|
|
|
|
|
SELECT * FROM test_inout_params('test_in');
|
2009-08-13 16:50:05 -04:00
|
|
|
|
2011-08-17 17:07:16 -04:00
|
|
|
-- try changing the return types and call functions again
|
|
|
|
|
|
|
|
|
|
ALTER TABLE table_record DROP COLUMN first;
|
|
|
|
|
ALTER TABLE table_record DROP COLUMN second;
|
|
|
|
|
ALTER TABLE table_record ADD COLUMN first text;
|
|
|
|
|
ALTER TABLE table_record ADD COLUMN second int4;
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_table_record_as('obj', 'one', 1, false);
|
|
|
|
|
|
|
|
|
|
ALTER TYPE type_record DROP ATTRIBUTE first;
|
|
|
|
|
ALTER TYPE type_record DROP ATTRIBUTE second;
|
|
|
|
|
ALTER TYPE type_record ADD ATTRIBUTE first text;
|
|
|
|
|
ALTER TYPE type_record ADD ATTRIBUTE second int4;
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_type_record_as('obj', 'one', 1, false);
|
2009-08-13 16:50:05 -04:00
|
|
|
|
|
|
|
|
-- errors cases
|
|
|
|
|
|
|
|
|
|
CREATE FUNCTION test_type_record_error1() RETURNS type_record AS $$
|
|
|
|
|
return { 'first': 'first' }
|
|
|
|
|
$$ LANGUAGE plpythonu;
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_type_record_error1();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CREATE FUNCTION test_type_record_error2() RETURNS type_record AS $$
|
|
|
|
|
return [ 'first' ]
|
|
|
|
|
$$ LANGUAGE plpythonu;
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_type_record_error2();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CREATE FUNCTION test_type_record_error3() RETURNS type_record AS $$
|
|
|
|
|
class type_record: pass
|
|
|
|
|
type_record.first = 'first'
|
|
|
|
|
return type_record
|
|
|
|
|
$$ LANGUAGE plpythonu;
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_type_record_error3();
|
PL/Python: Accept strings in functions returning composite types
Before 9.1, PL/Python functions returning composite types could return
a string and it would be parsed using record_in. The 9.1 changes made
PL/Python only expect dictionaries, tuples, or objects supporting
getattr as output of composite functions, resulting in a regression
and a confusing error message, as the strings were interpreted as
sequences and the code for transforming lists to database tuples was
used. Fix this by treating strings separately as before, before
checking for the other types.
The reason why it's important to support string to database tuple
conversion is that trigger functions on tables with composite columns
get the composite row passed in as a string (from record_out).
Without supporting converting this back using record_in, this makes it
impossible to implement pass-through behavior for these columns, as
PL/Python no longer accepts strings for composite values.
A better solution would be to fix the code that transforms composite
inputs into Python objects to produce dictionaries that would then be
correctly interpreted by the Python->PostgreSQL counterpart code. But
that would be too invasive to backpatch to 9.1, and it is too late in
the 9.2 cycle to attempt it. It should be revisited in the future,
though.
Reported as bug #6559 by Kirill Simonov.
Jan Urbański
2012-04-26 14:03:48 -04:00
|
|
|
|
|
|
|
|
CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
|
|
|
|
|
return 'foo'
|
|
|
|
|
$$ LANGUAGE plpythonu;
|
|
|
|
|
|
|
|
|
|
SELECT * FROM test_type_record_error4();
|