X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=t%2F72pg.t;h=1e7ed0a48386e9f72c483225294118c3a71036c2;hb=961d79dbe25ef8a92e867fcc84914b4bf568c11d;hp=4065b260e55f181d78a666885c7b42d6ed5c3bd2;hpb=1f5aae08362fffa1fcf0991909293d9c418e1c40;p=dbsrgits%2FDBIx-Class-Historic.git diff --git a/t/72pg.t b/t/72pg.t index 4065b26..1e7ed0a 100644 --- a/t/72pg.t +++ b/t/72pg.t @@ -3,18 +3,25 @@ use warnings; use Test::More; use Test::Exception; +use Sub::Name; +use Config; +use DBIx::Class::Optional::Dependencies (); use lib qw(t/lib); use DBICTest; +use SQL::Abstract 'is_literal_value'; +use DBIx::Class::_Util 'is_exception'; +plan skip_all => 'Test needs ' . DBIx::Class::Optional::Dependencies->req_missing_for ('test_rdbms_pg') + unless DBIx::Class::Optional::Dependencies->req_ok_for ('test_rdbms_pg'); my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_PG_${_}" } qw/DSN USER PASS/}; -plan skip_all => < <<'EOM' unless $dsn && $user; +Set $ENV{DBICTEST_PG_DSN}, _USER and _PASS to run this test ( NOTE: This test drops and creates tables called 'artist', 'cd', 'timestamp_primary_key_test', 'track', 'casecheck', 'array_test' and 'sequence_test' as well as following sequences: 'pkid1_seq', 'pkid2_seq' and -'nonpkid_seq''. as well as following schemas: 'dbic_t_schema', +'nonpkid_seq'. as well as following schemas: 'dbic_t_schema', 'dbic_t_schema_2', 'dbic_t_schema_3', 'dbic_t_schema_4', and 'dbic_t_schema_5') EOM @@ -23,19 +30,6 @@ EOM our @test_classes; #< array that will be pushed into by test classes defined in this file DBICTest::Schema->load_classes( map {s/.+:://;$_} @test_classes ) if @test_classes; -my $schema; - -require DBIx::Class::Storage::DBI::Pg; - -my $can_insert_returning = - DBIx::Class::Storage::DBI::Pg->can('can_insert_returning'); - -for my $use_insert_returning (0..1) { - no warnings qw/redefine once/; - local *DBIx::Class::Storage::DBI::Pg::can_insert_returning = sub { - $use_insert_returning - }; - ### pre-connect tests (keep each test separate as to make sure rebless() runs) { my $s = DBICTest::Schema->connect($dsn, $user, $pass); @@ -44,8 +38,10 @@ for my $use_insert_returning (0..1) { # Check that datetime_parser returns correctly before we explicitly connect. SKIP: { - eval { require DateTime::Format::Pg }; - skip "DateTime::Format::Pg required", 2 if $@; + skip ( + "Pg parser detection test needs " . DBIx::Class::Optional::Dependencies->req_missing_for ('test_dt_pg'), + 2 + ) unless DBIx::Class::Optional::Dependencies->req_ok_for ('test_dt_pg'); my $store = ref $s->storage; is($store, 'DBIx::Class::Storage::DBI', 'Started with generic storage'); @@ -56,6 +52,7 @@ for my $use_insert_returning (0..1) { ok (!$s->storage->_dbh, 'still not connected'); } + { my $s = DBICTest::Schema->connect($dsn, $user, $pass); # make sure sqlt_type overrides work (::Storage::DBI::Pg does this) @@ -64,18 +61,84 @@ for my $use_insert_returning (0..1) { ok (!$s->storage->_dbh, 'still not connected'); } -### connect, create postgres-specific test schema +# test LIMIT support +{ + my $schema = DBICTest::Schema->connect($dsn, $user, $pass); + drop_test_schema($schema); + create_test_schema($schema); + for (1..6) { + $schema->resultset('Artist')->create({ name => 'Artist ' . $_ }); + } + my $it = $schema->resultset('Artist')->search( {}, + { rows => 3, + offset => 2, + order_by => 'artistid' } + ); + is( $it->count, 3, "LIMIT count ok" ); # ask for 3 rows out of 6 artists + is( $it->next->name, "Artist 3", "iterator->next ok" ); + $it->next; + $it->next; + $it->next; + is( $it->next, undef, "next past end of resultset ok" ); + + # Limit with select-lock + lives_ok { + $schema->txn_do (sub { + isa_ok ( + $schema->resultset('Artist')->find({artistid => 1}, {for => 'update', rows => 1}), + 'DBICTest::Schema::Artist', + ); + }); + } 'Limited FOR UPDATE select works'; +} - $schema = DBICTest::Schema->connect($dsn, $user, $pass); - $schema->storage->ensure_connected; +# check if we indeed do support stuff +my $test_server_supports_insert_returning = do { + + my $si = DBICTest::Schema->connect($dsn, $user, $pass)->storage->_server_info; + die "Unparseable Pg server version: $si->{dbms_version}\n" + unless $si->{normalized_dbms_version}; + + $si->{normalized_dbms_version} < 8.002 ? 0 : 1; +}; +is ( + DBICTest::Schema->connect($dsn, $user, $pass)->storage->_use_insert_returning, + $test_server_supports_insert_returning, + 'insert returning capability guessed correctly' +); + +my $schema; +for my $use_insert_returning ($test_server_supports_insert_returning + ? (0,1) + : (0) +) { + + no warnings qw/once redefine/; + my $old_connection = DBICTest::Schema->can('connection'); + local *DBICTest::Schema::connection = subname 'DBICTest::Schema::connection' => sub { + my $s = shift->$old_connection(@_); + $s->storage->_use_insert_returning ($use_insert_returning); + $s; + }; - if ($use_insert_returning && (not $can_insert_returning->($schema->storage))) +### test capability override { - diag "Your version of PostgreSQL does not support INSERT ... RETURNING."; - diag "*** SKIPPING FURTHER TESTS"; - last; + my $s = DBICTest::Schema->connect($dsn, $user, $pass); + + ok (!$s->storage->_dbh, 'definitely not connected'); + + ok ( + ! ($s->storage->_use_insert_returning xor $use_insert_returning), + 'insert returning capability set correctly', + ); + ok (!$s->storage->_dbh, 'still not connected (capability override works)'); } +### connect, create postgres-specific test schema + + $schema = DBICTest::Schema->connect($dsn, $user, $pass); + $schema->storage->ensure_connected; + drop_test_schema($schema); create_test_schema($schema); @@ -140,7 +203,7 @@ for my $use_insert_returning (0..1) { use strict; use warnings; - use base 'DBIx::Class::Core'; + use base 'DBICTest::BaseResult'; __PACKAGE__->table('dbic_t_schema.array_test'); __PACKAGE__->add_columns(qw/id arrayfield/); @@ -151,32 +214,98 @@ for my $use_insert_returning (0..1) { SKIP: { skip "Need DBD::Pg 2.9.2 or newer for array tests", 4 if $DBD::Pg::VERSION < 2.009002; + my $arr_rs = $schema->resultset('ArrayTest'); + lives_ok { - $schema->resultset('ArrayTest')->create({ + $arr_rs->create({ arrayfield => [1, 2], }); } 'inserting arrayref as pg array data'; lives_ok { - $schema->resultset('ArrayTest')->update({ + $arr_rs->update({ arrayfield => [3, 4], }); } 'updating arrayref as pg array data'; - $schema->resultset('ArrayTest')->create({ + $arr_rs->create({ arrayfield => [5, 6], }); - my $count; lives_ok { - $count = $schema->resultset('ArrayTest')->search({ - arrayfield => \[ '= ?' => [arrayfield => [3, 4]] ], #Todo anything less ugly than this? - })->count; - } 'comparing arrayref to pg array data does not blow up'; - is($count, 1, 'comparing arrayref to pg array data gives correct result'); - } + $schema->populate('ArrayTest', [ + [ qw/arrayfield/ ], + [ [0,0] ], + ]); + } 'inserting arrayref using void ctx populate'; + # Search using arrays + lives_ok { + is_deeply ( + $arr_rs->search({ arrayfield => { -value => [3,4] } })->first->arrayfield, + [3,4], + 'Array value matches' + ); + } 'searching by arrayref'; + + lives_ok { + is_deeply ( + $arr_rs->search({ arrayfield => { '=' => { -value => [3,4] }} })->first->arrayfield, + [3,4], + 'Array value matches explicit equal' + ); + } 'searching by arrayref (explicit equal sign)'; + + lives_ok { + is_deeply ( + $arr_rs->search({ arrayfield => { '>' => { -value => [3,1] }} })->first->arrayfield, + [3,4], + 'Array value matches greater than' + ); + } 'searching by arrayref (greater than)'; + + lives_ok { + is ( + $arr_rs->search({ arrayfield => { '>' => { -value => [3,7] }} })->count, + 1, + 'Greater than search found [5,6]', + ); + } 'searching by arrayref (greater than)'; + + # Find using arrays + lives_ok { + is_deeply ( + $arr_rs->find({ arrayfield => { -value => [3,4] } })->arrayfield, + [3,4], + 'Array value matches implicit equal' + ); + } 'find by arrayref'; + lives_ok { + is_deeply ( + $arr_rs->find({ arrayfield => { '=' => { -value => [3,4] }} })->arrayfield, + [3,4], + 'Array value matches explicit equal' + ); + } 'find by arrayref (equal)'; + + # test inferred condition for creation + for my $cond ( + { -value => [3,4] }, + \[ '= ?' => [arrayfield => [3, 4]] ], + ) { + local $TODO = 'No introspection of complex literal conditions :(' + if is_literal_value $cond; + + + my $arr_rs_cond = $arr_rs->search({ arrayfield => $cond }); + + my $row = $arr_rs_cond->create({}); + is_deeply ($row->arrayfield, [3,4], 'Array value taken from $rs condition'); + $row->discard_changes; + is_deeply ($row->arrayfield, [3,4], 'Array value made it to storage'); + } + } ########## Case check @@ -204,17 +333,17 @@ for my $use_insert_returning (0..1) { is( $uc_name_info->{size}, 3, "Case insensitive matching info for 'uc_name'" ); - +## Test ResultSet->update +my $artist = $schema->resultset('Artist')->first; +my $cds = $artist->cds_unordered->search({ + year => { '!=' => 2010 } +}, { prefetch => 'liner_notes' }); +lives_ok { $cds->update({ year => '2010' }) } 'Update on prefetched rs'; ## Test SELECT ... FOR UPDATE - SKIP: { - if(eval "require Sys::SigAction" && !$@) { - Sys::SigAction->import( 'set_sig_handler' ); - } - else { - skip "Sys::SigAction is not available", 6; - } + skip "Your system does not support unsafe signals (d_sigaction) - unable to run deadlock test", 1 + unless eval { $Config{d_sigaction} and require POSIX }; my ($timed_out, $artist2); @@ -241,24 +370,40 @@ for my $use_insert_returning (0..1) { $schema2->source("Artist")->name("dbic_t_schema.artist"); $schema->txn_do( sub { - my $artist = $schema->resultset('Artist')->search( + my $rs = $schema->resultset('Artist')->search( { artistid => 1 }, $t->{update_lock} ? { for => 'update' } : {} - )->first; + ); + ok ($rs->count, 'Count works'); + + my $artist = $rs->next; is($artist->artistid, 1, "select returns artistid = 1"); $timed_out = 0; + eval { - my $h = set_sig_handler( 'ALRM', sub { die "DBICTestTimeout" } ); + # can not use %SIG assignment directly - we need sigaction below + # localization to a block still works however + local $SIG{ALRM}; + + POSIX::sigaction( POSIX::SIGALRM() => POSIX::SigAction->new( + sub { die "DBICTestTimeout" }, + )); + alarm(2); $artist2 = $schema2->resultset('Artist')->find(1); $artist2->name('fooey'); $artist2->update; - alarm(0); }; - $timed_out = $@ =~ /DBICTestTimeout/; + + alarm(0); + + if (is_exception($@)) { + $timed_out = $@ =~ /DBICTestTimeout/ + or die $@; + } }); $t->{test_sub}->(); @@ -281,7 +426,7 @@ for my $use_insert_returning (0..1) { ######## test non-serial auto-pk - if ($schema->storage->can_insert_returning) { + if ($schema->storage->_use_insert_returning) { $schema->source('TimestampPrimaryKey')->name('dbic_t_schema.timestamp_primary_key_test'); my $row = $schema->resultset('TimestampPrimaryKey')->create({}); ok $row->id; @@ -303,7 +448,7 @@ for my $use_insert_returning (0..1) { } 'with_deferred_fk_checks code survived'; is eval { $schema->resultset('Track')->find(999)->title }, 'deferred FK track', - 'code in with_deferred_fk_checks worked'; + 'code in with_deferred_fk_checks worked'; throws_ok { $schema->resultset('Track')->create({ @@ -317,7 +462,8 @@ done_testing; END { return unless $schema; drop_test_schema($schema); - eapk_drop_all( $schema) + eapk_drop_all($schema); + undef $schema; }; @@ -365,8 +511,7 @@ CREATE TABLE dbic_t_schema.track ( position int, title varchar(255), last_updated_on date, - last_updated_at date, - small_dt date + last_updated_at date ) EOS @@ -441,12 +586,12 @@ sub drop_test_schema { for my $stat ( 'DROP SCHEMA dbic_t_schema_5 CASCADE', - 'DROP SEQUENCE public.artist_artistid_seq', + 'DROP SEQUENCE public.artist_artistid_seq CASCADE', 'DROP SCHEMA dbic_t_schema_4 CASCADE', 'DROP SCHEMA dbic_t_schema CASCADE', - 'DROP SEQUENCE pkid1_seq', - 'DROP SEQUENCE pkid2_seq', - 'DROP SEQUENCE nonpkid_seq', + 'DROP SEQUENCE pkid1_seq CASCADE', + 'DROP SEQUENCE pkid2_seq CASCADE', + 'DROP SEQUENCE nonpkid_seq CASCADE', 'DROP SCHEMA dbic_t_schema_2 CASCADE', 'DROP SCHEMA dbic_t_schema_3 CASCADE', ) {