create or replace function test_call() returns text language plperlu as $func$ use lib "/Users/timbo/pg/PostgreSQL-PLPerl-Call/lib"; use PostgreSQL::PLPerl::Call; use Test::More 'no_plan'; my $row; my @ary; # ====== single-value single-row function ====== # --- no arguments like call('pi()'), qr/^3.14159/; # bad calls eval { call('pi()', 42) }; like $@, qr/expected 0 argument/; eval { call('pi', 42) }; like $@, qr/Can't parse 'pi'/; # error from call() itself # --- one argument, simple types is call('abs(int)', -42), 42; is call('abs(float)', -42.5), '42.5'; is call('bit_length(text)', 'jose'), 32; # --- one argument, multi-word types is call('abs(double precision)', -42.5), '42.5'; is call('bit_length(character varying(90))', 'jose'), 32; # --- lock calls call('pg_try_advisory_lock_shared(bigint)', 1234); call('pg_advisory_unlock_all()'); # bad calls eval { call('abs(int)', -42.5) }; like $@, qr/invalid input syntax for integer/; eval { call('abs(text)', -42.5) }; like $@, qr/function abs\(text\) does not exist/; eval { call('abs(nonesuchtype)', -42.5) }; like $@, qr/type "nonesuchtype" does not exist/; # --- multi-argument, simple types is call('trunc(numeric,int)', 42.4382, 2), '42.43'; # --- unusual types from strings is call('host(inet)', '192.168.1.5/24'), '192.168.1.5'; is call('network(inet)', '192.168.1.5/24'), '192.168.1.0/24'; is call('abbrev(cidr)', '10.1.0.0/16'), '10.1/16'; is call('numnode(tsquery)', '(fat & rat) | cat'), 5; spi_exec_query('create temp sequence seqn1 start with 42'); is call('nextval(regclass)', 'seqn1'), 42; is call('nextval(text)', 'seqn1'), 43; is call('string_to_array(text, text)', 'xx~^~yy~^~zz', '~^~'), '{xx,yy,zz}'; # --- array and array reference handling is call('array_dims(text[])', '{a,b,c}'), '[1:3]'; is call('array_dims(text[])', [qw(a b c)]), '[1:3]'; is call('array_dims(text[])', [[1,2,3], [4,5,6]]), '[1:2][1:3]'; is call('array_cat(int[], int[])', [1,2,3], [2,1]), '{1,2,3,2,1}'; # ====== single-value multi-row function ====== @ary = call('unnest(int[])', '{11,12,13}'); is scalar @ary, 3; is_deeply \@ary, [ 11, 12, 13 ]; @ary = call('generate_series(int,int)', 10, 19); is scalar @ary, 10; is_deeply \@ary, [ 10..19 ]; @ary = call('generate_series(int,int,int)', 10, 19, 4); is_deeply \@ary, [ 10, 14, 18 ]; @ary = call('generate_series(timestamp,timestamp,interval)', '2008-03-01', '2008-03-02', '12 hours'); is_deeply \@ary, [ '2008-03-01 00:00:00', '2008-03-01 12:00:00', '2008-03-02 00:00:00' ]; # bad calls eval { scalar call('generate_series(int,int)', 10, 19) }; like $@, qr/returned more than one row/; # ====== multi-value (record) returning functions ====== @ary = call('pg_get_keywords()'); cmp_ok scalar @ary, '>', 200; ok $row = $ary[0]; is ref $row, 'HASH'; ok exists $row->{word}, 'should contain a word column'; ok exists $row->{catcode}, 'should contain a catcode column'; ok exists $row->{catdesc}, 'should contain a catdesc column'; # single-record spi_exec_query(q{ create or replace function f1(out r1 text, out r2 int) language plperl as $$ return { r1=>10, r2=>11 }; $$ }); @ary = call('f1()'); is scalar @ary, 1; ok $row = $ary[0]; is $row->{r1}, 10; is $row->{r2}, 11; spi_exec_query('drop function f1()'); # multi-record spi_exec_query(q{ create or replace function f2() returns table (r1 text, r2 int) language plperl as $$ return_next { r1 => $_, r2 => $_+1 } for 1..5; return undef; $$ }); @ary = call('f2()'); is scalar @ary, 5; is $ary[-1]->{r1}, 5; is $ary[-1]->{r2}, 6; spi_exec_query('drop function f2()'); # ====== functions with defaults or varargs ====== spi_exec_query(q{ create or replace function f3(int default 42) returns int language plperl as $$ return shift() + 1; $$ }); is call('f3()'), 43; spi_exec_query('drop function f3(int)'); spi_exec_query(q{ create or replace function f4(VARIADIC numeric[]) returns float language plperlu as $$ use PostgreSQL::PLPerl::Call; my $sum; $sum += $_ for call('unnest(numeric[])', $_[0]); return $sum; $$ }); # call varadic with explicit number of args in the signature is call('f4(numeric, numeric)', 10,11 ), 21; is call('f4(numeric, numeric, numeric)', 10,11,12), 33; # call varadic using '...' in the signature is call('f4(numeric, numeric ...)', 10,11,12), 33; is call('f4(numeric ...)', 10,11,12), 33; is call('f4(numeric ...)', 10,11 ), 21; is call('f4(numeric ...)', 10 ), 10; # XXX doesn't work with no args - possibly natural consequence #is call('f4(numeric ...)' ), undef; spi_exec_query('drop function f4(varadic numeric[])'); # === finish up Test::More->builder->_ending; return undef; $func$; select * from test_call();