mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
Allow conversion from perl to postgresql array in OUT parameters. Second, allow hash form output from procedures with one OUT argument. Pavel Stehule
424 lines
10 KiB
PL/PgSQL
424 lines
10 KiB
PL/PgSQL
--
|
|
-- checkpoint so that if we have a crash in the tests, replay of the
|
|
-- just-completed CREATE DATABASE won't discard the core dump file
|
|
--
|
|
checkpoint;
|
|
|
|
--
|
|
-- Test result value processing
|
|
--
|
|
|
|
CREATE OR REPLACE FUNCTION perl_int(int) RETURNS INTEGER AS $$
|
|
return undef;
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_int(11);
|
|
SELECT * FROM perl_int(42);
|
|
|
|
CREATE OR REPLACE FUNCTION perl_int(int) RETURNS INTEGER AS $$
|
|
return $_[0] + 1;
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_int(11);
|
|
SELECT * FROM perl_int(42);
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION perl_set_int(int) RETURNS SETOF INTEGER AS $$
|
|
return undef;
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_set_int(5);
|
|
SELECT * FROM perl_set_int(5);
|
|
|
|
CREATE OR REPLACE FUNCTION perl_set_int(int) RETURNS SETOF INTEGER AS $$
|
|
return [0..$_[0]];
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_set_int(5);
|
|
SELECT * FROM perl_set_int(5);
|
|
|
|
|
|
CREATE TYPE testrowperl AS (f1 integer, f2 text, f3 text);
|
|
|
|
CREATE OR REPLACE FUNCTION perl_row() RETURNS testrowperl AS $$
|
|
return undef;
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_row();
|
|
SELECT * FROM perl_row();
|
|
|
|
CREATE OR REPLACE FUNCTION perl_row() RETURNS testrowperl AS $$
|
|
return {f2 => 'hello', f1 => 1, f3 => 'world'};
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_row();
|
|
SELECT * FROM perl_row();
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$
|
|
return undef;
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_set();
|
|
SELECT * FROM perl_set();
|
|
|
|
CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$
|
|
return [
|
|
{ f1 => 1, f2 => 'Hello', f3 => 'World' },
|
|
undef,
|
|
{ f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' }
|
|
];
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_set();
|
|
SELECT * FROM perl_set();
|
|
|
|
CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$
|
|
return [
|
|
{ f1 => 1, f2 => 'Hello', f3 => 'World' },
|
|
{ f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' },
|
|
{ f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' }
|
|
];
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_set();
|
|
SELECT * FROM perl_set();
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION perl_record() RETURNS record AS $$
|
|
return undef;
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_record();
|
|
SELECT * FROM perl_record();
|
|
SELECT * FROM perl_record() AS (f1 integer, f2 text, f3 text);
|
|
|
|
CREATE OR REPLACE FUNCTION perl_record() RETURNS record AS $$
|
|
return {f2 => 'hello', f1 => 1, f3 => 'world'};
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_record();
|
|
SELECT * FROM perl_record();
|
|
SELECT * FROM perl_record() AS (f1 integer, f2 text, f3 text);
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION perl_record_set() RETURNS SETOF record AS $$
|
|
return undef;
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_record_set();
|
|
SELECT * FROM perl_record_set();
|
|
SELECT * FROM perl_record_set() AS (f1 integer, f2 text, f3 text);
|
|
|
|
CREATE OR REPLACE FUNCTION perl_record_set() RETURNS SETOF record AS $$
|
|
return [
|
|
{ f1 => 1, f2 => 'Hello', f3 => 'World' },
|
|
undef,
|
|
{ f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' }
|
|
];
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_record_set();
|
|
SELECT * FROM perl_record_set();
|
|
SELECT * FROM perl_record_set() AS (f1 integer, f2 text, f3 text);
|
|
|
|
CREATE OR REPLACE FUNCTION perl_record_set() RETURNS SETOF record AS $$
|
|
return [
|
|
{ f1 => 1, f2 => 'Hello', f3 => 'World' },
|
|
{ f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' },
|
|
{ f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' }
|
|
];
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_record_set();
|
|
SELECT * FROM perl_record_set();
|
|
SELECT * FROM perl_record_set() AS (f1 integer, f2 text, f3 text);
|
|
|
|
CREATE OR REPLACE FUNCTION
|
|
perl_out_params(f1 out integer, f2 out text, f3 out text) AS $$
|
|
return {f2 => 'hello', f1 => 1, f3 => 'world'};
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_out_params();
|
|
SELECT * FROM perl_out_params();
|
|
SELECT (perl_out_params()).f2;
|
|
|
|
CREATE OR REPLACE FUNCTION
|
|
perl_out_params_set(out f1 integer, out f2 text, out f3 text)
|
|
RETURNS SETOF record AS $$
|
|
return [
|
|
{ f1 => 1, f2 => 'Hello', f3 => 'World' },
|
|
{ f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' },
|
|
{ f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' }
|
|
];
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_out_params_set();
|
|
SELECT * FROM perl_out_params_set();
|
|
SELECT (perl_out_params_set()).f3;
|
|
|
|
--
|
|
-- Check behavior with erroneous return values
|
|
--
|
|
|
|
CREATE TYPE footype AS (x INTEGER, y INTEGER);
|
|
|
|
CREATE OR REPLACE FUNCTION foo_good() RETURNS SETOF footype AS $$
|
|
return [
|
|
{x => 1, y => 2},
|
|
{x => 3, y => 4}
|
|
];
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT * FROM foo_good();
|
|
|
|
CREATE OR REPLACE FUNCTION foo_bad() RETURNS footype AS $$
|
|
return {y => 3, z => 4};
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT * FROM foo_bad();
|
|
|
|
CREATE OR REPLACE FUNCTION foo_bad() RETURNS footype AS $$
|
|
return 42;
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT * FROM foo_bad();
|
|
|
|
CREATE OR REPLACE FUNCTION foo_bad() RETURNS footype AS $$
|
|
return [
|
|
[1, 2],
|
|
[3, 4]
|
|
];
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT * FROM foo_bad();
|
|
|
|
CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$
|
|
return 42;
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT * FROM foo_set_bad();
|
|
|
|
CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$
|
|
return {y => 3, z => 4};
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT * FROM foo_set_bad();
|
|
|
|
CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$
|
|
return [
|
|
[1, 2],
|
|
[3, 4]
|
|
];
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT * FROM foo_set_bad();
|
|
|
|
CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$
|
|
return [
|
|
{y => 3, z => 4}
|
|
];
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT * FROM foo_set_bad();
|
|
|
|
--
|
|
-- Check passing a tuple argument
|
|
--
|
|
|
|
CREATE OR REPLACE FUNCTION perl_get_field(footype, text) RETURNS integer AS $$
|
|
return $_[0]->{$_[1]};
|
|
$$ LANGUAGE plperl;
|
|
|
|
SELECT perl_get_field((11,12), 'x');
|
|
SELECT perl_get_field((11,12), 'y');
|
|
SELECT perl_get_field((11,12), 'z');
|
|
|
|
--
|
|
-- Test return_next
|
|
--
|
|
|
|
CREATE OR REPLACE FUNCTION perl_srf_rn() RETURNS SETOF RECORD AS $$
|
|
my $i = 0;
|
|
for ("World", "PostgreSQL", "PL/Perl") {
|
|
return_next({f1=>++$i, f2=>'Hello', f3=>$_});
|
|
}
|
|
return;
|
|
$$ language plperl;
|
|
SELECT * from perl_srf_rn() AS (f1 INTEGER, f2 TEXT, f3 TEXT);
|
|
|
|
--
|
|
-- Test spi_query/spi_fetchrow
|
|
--
|
|
|
|
CREATE OR REPLACE FUNCTION perl_spi_func() RETURNS SETOF INTEGER AS $$
|
|
my $x = spi_query("select 1 as a union select 2 as a");
|
|
while (defined (my $y = spi_fetchrow($x))) {
|
|
return_next($y->{a});
|
|
}
|
|
return;
|
|
$$ LANGUAGE plperl;
|
|
SELECT * from perl_spi_func();
|
|
|
|
--
|
|
-- Test spi_fetchrow abort
|
|
--
|
|
CREATE OR REPLACE FUNCTION perl_spi_func2() RETURNS INTEGER AS $$
|
|
my $x = spi_query("select 1 as a union select 2 as a");
|
|
spi_cursor_close( $x);
|
|
return 0;
|
|
$$ LANGUAGE plperl;
|
|
SELECT * from perl_spi_func2();
|
|
|
|
|
|
---
|
|
--- Test recursion via SPI
|
|
---
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION recurse(i int) RETURNS SETOF TEXT LANGUAGE plperl
|
|
AS $$
|
|
|
|
my $i = shift;
|
|
foreach my $x (1..$i)
|
|
{
|
|
return_next "hello $x";
|
|
}
|
|
if ($i > 2)
|
|
{
|
|
my $z = $i-1;
|
|
my $cursor = spi_query("select * from recurse($z)");
|
|
while (defined(my $row = spi_fetchrow($cursor)))
|
|
{
|
|
return_next "recurse $i: $row->{recurse}";
|
|
}
|
|
}
|
|
return undef;
|
|
|
|
$$;
|
|
|
|
SELECT * FROM recurse(2);
|
|
SELECT * FROM recurse(3);
|
|
|
|
|
|
---
|
|
--- Test arrary return
|
|
---
|
|
CREATE OR REPLACE FUNCTION array_of_text() RETURNS TEXT[][]
|
|
LANGUAGE plperl as $$
|
|
return [['a"b',undef,'c,d'],['e\\f',undef,'g']];
|
|
$$;
|
|
|
|
SELECT array_of_text();
|
|
|
|
--
|
|
-- Test spi_prepare/spi_exec_prepared/spi_freeplan
|
|
--
|
|
CREATE OR REPLACE FUNCTION perl_spi_prepared(INTEGER) RETURNS INTEGER AS $$
|
|
my $x = spi_prepare('select $1 AS a', 'INT4');
|
|
my $q = spi_exec_prepared( $x, $_[0] + 1);
|
|
spi_freeplan($x);
|
|
return $q->{rows}->[0]->{a};
|
|
$$ LANGUAGE plperl;
|
|
SELECT * from perl_spi_prepared(42);
|
|
|
|
--
|
|
-- Test spi_prepare/spi_query_prepared/spi_freeplan
|
|
--
|
|
CREATE OR REPLACE FUNCTION perl_spi_prepared_set(INTEGER, INTEGER) RETURNS SETOF INTEGER AS $$
|
|
my $x = spi_prepare('SELECT $1 AS a union select $2 as a', 'INT4', 'INT4');
|
|
my $q = spi_query_prepared( $x, 1+$_[0], 2+$_[1]);
|
|
while (defined (my $y = spi_fetchrow($q))) {
|
|
return_next $y->{a};
|
|
}
|
|
spi_freeplan($x);
|
|
return;
|
|
$$ LANGUAGE plperl;
|
|
SELECT * from perl_spi_prepared_set(1,2);
|
|
|
|
---
|
|
--- Some OUT and OUT array tests
|
|
---
|
|
|
|
CREATE OR REPLACE FUNCTION test_out_params(OUT a varchar, OUT b varchar) AS $$
|
|
return { a=> 'ahoj', b=>'svete'};
|
|
$$ LANGUAGE plperl;
|
|
SELECT '01' AS i, * FROM test_out_params();
|
|
|
|
CREATE OR REPLACE FUNCTION test_out_params_array(OUT a varchar[], OUT b varchar[]) AS $$
|
|
return { a=> ['ahoj'], b=>['svete']};
|
|
$$ LANGUAGE plperl;
|
|
SELECT '02' AS i, * FROM test_out_params_array();
|
|
|
|
CREATE OR REPLACE FUNCTION test_out_params_set(OUT a varchar, out b varchar) RETURNS SETOF RECORD AS $$
|
|
return_next { a=> 'ahoj', b=>'svete'};
|
|
return_next { a=> 'ahoj', b=>'svete'};
|
|
return_next { a=> 'ahoj', b=>'svete'};
|
|
$$ LANGUAGE plperl;
|
|
SELECT '03' AS I,* FROM test_out_params_set();
|
|
|
|
CREATE OR REPLACE FUNCTION test_out_params_set_array(OUT a varchar[], out b varchar[]) RETURNS SETOF RECORD AS $$
|
|
return_next { a=> ['ahoj'], b=>['velky','svete']};
|
|
return_next { a=> ['ahoj'], b=>['velky','svete']};
|
|
return_next { a=> ['ahoj'], b=>['velky','svete']};
|
|
$$ LANGUAGE plperl;
|
|
SELECT '04' AS I,* FROM test_out_params_set_array();
|
|
|
|
|
|
DROP FUNCTION test_out_params();
|
|
DROP FUNCTION test_out_params_set();
|
|
DROP FUNCTION test_out_params_array();
|
|
DROP FUNCTION test_out_params_set_array();
|
|
|
|
-- one out argument can be returned as scalar or hash
|
|
CREATE OR REPLACE FUNCTION test01(OUT a varchar) AS $$
|
|
return 'ahoj';
|
|
$$ LANGUAGE plperl ;
|
|
SELECT '01' AS i,* FROM test01();
|
|
|
|
CREATE OR REPLACE FUNCTION test02(OUT a varchar[]) AS $$
|
|
return {a=>['ahoj']};
|
|
$$ LANGUAGE plperl;
|
|
SELECT '02' AS i,a[1] FROM test02();
|
|
|
|
CREATE OR REPLACE FUNCTION test03(OUT a varchar[]) RETURNS SETOF varchar[] AS $$
|
|
return_next { a=> ['ahoj']};
|
|
return_next { a=> ['ahoj']};
|
|
return_next { a=> ['ahoj']};
|
|
$$ LANGUAGE plperl;
|
|
SELECT '03' AS i,* FROM test03();
|
|
|
|
CREATE OR REPLACE FUNCTION test04() RETURNS SETOF VARCHAR[] AS $$
|
|
return_next ['ahoj'];
|
|
return_next ['ahoj'];
|
|
$$ LANGUAGE plperl;
|
|
SELECT '04' AS i,* FROM test04();
|
|
|
|
CREATE OR REPLACE FUNCTION test05(OUT a varchar) AS $$
|
|
return {a=>'ahoj'};
|
|
$$ LANGUAGE plperl;
|
|
SELECT '05' AS i,a FROM test05();
|
|
|
|
CREATE OR REPLACE FUNCTION test06(OUT a varchar) RETURNS SETOF varchar AS $$
|
|
return_next { a=> 'ahoj'};
|
|
return_next { a=> 'ahoj'};
|
|
return_next { a=> 'ahoj'};
|
|
$$ LANGUAGE plperl;
|
|
SELECT '06' AS i,* FROM test06();
|
|
|
|
CREATE OR REPLACE FUNCTION test07() RETURNS SETOF VARCHAR AS $$
|
|
return_next 'ahoj';
|
|
return_next 'ahoj';
|
|
$$ LANGUAGE plperl;
|
|
SELECT '07' AS i,* FROM test07();
|
|
|
|
DROP FUNCTION test01();
|
|
DROP FUNCTION test02();
|
|
DROP FUNCTION test03();
|
|
DROP FUNCTION test04();
|
|
DROP FUNCTION test05();
|
|
DROP FUNCTION test06();
|
|
DROP FUNCTION test07();
|
|
|