#!perl ## Test the COPY functionality use 5.006; use strict; use warnings; use Data::Dumper; use DBD::Pg ':async'; use Test::More; use lib 't','.'; require 'dbdpg_test_setup.pl'; select(($|=1,select(STDERR),$|=1)[1]); my $dbh = connect_database(); if ($dbh) { plan tests => 57; } else { plan skip_all => 'Connection to database failed, cannot continue testing'; } ok (defined $dbh, 'Connect to database for bytea testing'); my ($result,$expected,@data,$t); my $table = 'dbd_pg_test4'; $dbh->do(qq{CREATE TABLE $table(id2 integer, val2 text)}); $dbh->commit(); my $pgversion = $dbh->{pg_server_version}; # # Test of the pg_putline and pg_endcopy methods # ## pg_putline should fail unless we are in a COPY IN state $t='pg_putline fails when issued without a preceding COPY command'; eval { $dbh->pg_putline("12\tMulberry"); }; ok ($@, $t); $t='putline returned a value of 1 for success'; $dbh->do("COPY $table FROM STDIN"); $result = $dbh->pg_putline("12\tMulberry\n"); is ($result, 1, $t); $t='putline returned a value of 1 for success'; $result = $dbh->pg_putline("13\tStrawberry\n"); is ($result, 1, $t); $t='putline returned a value of 1 for success'; $result = $dbh->pg_putline("14\tBlueberry\n"); is ($result, 1, $t); ## Commands are not allowed while in a COPY IN state $t='do() fails while in a COPY IN state'; eval { $dbh->do(q{SELECT 'dbdpg_copytest'}); }; ok ($@, $t); ## pg_getline is not allowed as we are in a COPY_IN state $t='pg_getline fails while in a COPY IN state'; $data[0] = ''; eval { $dbh->pg_getline($data[0], 100); }; ok ($@, $t); $t='pg_endcopy returned a 1'; $result = $dbh->pg_endcopy(); is ($result, 1, $t); ## Make sure we can issue normal commands again $dbh->do(q{SELECT 'dbdpg_copytest'}); ## Make sure we are out of the COPY IN state and pg_putline no longer works $t='pg_putline fails when issued after pg_endcopy called'; eval { $dbh->pg_putline("16\tBlackberry"); }; ok ($@, $t); ## Check that our lines were inserted properly $t='putline inserted values correctly'; $expected = [[12 => 'Mulberry'],[13 => 'Strawberry'],[14 => 'Blueberry']]; $result = $dbh->selectall_arrayref("SELECT id2,val2 FROM $table ORDER BY id2"); is_deeply ($result, $expected, $t); # pg_endcopy should not work because we are no longer in a COPY state $t='pg_endcopy fails when called twice after COPY IN'; eval { $dbh->pg_endcopy; }; ok ($@, $t); $dbh->commit(); # # Test of the pg_getline method # ## pg_getline should fail unless we are in a COPY OUT state $t='pg_getline fails when issued without a preceding COPY command'; eval { $dbh->pg_getline($data[0], 100); }; ok ($@, $t); $t='pg_getline returns a 1'; $dbh->do("COPY $table TO STDOUT"); my ($buffer,$badret,$badval) = ('',0,0); $result = $dbh->pg_getline($data[0], 100); is ($result, 1, $t); ## Commands are not allowed while in a COPY OUT state $t='do() fails while in a COPY OUT state'; eval { $dbh->do(q{SELECT 'dbdpg_copytest'}); }; ok ($@, $t); ## pg_putline is not allowed as we are in a COPY OUT state $t='pg_putline fails while in a COPY OUT state'; eval { $dbh->pg_putline("99\tBogusberry"); }; ok ($@, $t); $t='pg_getline returned a 1'; $data[1]=$data[2]=$data[3]=''; $result = $dbh->pg_getline($data[1], 100); is ($result, 1, $t); $t='pg_getline returned a 1'; $result = $dbh->pg_getline($data[2], 100); is ($result, 1, $t); $t='pg_getline returns empty on final call'; $result = $dbh->pg_getline($data[3], 100); is ($result, '', $t); $t='getline returned all rows successfuly'; $result = \@data; $expected = ["12\tMulberry\n","13\tStrawberry\n","14\tBlueberry\n",'']; is_deeply ($result, $expected, $t); ## Make sure we can issue normal commands again $dbh->do(q{SELECT 'dbdpg_copytest'}); ## Make sure we are out of the COPY OUT state and pg_getline no longer works $t='pg_getline fails when issued after pg_endcopy called'; eval { $data[5]=''; $dbh->pg_getline($data[5], 100); }; ok ($@, $t); ## pg_endcopy should fail because we are no longer in a COPY state $t='pg_endcopy fails when called twice after COPY OUT'; eval { $dbh->pg_endcopy; }; ok ($@, $t); ## ## Test the new COPY methods ## $dbh->do("DELETE FROM $table"); $t='pg_putcopydata fails if not after a COPY FROM statement'; eval { $dbh->pg_putcopydata("pizza\tpie"); }; like ($@, qr{COPY FROM command}, $t); $t='pg_getcopydata fails if not after a COPY TO statement'; eval { $dbh->pg_getcopydata($data[0]); }; like ($@, qr{COPY TO command}, $t); $t='pg_getcopydata_async fails if not after a COPY TO statement'; eval { $dbh->pg_getcopydata_async($data[0]); }; like ($@, qr{COPY TO command}, $t); $t='pg_putcopyend warns but does not die if not after a COPY statement'; eval { require Test::Warn; }; if ($@) { pass ('Skipping Test::Warn test'); } else { Test::Warn::warning_like (sub { $dbh->pg_putcopyend(); }, qr/until a COPY/, $t); } $t='pg_getcopydata does not work if we are using COPY .. TO'; $dbh->rollback(); $dbh->do("COPY $table FROM STDIN"); eval { $dbh->pg_getcopydata($data[0]); }; like ($@, qr{COPY TO command}, $t); $t='pg_putcopydata does not work if we are using COPY .. FROM'; $dbh->rollback(); $dbh->do("COPY $table TO STDOUT"); eval { $dbh->pg_putcopydata("pizza\tpie"); }; like ($@, qr{COPY FROM command}, $t); $t='pg_putcopydata works and returns a 1 on success'; $dbh->rollback(); $dbh->do("COPY $table FROM STDIN"); $result = $dbh->pg_putcopydata("15\tBlueberry"); is ($result, 1, $t); $t='pg_putcopydata works on second call'; $dbh->rollback(); $dbh->do("COPY $table FROM STDIN"); $result = $dbh->pg_putcopydata("16\tMoreBlueberries"); is ($result, 1, $t); $t='pg_putcopydata fails with invalid data'; $dbh->rollback(); $dbh->do("COPY $table FROM STDIN"); eval { $dbh->pg_putcopydata(); }; ok ($@, $t); $t='Calling pg_getcopydata gives an error when in the middle of COPY .. TO'; eval { $dbh->pg_getcopydata($data[0]); }; like ($@, qr{COPY TO command}, $t); $t='Calling do() gives an error when in the middle of COPY .. FROM'; eval { $dbh->do('SELECT 123'); }; like ($@, qr{call pg_putcopyend}, $t); $t='pg_putcopydata works after a rude non-COPY attempt'; eval { $result = $dbh->pg_putcopydata("17\tMoreBlueberries"); }; is ($@, q{}, $t); is ($result, 1, $t); $t='pg_putcopyend works and returns a 1'; eval { $result = $dbh->pg_putcopyend(); }; is ($@, q{}, $t); is ($result, 1, $t); $t='pg_putcopydata fails after pg_putcopyend is called'; $dbh->commit(); eval { $result = $dbh->pg_putcopydata('root'); }; like ($@, qr{COPY FROM command}, $t); $t='Normal queries work after pg_putcopyend is called'; eval { $dbh->do('SELECT 123'); }; is ($@, q{}, $t); $t='Data from pg_putcopydata was entered correctly'; $result = $dbh->selectall_arrayref("SELECT id2,val2 FROM $table ORDER BY id2"); $expected = [['12','Mulberry'],['13','Strawberry'],[14,'Blueberry'],[17,'MoreBlueberries']]; is_deeply ($result, $expected, $t); $t='pg_getcopydata fails when argument is not a variable'; $dbh->do("COPY $table TO STDOUT"); eval { $dbh->pg_getcopydata('wrongo'); }; like ($@, qr{read-only}, $t); $t='pg_getcopydata works and returns the length of the string'; $data[0] = 'old'; eval { $dbh->pg_getcopydata($data[0]); }; is ($@, q{}, $t); is ($data[0], "13\tStrawberry\n", $t); $t='pg_getcopydata works when argument is a reference'; eval { $dbh->pg_getcopydata(\$data[0]); }; is ($@, q{}, $t); is ($data[0], "14\tBlueberry\n", $t); $t='Calling do() gives an error when in the middle of COPY .. TO'; eval { $dbh->do('SELECT 234'); }; like ($@, qr{pg_getcopydata}, $t); $t='Calling pg_putcopydata gives an errors when in the middle of COPY .. FROM'; eval { $dbh->pg_putcopydata('pie'); }; like ($@, qr{COPY FROM command}, $t); $t='pg_getcopydata returns 0 when no more data'; $dbh->pg_getcopydata(\$data[0]); eval { $result = $dbh->pg_getcopydata(\$data[0]); }; is ($@, q{}, $t); is ($data[0], '', $t); is ($result, -1, $t); $t='Normal queries work after pg_getcopydata runs out'; eval { $dbh->do('SELECT 234'); }; is ($@, q{}, $t); $t='Async queries work after COPY OUT'; $dbh->do('CREATE TEMP TABLE foobar AS SELECT 123::INTEGER AS x'); $dbh->do('COPY foobar TO STDOUT'); 1 while ($dbh->pg_getcopydata($buffer) >= 0); eval { $dbh->do('SELECT 111', { pg_async => PG_ASYNC} ); }; is ($@, q{}, $t); $dbh->pg_result(); $t='Async queries work after COPY IN'; $dbh->do('COPY foobar FROM STDIN'); $dbh->pg_putcopydata(456); $dbh->pg_putcopyend(); eval { $dbh->do('SELECT 222', { pg_async => PG_ASYNC} ); }; is ($@, q{}, $t); $dbh->pg_result(); SKIP: { $pgversion < 80200 and skip ('Server version 8.2 or greater needed for test', 1); $t='pg_getcopydata works when pulling from an empty table into an empty var'; $dbh->do(q{COPY (SELECT 1 FROM pg_class LIMIT 0) TO STDOUT}); eval { my $newvar; $dbh->pg_getcopydata($newvar); }; is ($@, q{}, $t); } # # Make sure rollback and commit reset our internal copystate tracking # $t='commit resets COPY state'; $dbh->do("COPY $table TO STDOUT"); $dbh->commit(); eval { $dbh->do(q{SELECT 'dbdpg_copytest'}); }; ok (!$@, $t); $t='rollback resets COPY state'; $dbh->do("COPY $table TO STDOUT"); $dbh->rollback(); eval { $dbh->do(q{SELECT 'dbdpg_copytest'}); }; ok (!$@, $t); # # Keep old-style calls around for backwards compatibility # $t=q{old-style dbh->func('text', 'putline') still works}; $dbh->do("COPY $table FROM STDIN"); $result = $dbh->func("13\tOlive\n", 'putline'); is ($result, 1, $t); $t=q{old-style dbh->func(var, length, 'getline') still works}; $dbh->pg_endcopy; $dbh->do("COPY $table TO STDOUT"); $result = $dbh->func($data[0], 100, 'getline'); is ($result, 1, $t); 1 while ($result = $dbh->func($data[0], 100, 'getline')); $dbh->do("DROP TABLE $table"); $dbh->commit(); cleanup_database($dbh,'test'); $dbh->disconnect;