Merge 'pg_unqualified_schema' into 'trunk'
Peter Rabbitson [Wed, 4 Nov 2009 09:55:51 +0000 (09:55 +0000)]
r7817@Thesaurus (orig r7805):  rbuels | 2009-10-21 02:37:28 +0200
making a branch, here we go again with the pg_unqualified_schema
r7818@Thesaurus (orig r7806):  rbuels | 2009-10-21 02:38:59 +0200
more pg unqualified schema tests, which expose a gap in the coverage
r7819@Thesaurus (orig r7807):  rbuels | 2009-10-21 03:10:38 +0200
gutted Pg storage driver's sequence discovery to just rely on DBD::Pg's last_insert_id.  this needs testing with older versions of DBD::Pg
r7821@Thesaurus (orig r7809):  rbuels | 2009-10-21 04:00:39 +0200
more coverage in Pg sequence-discovery tests.  i think this shows why last_insert_id cannot be used.
r7822@Thesaurus (orig r7810):  rbuels | 2009-10-21 04:07:05 +0200
reverted [7807], and just changed code to use the custom pg_catalog query, which is the only thing that works in the pathological case where DBIC is told a different primary key from the primary key that is set on the table in the DB ([7809] added testing for this)
r7852@Thesaurus (orig r7840):  rbuels | 2009-11-03 18:47:05 +0100
added Changes line mentioning tweak to Pg auto-inc fix
r7854@Thesaurus (orig r7842):  ribasushi | 2009-11-04 10:55:35 +0100
Cleanup exceptions

Changes
lib/DBIx/Class/Storage/DBI/Pg.pm
t/72pg.t

diff --git a/Changes b/Changes
index c0a3163..f76cc0a 100644 (file)
--- a/Changes
+++ b/Changes
@@ -20,6 +20,9 @@ Revision history for DBIx::Class
         - Fixed on_connect_do/call regression when used with a coderef
           connector (RT #50003)
         - A couple of fixes to Ordered to remedy subclassing issues
+        - Fixed another lingering problem with PostgreSQL
+          auto-increment support and its interaction with multiple
+          schemas
 
 0.08112 2009-09-21 10:57:00 (UTC)
         - Remove the recommends from Makefile.PL, DBIx::Class is not
index 92f1a78..d0e8d73 100644 (file)
@@ -26,11 +26,11 @@ sub last_insert_id {
 
   for my $col (@cols) {
     my $seq = ( $source->column_info($col)->{sequence} ||= $self->dbh_do('_dbh_get_autoinc_seq', $source, $col) )
-      or $self->throw_exception( "could not determine sequence for "
-                                 . $source->name
-                                 . ".$col, please consider adding a "
-                                 . "schema-qualified sequence to its column info"
-                               );
+      or $self->throw_exception( sprintf(
+        'could not determine sequence for column %s.%s, please consider adding a schema-qualified sequence to its column info',
+          $source->name,
+          $col,
+      ));
 
     push @values, $self->_dbh_last_insert_id ($self->_dbh, $seq);
   }
@@ -61,26 +61,22 @@ sub _dbh_get_autoinc_seq {
     ( $schema, $table ) = ( $1, $2 );
   }
 
-### XXX This is unsafe in DBD::Pg 2.15.1, it can disconnect for some reason
-###
-  # use DBD::Pg to fetch the column info if it is recent enough to
-  # work. otherwise, use custom SQL
-#  my $seq_expr =  $DBD::Pg::VERSION >= 2.015001
-#      ? eval{ $dbh->column_info(undef,$schema,$table,$col)->fetchrow_hashref->{COLUMN_DEF} }
-#      : $self->_dbh_get_column_default( $dbh, $schema, $table, $col );
-
+  # get the column default using a Postgres-specific pg_catalog query
   my $seq_expr = $self->_dbh_get_column_default( $dbh, $schema, $table, $col );
 
   # if no default value is set on the column, or if we can't parse the
   # default value as a sequence, throw.
-  unless ( defined $seq_expr and $seq_expr =~ /^nextval\(+'([^']+)'::(?:text|regclass)\)/i ){
+  unless ( defined $seq_expr and $seq_expr =~ /^nextval\(+'([^']+)'::(?:text|regclass)\)/i ) {
     $seq_expr = '' unless defined $seq_expr;
     $schema = "$schema." if defined $schema && length $schema;
-    $self->throw_exception( "no sequence found for $schema$table.$col, check table definition, "
-                            . "or explicitly set the 'sequence' for this column in the "
-                            . $source->source_name
-                            . " class"
-                          );
+    $self->throw_exception( sprintf (
+      'no sequence found for %s%s.%s, check the RDBMS table definition or explicitly set the '.
+      "'sequence' for this column in %s",
+        $schema ? "$schema." : '',
+        $table,
+        $col,
+        $source->source_name,
+    ));
   }
 
   return $1;
index d6cb0a9..3fdefb7 100644 (file)
--- a/t/72pg.t
+++ b/t/72pg.t
@@ -472,6 +472,7 @@ BEGIN {
 
 my @eapk_schemas;
 BEGIN{ @eapk_schemas = map "dbic_apk_$_", 0..5 }
+my %seqs; #< hash of schema.table.col => currval of its (DBIC) primary key sequence
 
 sub run_extended_apk_tests {
   my $schema = shift;
@@ -489,10 +490,18 @@ sub run_extended_apk_tests {
         for @eapk_schemas;
 
     $dbh->do("CREATE SEQUENCE $eapk_schemas[5].fooseq");
+    $dbh->do("SELECT setval('$eapk_schemas[5].fooseq',400)");
+    $seqs{"$eapk_schemas[1].apk.id2"} = 400;
+
     $dbh->do("CREATE SEQUENCE $eapk_schemas[4].fooseq");
+    $dbh->do("SELECT setval('$eapk_schemas[4].fooseq',300)");
+    $seqs{"$eapk_schemas[3].apk.id2"} = 300;
+
     $dbh->do("CREATE SEQUENCE $eapk_schemas[3].fooseq");
+    $dbh->do("SELECT setval('$eapk_schemas[3].fooseq',200)");
+    $seqs{"$eapk_schemas[4].apk.id2"} = 200;
 
-    $dbh->do("SET search_path = ".join ',', @eapk_schemas );
+    $dbh->do("SET search_path = ".join ',', reverse @eapk_schemas );
   });
 
   # clear our search_path cache
@@ -519,12 +528,14 @@ sub run_extended_apk_tests {
                qualify_table => 4,
              );
 
+  eapk_poke( $schema );
   eapk_poke( $schema, 0 );
   eapk_poke( $schema, 2 );
   eapk_poke( $schema, 4 );
   eapk_poke( $schema, 1 );
   eapk_poke( $schema, 0 );
   eapk_poke( $schema, 1 );
+  eapk_poke( $schema );
   eapk_poke( $schema, 4 );
   eapk_poke( $schema, 3 );
   eapk_poke( $schema, 1 );
@@ -538,8 +549,6 @@ sub run_extended_apk_tests {
 # do a DBIC create on the apk table in the given schema number (which is an
 # index of @eapk_schemas)
 
-my %seqs; #< sanity-check hash of schema.table.col => currval of its sequence
-
 sub eapk_poke {
   my ($s, $schema_num) = @_;
 
@@ -547,7 +556,7 @@ sub eapk_poke {
       ? $eapk_schemas[$schema_num]
       : '';
 
-  my $schema_name_actual = $schema_name || eapk_get_search_path($s)->[0];
+  my $schema_name_actual = $schema_name || eapk_find_visible_schema($s);
 
   $s->source('ExtAPK')->name($schema_name ? $schema_name.'.apk' : 'apk');
   #< clear sequence name cache
@@ -558,12 +567,13 @@ sub eapk_poke {
   lives_ok {
     my $new;
     for my $inc (1,2,3) {
-      $new = $schema->resultset('ExtAPK')->create({});
+      $new = $schema->resultset('ExtAPK')->create({ id1 => 1});
       my $proper_seqval = ++$seqs{"$schema_name_actual.apk.id2"};
       is( $new->id2, $proper_seqval, "$schema_name_actual.apk.id2 correct inc $inc" )
           or eapk_seq_diag($s,$schema_name);
       $new->discard_changes;
-      for my $id (grep $_ ne 'id2', @eapk_id_columns) {
+      is( $new->id1, 1 );
+      for my $id ('id3','id4') {
         my $proper_seqval = ++$seqs{"$schema_name_actual.apk.$id"};
         is( $new->$id, $proper_seqval, "$schema_name_actual.apk.$id correct inc $inc" )
             or eapk_seq_diag($s,$schema_name);
@@ -577,7 +587,7 @@ sub eapk_poke {
 # class
 sub eapk_seq_diag {
     my $s = shift;
-    my $schema = shift || eapk_get_search_path($s)->[0];
+    my $schema = shift || eapk_find_visible_schema($s);
 
     diag "$schema.apk sequences: ",
         join(', ',
@@ -633,13 +643,13 @@ sub eapk_create {
         local $_[1]->{Warn} = 0;
 
         my $id_def = $a{nextval}
-            ? "integer primary key not null default nextval('$a{nextval}'::regclass)"
-            : 'serial primary key';
+            ? "integer not null default nextval('$a{nextval}'::regclass)"
+            : 'serial';
         $dbh->do(<<EOS);
 CREATE TABLE $table_name (
   id1 serial
   , id2 $id_def
-  , id3 serial
+  , id3 serial primary key
   , id4 serial
 )
 EOS
@@ -667,3 +677,19 @@ sub eapk_drop_all {
 
     });
 }
+
+sub eapk_find_visible_schema {
+    my ($s) = @_;
+
+    my ($schema) =
+        $s->storage->dbh_do(sub {
+            $_[1]->selectrow_array(<<EOS);
+SELECT n.nspname
+FROM pg_catalog.pg_namespace n
+JOIN pg_catalog.pg_class c ON c.relnamespace = n.oid
+WHERE c.relname = 'apk'
+  AND pg_catalog.pg_table_is_visible(c.oid)
+EOS
+        });
+    return $schema;
+}