Support for Firebird as a target database.
authorGreg Sabino Mullane <greg@endpoint.com>
Wed, 26 Aug 2015 15:10:48 +0000 (11:10 -0400)
committerGreg Sabino Mullane <greg@endpoint.com>
Wed, 26 Aug 2015 15:10:48 +0000 (11:10 -0400)
Still a little rough around the edges, but should mostly work for simple cases.

Bucardo.pm
MANIFEST
bucardo
bucardo.schema
t/20-firebird.t [new file with mode: 0644]
t/20-mysql.t
t/BucardoTesting.pm

index 4fe64e495b407beb3a4abfb842b07ec4e9d4ec1a..f368076a7f9c2f52330512b6c9200c0b42bff950 100644 (file)
@@ -2465,7 +2465,7 @@ sub start_kid {
     ## Also setup common attributes
     my (@dbs, @dbs_source, @dbs_target, @dbs_delta, @dbs_fullcopy,
         @dbs_connectable, @dbs_dbi, @dbs_write, @dbs_non_fullcopy,
-        @dbs_postgres, @dbs_drizzle, @dbs_mongo, @dbs_mysql, @dbs_oracle,
+        @dbs_postgres, @dbs_drizzle, @dbs_firebird, @dbs_mongo, @dbs_mysql, @dbs_oracle,
         @dbs_redis, @dbs_sqlite);
 
     ## Used to weed out all but one source if in onetimecopy mode
@@ -2572,6 +2572,16 @@ sub start_kid {
             $d->{has_mysql_timestamp_issue} = 1;
         }
 
+        ## Firebird
+        if ('firebird' eq $d->{dbtype}) {
+            push @dbs_firebird => $dbname;
+            $d->{does_sql}        = 1;
+            $d->{does_truncate}   = 1;
+            $d->{does_savepoints} = 1;
+            $d->{does_limit}      = 1;
+            $d->{has_mysql_timestamp_issue} = 1;
+        }
+
         ## Oracle
         if ('oracle' eq $d->{dbtype}) {
             push @dbs_oracle => $dbname;
@@ -2639,6 +2649,7 @@ sub start_kid {
         ## Databases with Perl DBI support
         if ($d->{dbtype} eq 'postgres'
                 or $d->{dbtype} eq 'drizzle'
+                or $d->{dbtype} eq 'firebird'
                 or $d->{dbtype} eq 'mariadb'
                 or $d->{dbtype} eq 'mysql'
                 or $d->{dbtype} eq 'oracle'
@@ -5663,6 +5674,9 @@ sub connect_database {
 
             return $backend, $dbh;
         }
+        elsif ('firebird' eq $dbtype) {
+            $dsn = "dbi:Firebird:db=$dbname";
+        }
         elsif ('mysql' eq $dbtype or 'mariadb' eq $dbtype) {
             $dsn = "dbi:mysql:database=$dbname";
         }
@@ -9217,6 +9231,12 @@ sub delete_rows {
         ## The actual target table name: may differ from the source!
         my $tname = $newname->{$t->{name}};
 
+        if ('firebird' eq $type) {
+            $goat->{pklist} =~ s/\"//g; ## not ideal: fix someday
+            $goat->{pklist} = uc $goat->{pklist};
+                       $tname = qq{"$tname"} if $tname !~ /"/;
+        }
+
         ## Internal counters to help us break queries into chunks if needed
         my ($round, $roundtotal) = (0,0);
 
@@ -9385,6 +9405,12 @@ sub delete_rows {
             ## The actual target name
             my $tname = $newname->{$t->{name}};
 
+                       $self->glog("Deleting from target $tname (type=$type)", LOG_DEBUG);
+
+                       if ('firebird' eq $type) {
+                               $tname = qq{"$tname"} if $tname !~ /"/;
+                       }
+
             if ('mongo' eq $type) {
 
                 ## Grab the collection name and store it
@@ -9499,7 +9525,7 @@ sub delete_rows {
                 $self->glog("Mongo objects removed from $tname: $t->{deleted_rows}", LOG_VERBOSE);
             }
             elsif ('mysql' eq $type or 'drizzle' eq $type or 'mariadb' eq $type
-                       or 'oracle' eq $type or 'sqlite' eq $type) {
+                       or 'oracle' eq $type or 'sqlite' eq $type or 'firebird' eq $type) {
                 my $tdbh = $t->{dbh};
                 for (@{ $SQL{IN}{$tname} }) {
                     $t->{deleted_rows} += $tdbh->do($_);
@@ -9713,6 +9739,14 @@ sub push_rows {
                 $tgtcmd =~ s/,$/)/o;
                 $target->{sth} = $target->{dbh}->prepare($tgtcmd);
             }
+            elsif ('firebird' eq $type) {
+                               $columnlist =~ s/\"//g;
+                               $target_tablename = qq{"$target_tablename"} if $target_tablename !~ /"/;
+                my $tgtcmd = "INSERT INTO $target_tablename$columnlist VALUES (";
+                $tgtcmd .= '?,' x @$cols;
+                $tgtcmd =~ s/,$/)/o;
+                $target->{sth} = $target->{dbh}->prepare($tgtcmd);
+            }
             elsif ('oracle' eq $type) {
                 my $tgtcmd = "INSERT INTO $target_tablename$columnlist VALUES (";
                 $tgtcmd .= '?,' x @$cols;
@@ -9836,15 +9870,16 @@ sub push_rows {
                                 }
                             }
                             elsif ($Table->{columnhash}{$key}{ftype} =~ /real|double|numeric/o) {
-                                $object->{$key} = strtod($object->{$key}); 
+                                $object->{$key} = strtod($object->{$key});
                             }
                         }
                         $self->{collection}->insert($object, { safe => 1 });
                     }
-                    ## For MySQL, MariaDB, Drizzle, Oracle, and SQLite, do some basic INSERTs
+                    ## For MySQL, MariaDB, Firebird, Drizzle, Oracle, and SQLite, do some basic INSERTs
                     elsif ('mysql' eq $type
                             or 'mariadb' eq $type
                             or 'drizzle' eq $type
+                            or 'firebird' eq $type
                             or 'oracle' eq $type
                             or 'sqlite' eq $type) {
                         chomp $buffer;
@@ -9974,7 +10009,7 @@ sub push_rows {
                 $self->glog(qq{Rows copied to Redis $target->{name}.$tname:<pkeyvalue>: $total_source_rows}, LOG_VERBOSE);
             }
             else {
-                ## Nothing to be done for mongo, mysql, mariadb, sqlite, oracle
+                ## Nothing to be done for mongo, mysql, mariadb, sqlite, oracle, firebird
             }
         }
 
index 5c07c13ceabc3c11a0d67111061b4089fc6a3e56..6b24a4508e3b3a4b8ce36d49b03520643a7c08b2 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -36,6 +36,7 @@ t/02-bctl-table.t
 t/10-fullcopy.t
 t/10-object-names.t
 t/20-drizzle.t
+t/20-firebird.t
 t/10-makedelta.t
 t/20-mariadb.t
 t/20-mongo.t
diff --git a/bucardo b/bucardo
index d53b3116e48dc575e9171193390fc82440cd3a50..3de8c43471df00c7dd2bc32cfeda7ce720c91788 100755 (executable)
--- a/bucardo
+++ b/bucardo
@@ -1863,6 +1863,7 @@ sub add_database {
         ## This also makes sure we have a valid type
         my %dbtypeinfo = (
             drizzle  => ['DBD::drizzle',  'Drizzle database'],
+            firebird => ['DBD::Firebird', 'Firebird database'],
             mongo    => ['MongoDB',       'MongoDB'],
             mysql    => ['DBD::mysql',    'MySQL database'],
             mariadb  => ['DBD::mysql',    'MariaDB database'],
@@ -2451,6 +2452,11 @@ sub list_databases {
                 $showhost,
                 $showport;
         }
+        if ($dbtype eq 'firebird') {
+            printf 'Conn: isql-fb -u %s %s',
+                $info->{dbuser},
+                $info->{dbname};
+        }
         if ($dbtype eq 'oracle') {
             printf 'Conn: sqlplus %s%s',
                 $info->{dbuser},
@@ -9780,6 +9786,7 @@ sub standardize_rdbms_name {
     $name =~ s/postgres.*/postgres/io;
     $name =~ s/pg.*/postgres/io;
     $name =~ s/driz?zle.*/drizzle/io;
+    $name =~ s/firebird/firebird/io;
     $name =~ s/mongo.*/mongo/io;
     $name =~ s/mysql.*/mysql/io;
     $name =~ s/maria.*/mariadb/io;
index 5b12bc95708c42b7c71f976444ef4b4d60466e37..6e7567ac6d54f2580d291b013d82dee8c6c825b7 100644 (file)
@@ -678,6 +678,7 @@ AS $bc$
 ## Given the name of a db, return the type, plus type-specific connection information
 ## Postgres: a connection string, username, password, and attribs
 ## Drizzle: a connection string, username, and password
+## Firebird: a connection string, username, and password
 ## Mongo: "foo: bar" style connection information, one per line
 ## MariaDB: a connection string, username, and password
 ## MySQL: a connection string, username, and password
@@ -793,6 +794,21 @@ if ($dbtype eq 'mysql' or $dbtype eq 'mariadb') {
 
 } ## end mysql/mariadb
 
+if ($dbtype eq 'firebird') {
+
+    length $db{name} or elog(ERROR, qq{Database name is mandatory\n});
+    length $db{user} or elog(ERROR, qq{Database username is mandatory\n});
+
+    my $connstring = "dbi:Firebird:db=$db{name}";
+    $db{host} ||= ''; $db{port} ||= ''; $db{pass} ||= '';
+    length $db{host} and $connstring .= ";host=$db{host}";
+    length $db{port} and $connstring .= ";port=$db{port}";
+    length $db{conn} and $connstring .= ";$db{conn}";
+
+    return "$dbtype\n$connstring\n$db{user}\n$db{pass}";
+
+} ## end firebird
+
 if ($dbtype eq 'oracle') {
 
     ## We should loosen this up somewhere
@@ -1302,7 +1318,7 @@ for my $dbname (sort { ($db{$b}{role} eq 'source') <=> ($db{$a}{role} eq 'source
     next if $db{$dbname}{dbtype} =~ /flat/;
 
     ## Skip if this is a non-supported database
-    next if $db{$dbname}{dbtype} =~ /drizzle|mariadb|mongo|mysql|oracle|redis|sqlite/;
+    next if $db{$dbname}{dbtype} =~ /drizzle|mariadb|mongo|mysql|oracle|redis|sqlite|firebird/;
 
     ## Figure out how to connect to this database
     my $rv = spi_exec_query("SELECT bucardo.db_getconn('$dbname') AS conn");
diff --git a/t/20-firebird.t b/t/20-firebird.t
new file mode 100644 (file)
index 0000000..83f8099
--- /dev/null
@@ -0,0 +1,380 @@
+#!/usr/bin/env perl
+# -*-mode:cperl; indent-tabs-mode: nil-*-
+
+## Test using Firebird as a database target
+
+use 5.008003;
+use strict;
+use warnings;
+use Data::Dumper;
+use lib 't','.';
+use DBD::Pg;
+use Test::More;
+use MIME::Base64;
+
+use vars qw/ $bct $dbhX $dbhA $dbhB $dbhC $dbhD $res $command $t %pkey $SQL %sth %sql/;
+
+## Must have the DBD::firebird module
+my $evalok = 0;
+eval {
+    require DBD::Firebird;
+    $evalok = 1;
+};
+if (!$evalok) {
+    plan (skip_all =>  'Cannot test Firebird unless the Perl module DBD::Firebird is installed');
+}
+
+## Firebird must be up and running
+$evalok = 0;
+my $dbh;
+my $dbuser = 'testuser';
+my $dbpass = 'foo';
+my $dbname = '/tmp/test';
+eval {
+    $dbh = DBI->connect("dbi:Firebird:database=$dbname", $dbuser, $dbpass,
+                         {AutoCommit=>1, PrintError=>0, RaiseError=>1});
+    $evalok = 1;
+};
+if (!$evalok) {
+    plan (skip_all =>  "Cannot test Firebird as we cannot connect to a running Firebird database: $@");
+}
+
+use BucardoTesting;
+
+## For now, remove a few test tables
+for my $x (2,5,6,8,9,10) {
+    delete $tabletypefirebird{"bucardo_test$x"};
+}
+
+my $numtabletypes = keys %tabletypefirebird;
+plan tests => 151;
+
+## Drop the test database if it exists
+$dbname = q{/tmp/bucardo_test};
+
+eval {
+    $dbh->do("DROP DATABASE $dbname");
+};
+## Create the test database
+#$dbh->do("CREATE DATABASE $dbname");
+
+## Reconnect to the new database
+$dbh = DBI->connect("dbi:Firebird:database=$dbname", $dbuser, $dbpass,
+                    {AutoCommit=>1, PrintError=>0, RaiseError=>1});
+
+## Create one table for each table type
+for my $table (sort keys %tabletypefirebird) {
+
+    my $pkeyname = $table =~ /test5/ ? q{"id space"} : 'id';
+    my $pkindex = $table =~ /test2/ ? '' : 'PRIMARY KEY';
+    $SQL = qq{
+            CREATE TABLE "$table" (
+                $pkeyname    $tabletypefirebird{$table} NOT NULL $pkindex};
+    $SQL .= $table =~ /X/ ? "\n)" : qq{,
+                data1 VARCHAR(100)        NOT NULL   ,
+                inty  SMALLINT               ,
+                booly SMALLINT,
+                bite1 VARCHAR(100)         ,
+                bite2 VARCHAR(100)         ,
+                email VARCHAR(100)            UNIQUE
+            )
+            };
+
+    eval { $dbh->do(qq{DROP TABLE "$table"});};
+    eval { $dbh->do($SQL);};
+
+    diag "Created $table";
+    if ($table =~ /test2/) {
+        $dbh->do(qq{ALTER TABLE "$table" ADD CONSTRAINT multipk PRIMARY KEY ($pkeyname,data1)});
+    }
+
+}
+
+$bct = BucardoTesting->new() or BAIL_OUT "Creation of BucardoTesting object failed\n";
+$location = 'firebird';
+
+pass("*** Beginning Firebird tests");
+
+END {
+    $bct and $bct->stop_bucardo($dbhX);
+    $dbhX and  $dbhX->disconnect();
+    $dbhA and $dbhA->disconnect();
+    $dbhB and $dbhB->disconnect();
+    $dbhC and $dbhC->disconnect();
+    $dbhD and $dbhD->disconnect();
+}
+
+## Get Postgres database A and B and C created
+$dbhA = $bct->repopulate_cluster('A');
+$dbhB = $bct->repopulate_cluster('B');
+$dbhC = $bct->repopulate_cluster('C');
+
+## Create a bucardo database, and install Bucardo into it
+$dbhX = $bct->setup_bucardo('A');
+
+## Tell Bucardo about these databases
+
+## Three Postgres databases will be source, source, and target
+for my $name (qw/ A B C /) {
+    $t = "Adding database from cluster $name works";
+    my ($dbuser,$dbport,$dbhost) = $bct->add_db_args($name);
+    $command = "bucardo add db $name dbname=bucardo_test user=$dbuser port=$dbport host=$dbhost";
+    $res = $bct->ctl($command);
+    like ($res, qr/Added database "$name"/, $t);
+}
+
+$t = 'Adding firebird database Q works';
+$command =
+"bucardo add db Q dbname=$dbname type=firebird dbuser=$dbuser password=$dbpass";
+$res = $bct->ctl($command);
+like ($res, qr/Added database "Q"/, $t);
+
+## Teach Bucardo about all pushable tables, adding them to a new relgroup named "therd"
+$t = q{Adding all tables on the master works};
+$command =
+"bucardo add tables all db=A relgroup=therd pkonly";
+$res = $bct->ctl($command);
+like ($res, qr/Creating relgroup: therd.*New tables added: \d/s, $t);
+
+## Add all sequences, and add them to the newly created relgroup
+$t = q{Adding all sequences on the master works};
+$command =
+"bucardo add sequences all db=A relgroup=therd";
+$res = $bct->ctl($command);
+like ($res, qr/New sequences added: \d/, $t);
+
+## Create a new dbgroup
+$t = q{Created a new dbgroup};
+$command =
+"bucardo add dbgroup qx A:source B:source C Q";
+$res = $bct->ctl($command);
+like ($res, qr/Created dbgroup "qx"/, $t);
+
+## Create a new sync
+$t = q{Created a new sync};
+$command =
+"bucardo add sync firebird relgroup=therd dbs=qx autokick=false";
+$res = $bct->ctl($command);
+like ($res, qr/Added sync "firebird"/, $t);
+
+## Create a second sync, solely for multi-sync interaction issues
+$bct->ctl('bucardo add dbgroup t1 A:source B C');
+$bct->ctl('bucardo add sync tsync1 relgroup=therd dbs=t1 autokick=false status=inactive');
+
+## Start up Bucardo with these new syncs
+$bct->restart_bucardo($dbhX);
+
+## Boolean values
+my (@boolys) = qw( xxx true false null false true null );
+
+## Get the statement handles ready for each table type
+for my $table (sort keys %tabletypefirebird) {
+
+    $pkey{$table} = $table =~ /test5/ ? q{"id space"} : 'id';
+
+    ## INSERT
+    for my $x (1..6) {
+        $SQL = $table =~ /X/
+            ? qq{INSERT INTO "$table"($pkey{$table}) VALUES (?)}
+                : qq{INSERT INTO "$table"($pkey{$table},data1,inty,booly) VALUES (?,'foo',$x,$boolys[$x])};
+        $sth{insert}{$x}{$table}{A} = $dbhA->prepare($SQL);
+        if ('BYTEA' eq $tabletypefirebird{$table}) {
+            $sth{insert}{$x}{$table}{A}->bind_param(1, undef, {pg_type => PG_BYTEA});
+        }
+    }
+
+    ## SELECT
+    $sql{select}{$table} = qq{SELECT inty, booly FROM "$table" ORDER BY $pkey{$table}};
+    $table =~ /X/ and $sql{select}{$table} =~ s/inty/$pkey{$table}/;
+
+    ## DELETE ALL
+    $SQL = qq{DELETE FROM "$table"};
+    $sth{deleteall}{$table}{A} = $dbhA->prepare($SQL);
+
+    ## DELETE ONE
+    $SQL = qq{DELETE FROM "$table" WHERE inty = ?};
+    $sth{deleteone}{$table}{A} = $dbhA->prepare($SQL);
+
+    ## TRUNCATE
+    $SQL = qq{TRUNCATE TABLE "$table"};
+    $sth{truncate}{$table}{A} = $dbhA->prepare($SQL);
+    ## UPDATE
+    $SQL = qq{UPDATE "$table" SET inty = ?};
+    $sth{update}{$table}{A} = $dbhA->prepare($SQL);
+}
+
+## Add one row per table type to A
+for my $table (keys %tabletypefirebird) {
+    my $type = $tabletypefirebird{$table};
+    my $val1 = $val{$type}{1};
+    $sth{insert}{1}{$table}{A}->execute($val1);
+}
+
+## Before the commit on A, B, C, and Q should be empty
+for my $table (sort keys %tabletypefirebird) {
+    my $type = $tabletypefirebird{$table};
+    $t = qq{B has not received rows for table $table before A commits};
+    $res = [];
+    bc_deeply($res, $dbhB, $sql{select}{$table}, $t);
+    bc_deeply($res, $dbhC, $sql{select}{$table}, $t);
+    bc_deeply($res, $dbh, $sql{select}{$table}, $t);
+}
+
+## Commit, then kick off the sync
+$dbhA->commit();
+$bct->ctl('bucardo kick firebird 0');
+$bct->ctl('bucardo kick firebird 0');
+
+## Check B and C for the new rows
+for my $table (sort keys %tabletypefirebird) {
+
+    my $type = $tabletypefirebird{$table};
+    $t = qq{Row with pkey of type $type gets copied to B};
+
+    $res = [[1,1]];
+    bc_deeply($res, $dbhB, $sql{select}{$table}, $t);
+    bc_deeply($res, $dbhC, $sql{select}{$table}, $t);
+}
+
+## Check that Firebird has the new rows
+for my $table (sort keys %tabletypefirebird) {
+    $t = "Firebird table $table has correct number of rows after insert";
+    $SQL = qq{SELECT * FROM "$table"};
+    my $sth = $dbh->prepare($SQL);
+    my $count = $sth->execute();
+    is ($count, 1, $t);
+
+    $t = "Firebird table $table has correct entries";
+    my $info = $sth->fetchall_arrayref({})->[0];
+    my $type = $tabletypefirebird{$table};
+    my $id = $val{$type}{1};
+    my $pkeyname = $table =~ /test5/ ? 'id space' : 'id';
+
+    ## For now, binary is stored in escaped form, so we skip this one
+    next if $table =~ /test8/;
+
+    ## Datetime has no time zone thingy at the end
+    $tabletypefirebird{$table} =~ /DATETIME/ and $id =~ s/\+.*//;
+
+    is_deeply(
+        $info,
+        {
+            $pkeyname => $id,
+            inty => 1,
+            booly => 1,
+            email => undef,
+            bite1 => undef,
+            bite2 => undef,
+            data1 => 'foo',
+        },
+
+        $t);
+}
+
+## Update each row
+for my $table (keys %tabletypefirebird) {
+    $sth{update}{$table}{A}->execute(42);
+}
+$dbhA->commit();
+$bct->ctl('bucardo kick firebird 0');
+
+for my $table (keys %tabletypefirebird) {
+    $t = "Firebird table $table has correct number of rows after update";
+    $SQL = qq{SELECT * FROM "$table"};
+    my $sth = $dbh->prepare($SQL);
+    my $count = $sth->execute();
+    is ($count, 1, $t);
+
+    $t = "Firebird table $table has updated value";
+    my $info = $sth->fetchall_arrayref({})->[0];
+    is ($info->{inty}, 42, $t);
+}
+
+## Delete each row
+for my $table (keys %tabletypefirebird) {
+    $sth{deleteall}{$table}{A}->execute();
+}
+$dbhA->commit();
+$bct->ctl('bucardo kick firebird 0');
+
+for my $table (keys %tabletypefirebird) {
+    $t = "Firebird table $table has correct number of rows after delete";
+    $SQL = qq{SELECT * FROM "$table"};
+    my $sth = $dbh->prepare($SQL);
+    (my $count = $sth->execute()) =~ s/0E0/0/;
+    $sth->finish();
+    is ($count, 0, $t);
+}
+
+## Insert two rows, then delete one of them
+## Add one row per table type to A
+for my $table (keys %tabletypefirebird) {
+    my $type = $tabletypefirebird{$table};
+    my $val1 = $val{$type}{1};
+    $sth{insert}{1}{$table}{A}->execute($val1);
+    my $val2 = $val{$type}{2};
+    $sth{insert}{2}{$table}{A}->execute($val2);
+}
+$dbhA->commit();
+$bct->ctl('bucardo kick firebird 0');
+
+for my $table (keys %tabletypefirebird) {
+    $t = "Firebird table $table has correct number of rows after double insert";
+    $SQL = qq{SELECT * FROM "$table"};
+    my $sth = $dbh->prepare($SQL);
+    my $count = $sth->execute();
+    $sth->finish();
+    is ($count, 2, $t);
+}
+
+## Delete one of the rows
+for my $table (keys %tabletypefirebird) {
+    $sth{deleteone}{$table}{A}->execute(2); ## inty = 2
+}
+$dbhA->commit();
+$bct->ctl('bucardo kick firebird 0');
+
+for my $table (keys %tabletypefirebird) {
+    $t = "Firebird table $table has correct number of rows after single deletion";
+    $SQL = qq{SELECT * FROM "$table"};
+    my $sth = $dbh->prepare($SQL);
+    my $count = $sth->execute();
+    $sth->finish();
+    is ($count, 1, $t);
+}
+
+## Insert two more rows
+for my $table (keys %tabletypefirebird) {
+    my $type = $tabletypefirebird{$table};
+    my $val3 = $val{$type}{3};
+    $sth{insert}{3}{$table}{A}->execute($val3);
+    my $val4 = $val{$type}{4};
+    $sth{insert}{4}{$table}{A}->execute($val4);
+}
+$dbhA->commit();
+$bct->ctl('bucardo kick firebird 0');
+
+for my $table (keys %tabletypefirebird) {
+    $t = "Firebird table $table has correct number of rows after more inserts";
+    $SQL = qq{SELECT * FROM "$table"};
+    my $sth = $dbh->prepare($SQL);
+    my $count = $sth->execute();
+    is ($count, 3, $t);
+
+    $t = "Firebird table $table has updated values";
+    my $info = $sth->fetchall_arrayref({});
+    $info = [ sort { $a->{inty} <=> $b->{inty} } @$info ];
+    my ($val1, $val3, $val4) = @{$val{$tabletypefirebird{$table}}}{1, 3, 4};
+
+    my $pkeyname = $table =~ /test5/ ? 'id space' : 'id';
+    my(@invar) = ( data1 => 'foo', 'email' => undef, bite1 => undef, bite2 => undef );
+    is_deeply ($info, [{ $pkeyname=>$val1, inty=>1, booly=>1,     @invar },
+                       { $pkeyname=>$val3, inty=>3, booly=>undef, @invar },
+                       { $pkeyname=>$val4, inty=>4, booly=>0,     @invar }], $t) || diag explain $info;
+
+    $sth->finish();
+
+}
+
+
+exit;
index 4ee62cc88d3d8720c2204b0206918a4c30b35866..88aff7b10b25ba800ad3887fe97d7d6d9906b17d 100644 (file)
@@ -29,18 +29,18 @@ $evalok = 0;
 my $dbh;
 my $dbuser = 'root';
 eval {
-    $dbh = DBI->connect('dbi:mysql:database=test', $dbuser, '',
+    $dbh = DBI->connect('dbi:mysql:database=test', $dbuser, 'fred',
                          {AutoCommit=>1, PrintError=>0, RaiseError=>1});
     $evalok = 1;
 };
 if (!$evalok) {
-    plan (skip_all =>  "Cannot test MySQL as we cannot connect to a running MySQL database");
+    plan (skip_all =>  "Cannot test MySQL as we cannot connect to a running MySQL database: $@");
 }
 
 ## Need to ensure this is really MySQL, not MariaDB
 my $ver = $dbh->selectall_arrayref('SELECT version()')->[0][0];
 if ($ver =~ /MariaDB/) {
-    plan (skip_all =>  "Cannot test MySQL: MySQL port is being used by MariaDB");
+#    plan (skip_all =>  "Cannot test MySQL: MySQL port is being used by MariaDB");
 }
 
 use BucardoTesting;
@@ -61,7 +61,7 @@ eval {
 $dbh->do("CREATE DATABASE $dbname");
 
 ## Reconnect to the new database
-$dbh = DBI->connect("dbi:mysql:database=$dbname", $dbuser, '',
+$dbh = DBI->connect("dbi:mysql:database=$dbname", $dbuser, 'fred',
                     {AutoCommit=>1, PrintError=>0, RaiseError=>1});
 
 ## Yes, this must be turned on manually!
index ed81623e0bb2df896b660fc13cd2a46a6e4d4e19..cc43902fce75952d55908f00e1c2bc722309fb5b 100644 (file)
@@ -25,7 +25,7 @@ my $DEBUG = $ENV{BUCARDO_DEBUG} || 0;
 $ENV{BUCARDO_CONFIRM} = 0 if exists $ENV{BUCARDO_CONFIRM};
 
 use base 'Exporter';
-our @EXPORT = qw/%tabletype %tabletypemysql %tabletypemariadb %tabletypeoracle %tabletypesqlite
+our @EXPORT = qw/%tabletype %tabletypemysql %tabletypemariadb %tabletypeoracle %tabletypesqlite %tabletypefirebird
                  %sequences %val
                  compare_tables bc_deeply clear_notices wait_for_notice
                  $location $oldherd_msg $newherd_msg $addtable_msg $deltable_msg $nomatch_msg/;
@@ -120,6 +120,21 @@ our %tabletypemariadb =
      'bucardo space test' => 'INT',
      );
 
+our %tabletypefirebird =
+    (
+     'bucardo_test1'  => 'SMALLINT',
+     'bucardo_test2'  => 'INT',
+     'Bucardo_test3'  => 'BIGINT',
+     'bucardo_test4'  => 'VARCHAR(700)',
+     'bucardo_test5'  => 'DATE',
+     'bucardo_test6'  => 'DATETIME',
+     'bucardo_test7'  => 'NUMERIC(5,1)',
+     'bucardo_test8'  => 'VARBINARY(1000)',
+     'bucardo_test9'  => 'INTEGER UNSIGNED',
+     'bucardo_test10' => 'TIMESTAMP',
+     'bucardo space test' => 'INT',
+     );
+
 our %tabletypeoracle =
     (
      'bucardo_test1'  => 'SMALLINT',