auto_nextval support for Firebird
[dbsrgits/DBIx-Class.git] / t / 750firebird.t
index 1e1f72b..27879a6 100644 (file)
@@ -5,6 +5,7 @@ use Test::More;
 use Test::Exception;
 use lib qw(t/lib);
 use DBICTest;
+use Scope::Guard ();
 
 # tests stolen from 749sybase_asa.t
 
@@ -21,39 +22,62 @@ my @info = (
   [ $dsn2, $user2, $pass2 ],
 );
 
-my @handles_to_clean;
+my $schema;
 
-foreach my $info (@info) {
-  my ($dsn, $user, $pass) = @$info;
+foreach my $conn_idx (0..$#info) {
+  my ($dsn, $user, $pass) = @{ $info[$conn_idx] || [] };
 
   next unless $dsn;
 
-  my $schema = DBICTest::Schema->connect($dsn, $user, $pass);
-
+  $schema = DBICTest::Schema->connect($dsn, $user, $pass, {
+    auto_savepoint  => 1,
+    quote_char      => q["],
+    name_sep        => q[.],
+    on_connect_call => 'use_softcommit',
+  });
   my $dbh = $schema->storage->dbh;
 
-  push @handles_to_clean, $dbh;
-
-  eval { $dbh->do("DROP TABLE artist") };
+  my $sg = Scope::Guard->new(\&cleanup);
 
+  eval { $dbh->do(q[DROP TABLE "artist"]) };
   $dbh->do(<<EOF);
-  CREATE TABLE artist (
-    artistid INT PRIMARY KEY,
-    name VARCHAR(255),
-    charfield CHAR(10),
-    rank INT DEFAULT 13
+  CREATE TABLE "artist" (
+    "artistid" INT PRIMARY KEY,
+    "name" VARCHAR(255),
+    "charfield" CHAR(10),
+    "rank" INT DEFAULT 13
   )
 EOF
-  $dbh->do('CREATE GENERATOR gen_artist_artistid');
+  eval { $dbh->do(q[DROP GENERATOR "gen_artist_artistid"]) };
+  $dbh->do('CREATE GENERATOR "gen_artist_artistid"');
+  eval { $dbh->do('DROP TRIGGER "artist_bi"') };
   $dbh->do(<<EOF);
-  CREATE TRIGGER artist_bi FOR artist
+  CREATE TRIGGER "artist_bi" FOR "artist"
   ACTIVE BEFORE INSERT POSITION 0
   AS
   BEGIN
-   IF (NEW.artistid IS NULL) THEN
-    NEW.artistid = GEN_ID(gen_artist_artistid,1);
+   IF (NEW."artistid" IS NULL) THEN
+    NEW."artistid" = GEN_ID("gen_artist_artistid",1);
   END
 EOF
+  eval { $dbh->do('DROP TABLE "sequence_test"') };
+  $dbh->do(<<EOF);
+  CREATE TABLE "sequence_test" (
+    "pkid1" INT NOT NULL,
+    "pkid2" INT NOT NULL,
+    "nonpkid" INT,
+    "name" VARCHAR(255)
+  )
+EOF
+  $dbh->do('ALTER TABLE "sequence_test" ADD CONSTRAINT "sequence_test_constraint" PRIMARY KEY ("pkid1", "pkid2")');
+  eval { $dbh->do('DROP GENERATOR "pkid1_seq"') };
+  eval { $dbh->do('DROP GENERATOR "pkid2_seq"') };
+  eval { $dbh->do('DROP GENERATOR "nonpkid_seq"') };
+  $dbh->do('CREATE GENERATOR "pkid1_seq"');
+  $dbh->do('CREATE GENERATOR "pkid2_seq"');
+  $dbh->do('SET GENERATOR "pkid2_seq" TO 9');
+  $dbh->do('CREATE GENERATOR "nonpkid_seq"');
+  $dbh->do('SET GENERATOR "nonpkid_seq" TO 19');
 
   my $ars = $schema->resultset('Artist');
   is ( $ars->count, 0, 'No rows at first' );
@@ -62,12 +86,51 @@ EOF
   my $new = $ars->create({ name => 'foo' });
   ok($new->artistid, "Auto-PK worked");
 
+# test auto increment using generators WITHOUT triggers
+  for (1..5) {
+      my $st = $schema->resultset('SequenceTest')->create({ name => 'foo' });
+      is($st->pkid1, $_, "Firebird Auto-PK without trigger: First primary key");
+      is($st->pkid2, $_ + 9, "Firebird Auto-PK without trigger: Second primary key");
+      is($st->nonpkid, $_ + 19, "Firebird Auto-PK without trigger: Non-primary key");
+  }
+  my $st = $schema->resultset('SequenceTest')->create({ name => 'foo', pkid1 => 55 });
+  is($st->pkid1, 55, "Firebird Auto-PK without trigger: First primary key set manually");
+
+# test savepoints
+  eval {
+    $schema->txn_do(sub {
+      eval {
+        $schema->txn_do(sub {
+          $ars->create({ name => 'in_savepoint' });
+          die "rolling back savepoint";
+        });
+      };
+      ok ((not $ars->search({ name => 'in_savepoint' })->first),
+        'savepoint rolled back');
+      $ars->create({ name => 'in_outer_txn' });
+      die "rolling back outer txn";
+    });
+  };
+
+  like $@, qr/rolling back outer txn/,
+    'correct exception for rollback';
+
+  ok ((not $ars->search({ name => 'in_outer_txn' })->first),
+    'outer txn rolled back');
+
 # test explicit key spec
   $new = $ars->create ({ name => 'bar', artistid => 66 });
   is($new->artistid, 66, 'Explicit PK worked');
   $new->discard_changes;
   is($new->artistid, 66, 'Explicit PK assigned');
 
+# row update
+  lives_ok {
+    $new->update({ name => 'baz' })
+  } 'update survived';
+  $new->discard_changes;
+  is $new->name, 'baz', 'row updated';
+
 # test populate
   lives_ok (sub {
     my @pop;
@@ -89,6 +152,17 @@ EOF
 # count what we did so far
   is ($ars->count, 6, 'Simple count works');
 
+# test ResultSet UPDATE
+  lives_and {
+    $ars->search({ name => 'foo' })->update({ rank => 4 });
+
+    is eval { $ars->search({ name => 'foo' })->first->rank }, 4;
+  } 'Can update a column';
+
+  my ($updated) = $schema->resultset('Artist')->search({name => 'foo'});
+  is eval { $updated->rank }, 4, 'and the update made it to the database';
+
+
 # test LIMIT support
   my $lim = $ars->search( {},
     {
@@ -102,10 +176,19 @@ EOF
 
 # test iterator
   $lim->reset;
-  is( $lim->next->artistid, 101, "iterator->next ok" );
-  is( $lim->next->artistid, 102, "iterator->next ok" );
+  is( eval { $lim->next->artistid }, 101, "iterator->next ok" );
+  is( eval { $lim->next->artistid }, 102, "iterator->next ok" );
   is( $lim->next, undef, "next past end of resultset ok" );
 
+# test multiple executing cursors
+  {
+    my $rs1 = $ars->search({}, { order_by => { -asc  => 'artistid' }});
+    my $rs2 = $ars->search({}, { order_by => { -desc => 'artistid' }});
+
+    is $rs1->next->artistid, 1,   'multiple cursors';
+    is $rs2->next->artistid, 102, 'multiple cursors';
+  }
+
 # test empty insert
   {
     local $ars->result_source->column_info('artistid')->{is_auto_increment} = 0;
@@ -115,14 +198,14 @@ EOF
   }
 
 # test blobs (stolen from 73oracle.t)
-  eval { $dbh->do('DROP TABLE bindtype_test') };
+  eval { $dbh->do('DROP TABLE "bindtype_test2"') };
   $dbh->do(q[
-  CREATE TABLE bindtype_test
+  CREATE TABLE "bindtype_test2"
   (
-    id    INT   NOT NULL PRIMARY KEY,
-    bytea INT,
-    blob  BLOB,
-    clob  CLOB
+    "id"     INT PRIMARY KEY,
+    "bytea"  INT,
+    "a_blob" BLOB,
+    "a_clob" BLOB SUB_TYPE TEXT
   )
   ]);
 
@@ -132,10 +215,10 @@ EOF
   my $maxloblen = length $binstr{'large'};
   local $dbh->{'LongReadLen'} = $maxloblen;
 
-  my $rs = $schema->resultset('BindType');
+  my $rs = $schema->resultset('BindType2');
   my $id = 0;
 
-  foreach my $type (qw( blob clob )) {
+  foreach my $type (qw( a_blob a_clob )) {
     foreach my $size (qw( small large )) {
       $id++;
 
@@ -153,8 +236,26 @@ EOF
 done_testing;
 
 # clean up our mess
-END {
-  foreach my $dbh (@handles_to_clean) {
-    eval { $dbh->do("DROP TABLE $_") } for qw/artist bindtype_test/;
+
+sub cleanup {
+  my $dbh;
+  eval {
+    $schema->storage->disconnect; # to avoid object FOO is in use errors
+    $dbh = $schema->storage->dbh;
+  };
+  return unless $dbh;
+
+  eval { $dbh->do('DROP TRIGGER "artist_bi"') };
+  diag $@ if $@;
+
+  foreach my $generator (qw/gen_artist_artistid pkid1_seq pkid2_seq
+                            nonpkid_seq/) {
+    eval { $dbh->do(qq{DROP GENERATOR "$generator"}) };
+    diag $@ if $@;
+  }
+
+  foreach my $table (qw/artist bindtype_test2 sequence_test/) {
+    eval { $dbh->do(qq[DROP TABLE "$table"]) };
+    diag $@ if $@;
   }
 }