159 lines
5.9 KiB
Perl
159 lines
5.9 KiB
Perl
use v5.36;
|
|
use Test::More;
|
|
use FU::SQL;
|
|
|
|
plan skip_all => $@ if !eval { require FU::Pg; } && $@ =~ /Unable to load libpq/;
|
|
die $@ if $@;
|
|
plan skip_all => 'Please set FU_TEST_DB to a PostgreSQL connection string to run these tests' if !$ENV{FU_TEST_DB};
|
|
|
|
my $conn = FU::Pg->connect($ENV{FU_TEST_DB});
|
|
$conn->_debug_trace(0);
|
|
|
|
|
|
is_deeply $conn->Q('SELECT', 1, '::int')->param_types, [23];
|
|
is_deeply $conn->Q('SELECT 1', IN([1,2,3]))->param_types, [1007];
|
|
is $conn->Q('SELECT 1', IN([1,2,3]))->val, 1;
|
|
|
|
ok !eval { $conn->q('SELECT $1::aclitem', '')->exec; 1 };
|
|
like $@, qr/Unable to send type/;
|
|
|
|
|
|
subtest 'type overrides', sub {
|
|
$conn->set_type(int4 => recv => 'bytea');
|
|
is $conn->q('SELECT 5::int4')->val, "\0\0\0\5";
|
|
is_deeply $conn->q('SELECT ARRAY[5::int4]')->val, ["\0\0\0\5"];
|
|
|
|
$conn->set_type(int4 => send => 'bytea');
|
|
is $conn->q('SELECT $1::int4', "\0\0\0\5")->val, 5;
|
|
is_deeply $conn->q('SELECT $1::int4[]', ["\0\0\0\5"])->val, [5];
|
|
|
|
$conn->set_type(int4 => 'int2');
|
|
ok !eval { $conn->q('SELECT 5::int4')->val };
|
|
like $@, qr/Error parsing value/;
|
|
ok !eval { $conn->q('SELECT $1::int4', 5)->val };
|
|
like $@, qr/insufficient data left in message/;
|
|
|
|
$conn->set_type(int4 => undef);
|
|
is $conn->q('SELECT 5::int4')->val, 5;
|
|
|
|
ok !eval { $conn->set_type(int4 => 1007); };
|
|
like $@, qr/Cannot set a type to array/;
|
|
|
|
ok !eval { $conn->set_type(int4 => 1); };
|
|
like $@, qr/No builtin type found/;
|
|
};
|
|
|
|
|
|
subtest 'type override callback', sub {
|
|
$conn->set_type(text => recv => sub { length $_[0] });
|
|
is $conn->q('SELECT $1', 'a')->val, 1;
|
|
is $conn->q('SELECT $1', 'ab')->val, 2;
|
|
is $conn->q('SELECT $1', 'abc')->val, 3;
|
|
is $conn->q('SELECT $1', 'abcd')->val, 4;
|
|
|
|
$conn->set_type(text => send => sub { 'l'.length $_[0] });
|
|
is $conn->q('SELECT $1', 'a')->val, 'l1';
|
|
is $conn->q('SELECT $1', 'ab')->val, 'l2';
|
|
is $conn->q('SELECT $1', 'abc')->val, 'l3';
|
|
is $conn->q('SELECT $1', 'abcd')->val, 'l4';
|
|
};
|
|
|
|
|
|
subtest 'custom types', sub {
|
|
my $txn = $conn->txn;
|
|
|
|
is $txn->Q('SELECT 1', IN([1,2,3]))->val, 1;
|
|
|
|
$txn->exec(<<~_);
|
|
CREATE TYPE fupg_test_enum AS ENUM('aa', 'bb', 'ccccccccccccccccccc');
|
|
CREATE DOMAIN fupg_test_domain AS fupg_test_enum CHECK(value IN('aa','bb'));
|
|
CREATE TYPE fupg_test_record AS (
|
|
a int,
|
|
aenum fupg_test_enum[],
|
|
domain fupg_test_domain
|
|
);
|
|
_
|
|
is $txn->q("SELECT 'aa'::fupg_test_enum")->val, 'aa';
|
|
is $txn->q('SELECT $1::fupg_test_enum', 'ccccccccccccccccccc')->val, 'ccccccccccccccccccc';
|
|
|
|
is_deeply $txn->q("SELECT '{aa,bb,null}'::fupg_test_enum[]")->val, ['aa','bb',undef];
|
|
is $txn->q('SELECT $1::fupg_test_enum[]', ['aa','bb',undef])->text_results->val, '{aa,bb,NULL}';
|
|
|
|
is $txn->q("SELECT 'aa'::fupg_test_domain")->val, 'aa';
|
|
is $txn->q('SELECT $1::fupg_test_domain', 'bb')->val, 'bb';
|
|
|
|
is_deeply $txn->q("SELECT '{aa,bb,null}'::fupg_test_domain[]")->val, ['aa','bb',undef];
|
|
is $txn->q('SELECT $1::fupg_test_domain[]', ['aa','bb',undef])->text_results->val, '{aa,bb,NULL}';
|
|
|
|
my $val = { a => undef, aenum => ['aa','bb'], domain => 'aa' };
|
|
is_deeply $txn->q("SELECT '(,\"{aa,bb}\",aa)'::fupg_test_record")->val, $val;
|
|
is $txn->q('SELECT $1::fupg_test_record', $val)->text_results->val, '(,"{aa,bb}",aa)';
|
|
|
|
$txn->exec(<<~_);
|
|
CREATE TEMPORARY TABLE fupg_test_table (
|
|
rec fupg_test_record,
|
|
dom fupg_test_domain
|
|
);
|
|
_
|
|
|
|
$val = $txn->q(q{SELECT '{"(\"(2,{},bb)\",)","(\"(,,)\",bb)"}'::fupg_test_table[]})->val;
|
|
is_deeply $val, [
|
|
{ rec => { a => 2, aenum => [], domain => 'bb' }, dom => undef },
|
|
{ rec => { a => undef, aenum => undef, domain => undef }, dom => 'bb' },
|
|
];
|
|
$val->[0] = 0;
|
|
$val->[1]{rec}{a} = 0;
|
|
$val->[1]{rec} = 0;
|
|
$val->[1]{dom} = 0;
|
|
|
|
is $txn->q('SELECT $1::fupg_test_table[]', [
|
|
{ rec => { a => 2, aenum => [], domain => 'bb' }, dom => undef },
|
|
{ rec => {}, dom => 'bb', extra => 1 },
|
|
])->text_results->val, '{"(\"(2,{},bb)\",)","(\"(,,)\",bb)"}';
|
|
|
|
# Wonky Postgres behavior: selecting a domain directly actually returns the
|
|
# underlying type, but going through an array does work.
|
|
$conn->set_type(fupg_test_domain => 21);
|
|
is_deeply $txn->q("SELECT ARRAY['aa'::fupg_test_domain]")->val, [0x6161];
|
|
|
|
# Bind param type doesn't match column type, argh.
|
|
is $txn->q('SELECT $1::fupg_test_domain', 0x6161)->val, 'aa';
|
|
|
|
# Same for selecting from a table :(
|
|
$txn->exec("INSERT INTO fupg_test_table VALUES (NULL, 'bb')");
|
|
is $txn->q("SELECT dom FROM fupg_test_table")->val, 'bb';
|
|
$conn->set_type(fupg_test_enum => 21);
|
|
is $txn->q("SELECT dom FROM fupg_test_table")->val, 0x6262;
|
|
};
|
|
|
|
|
|
subtest 'identifier quoting', sub {
|
|
my $txn = $conn->txn;
|
|
$txn->exec('CREATE TEMPORARY TABLE fupg_test_tbl ("desc" int, ok int, "hello world" int)');
|
|
ok $txn->Q('INSERT INTO fupg_test_tbl', VALUES {desc => 5, ok => 10, 'hello world', 15})->exec;
|
|
is $txn->Q('SELECT', IDENT 'hello world', 'FROM fupg_test_tbl')->val, 15;
|
|
};
|
|
|
|
|
|
subtest 'vndbid', sub {
|
|
plan skip_all => 'type not loaded in the database' if !$conn->q("SELECT 1 FROM pg_type WHERE typname = 'vndbtag'")->val;
|
|
|
|
for my $t (qw/a zz xxx/) {
|
|
is $conn->q('SELECT $1::vndbtag', $t)->val, $t;
|
|
is $conn->q('SELECT $1::vndbtag', $t)->text_params->val, $t;
|
|
is $conn->q('SELECT $1::vndbtag', $t)->text_results->val, $t;
|
|
}
|
|
ok !eval { $conn->q('SELECT $1::vndbtag', '')->val };
|
|
ok !eval { $conn->q('SELECT $1::vndbtag', 'abcd')->val };
|
|
|
|
for my $t (qw/a123 zz992883231 xxx18388123/) {
|
|
is $conn->q('SELECT $1::vndbid', $t)->val, $t;
|
|
is $conn->q('SELECT $1::vndbid', $t)->text_params->val, $t;
|
|
is $conn->q('SELECT $1::vndbid', $t)->text_results->val, $t;
|
|
}
|
|
ok !eval { $conn->q('SELECT $1::vndbid', '')->val };
|
|
ok !eval { $conn->q('SELECT $1::vndbid', 'ab')->val };
|
|
ok !eval { $conn->q('SELECT $1::vndbid', 'ab1219229999999999')->val };
|
|
};
|
|
|
|
done_testing;
|