Applied patch from RT#49627 topic/pg_inheritance
Aaron Schrab [Tue, 27 Dec 2011 23:27:14 +0000 (18:27 -0500)]
Changes
lib/SQL/Translator/Parser/PostgreSQL.pm
lib/SQL/Translator/Producer/PostgreSQL.pm
t/14postgres-parser.t

diff --git a/Changes b/Changes
index a9d37ea..afc4cd6 100644 (file)
--- a/Changes
+++ b/Changes
@@ -5,6 +5,8 @@
   closes RT#70734, RT#71283, RT#70378
 * Proper quoting support in SQLite
 * Support for triggers in PostgreSQL producer and parser
+* Support for INHERITS statements in PG parser/producer
+  (RT#49627, patch from Vincent Veselosky)
 * Correct Data Type in SQLT::Parser::DBI::PostgreSQL (patch from Andrew Pam)
 * Fix index issue in SQLT::Parser::DBI::PostgreSQL
 * Add column and table comments in SQLT::Parser::DBI::PostgreSQL(patch from Andrew Pam)
index 29f26ab..d301d3a 100644 (file)
@@ -1051,6 +1051,8 @@ sub parse {
         ) or die "Couldn't create table '$table_name': " . $schema->error;
 
         $table->extra(temporary => 1) if $tdata->{'temporary'};
+        $table->extra(inherits => $tdata->{'table_options(s?)'}{inherits}{table_name})
+            if $tdata->{'table_options(s?)'}{inherits};
 
         $table->comments( $tdata->{'comments'} );
 
index c994817..42875f1 100644 (file)
@@ -267,8 +267,8 @@ sub mk_name {
 
 sub is_geometry
 {
-   my $field = shift;
-   return 1 if $field->data_type eq 'geometry';
+    my $field = shift;
+    return 1 if $field->data_type eq 'geometry';
 }
 
 sub is_geography
@@ -357,6 +357,10 @@ sub create_table
     if(exists $table->{extra}{temporary}) {
         $temporary = $table->{extra}{temporary} ? "TEMPORARY " : "";
     }
+    my $inherits = "";
+    if(my $tlist = $table->extra('inherits')) {
+        $inherits = sprintf(' INHERITS (%s)', join(', ',@$tlist));
+    }
 
     my $create_statement;
     $create_statement = join("\n", @comments);
@@ -369,22 +373,22 @@ sub create_table
     }
     $create_statement .= qq[CREATE ${temporary}TABLE $qt$table_name_ur$qt (\n].
                             join( ",\n", map { "  $_" } @field_defs, @constraint_defs ).
-                            "\n)"
+                            "\n)$inherits"
                             ;
     $create_statement .= @index_defs ? ';' : q{};
     $create_statement .= ( $create_statement =~ /;$/ ? "\n" : q{} )
         . join(";\n", @index_defs);
 
-   #
-   # Geometry
-   #
-   if(grep { is_geometry($_) } $table->get_fields){
+    #
+    # Geometry
+    #
+    if(grep { is_geometry($_) } $table->get_fields){
         $create_statement .= ";";
         my @geometry_columns;
         foreach my $col ($table->get_fields) { push(@geometry_columns,$col) if is_geometry($col); }
-      $create_statement .= "\n".join("\n", map{ drop_geometry_column($_) } @geometry_columns) if $options->{add_drop_table};
-      $create_statement .= "\n".join("\n", map{ add_geometry_column($_) } @geometry_columns);
-   }
+        $create_statement .= "\n".join("\n", map{ drop_geometry_column($_) } @geometry_columns) if $options->{add_drop_table};
+        $create_statement .= "\n".join("\n", map{ add_geometry_column($_) } @geometry_columns);
+    }
 
     return $create_statement, \@fks;
 }
@@ -496,20 +500,20 @@ sub create_view {
         #
         $field_def .= ' NOT NULL' unless $field->is_nullable;
 
-      #
-      # Geometry constraints
-      #
-      if(is_geometry($field)){
-         foreach ( create_geometry_constraints($field) ) {
-            my ($cdefs, $fks) = create_constraint($_,
-                                         {
-                                            quote_field_names => $qf,
-                                            quote_table_names => $qt,
-                                            table_name => $table_name,
-                                         });
-            push @$constraint_defs, @$cdefs;
-            push @$fks, @$fks;
-         }
+        #
+        # Geometry constraints
+        #
+        if(is_geometry($field)){
+            foreach ( create_geometry_constraints($field) ) {
+                my ($cdefs, $fks) = create_constraint($_,
+                                                      {
+                                                          quote_field_names => $qf,
+                                                          quote_table_names => $qt,
+                                                          table_name => $table_name,
+                                                      });
+                push @$constraint_defs, @$cdefs;
+                push @$fks, @$fks;
+            }
         }
 
         return $field_def;
@@ -517,30 +521,30 @@ sub create_view {
 }
 
 sub create_geometry_constraints{
-   my $field = shift;
-
-   my @constraints;
-   push @constraints, SQL::Translator::Schema::Constraint->new(
-                     name       => "enforce_dims_".$field->name,
-                     expression => "(ST_NDims($field) = ".$field->{extra}{dimensions}.")",
-                     table       => $field->table,
-                     type       => CHECK_C,
-                  );
-
-   push @constraints, SQL::Translator::Schema::Constraint->new(
-                     name       => "enforce_srid_".$field->name,
-                     expression => "(ST_SRID($field) = ".$field->{extra}{srid}.")",
-                     table       => $field->table,
-                     type       => CHECK_C,
-                  );
-   push @constraints, SQL::Translator::Schema::Constraint->new(
-                     name       => "enforce_geotype_".$field->name,
-                     expression => "(GeometryType($field) = '".$field->{extra}{geometry_type}."'::text OR $field IS NULL)",
-                     table       => $field->table,
-                     type       => CHECK_C,
-                  );
-
-   return @constraints;
+    my $field = shift;
+
+    my @constraints;
+    push @constraints, SQL::Translator::Schema::Constraint->new(
+                            name       => "enforce_dims_".$field->name,
+                            expression => "(ST_NDims($field) = ".$field->{extra}{dimensions}.")",
+                            table        => $field->table,
+                            type       => CHECK_C,
+                        );
+
+    push @constraints, SQL::Translator::Schema::Constraint->new(
+                            name       => "enforce_srid_".$field->name,
+                            expression => "(ST_SRID($field) = ".$field->{extra}{srid}.")",
+                            table        => $field->table,
+                            type       => CHECK_C,
+                        );
+    push @constraints, SQL::Translator::Schema::Constraint->new(
+                            name       => "enforce_geotype_".$field->name,
+                            expression => "(GeometryType($field) = '".$field->{extra}{geometry_type}."'::text OR $field IS NULL)",
+                            table        => $field->table,
+                            type       => CHECK_C,
+                        );
+
+    return @constraints;
 }
 
 sub create_index
@@ -815,10 +819,10 @@ sub alter_field
         if ( defined $new_default &&
              (!defined $old_default || $old_default ne $new_default) );
 
-    # fixes bug where removing the DEFAULT statement of a column
-    # would result in no change
+     # fixes bug where removing the DEFAULT statement of a column
+     # would result in no change
 
-    push @out, sprintf('ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT',
+     push @out, sprintf('ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT',
                        $to_field->table->name,
                        $to_field->name)
         if ( !defined $new_default && defined $old_default );
@@ -855,53 +859,53 @@ sub drop_field
     my $out = sprintf('ALTER TABLE %s DROP COLUMN %s',
                       $qt . $old_field->table->name . $qt,
                       $qf . $old_field->name . $qf);
-        $out .= "\n".drop_geometry_column($old_field) if is_geometry($old_field);
+    $out .= "\n".drop_geometry_column($old_field) if is_geometry($old_field);
     return $out;
 }
 
 sub add_geometry_column{
-   my ($field,$options) = @_;
-
-   my $out = sprintf("INSERT INTO geometry_columns VALUES ('%s','%s','%s','%s','%s','%s','%s')",
-                  '',
-                  $field->table->schema->name,
-                  $options->{table} ? $options->{table} : $field->table->name,
-                  $field->name,
-                  $field->{extra}{dimensions},
-                  $field->{extra}{srid},
-                  $field->{extra}{geometry_type});
+    my ($field,$options) = @_;
+
+    my $out = sprintf("INSERT INTO geometry_columns VALUES ('%s','%s','%s','%s','%s','%s','%s')",
+                        '',
+                        $field->table->schema->name,
+                        $options->{table} ? $options->{table} : $field->table->name,
+                        $field->name,
+                        $field->{extra}{dimensions},
+                        $field->{extra}{srid},
+                        $field->{extra}{geometry_type});
     return $out;
 }
 
 sub drop_geometry_column
 {
-   my $field = shift;
+    my $field = shift;
 
-   my $out = sprintf("DELETE FROM geometry_columns WHERE f_table_schema = '%s' AND f_table_name = '%s' AND f_geometry_column = '%s'",
-                  $field->table->schema->name,
-                  $field->table->name,
-                  $field->name);
+    my $out = sprintf("DELETE FROM geometry_columns WHERE f_table_schema = '%s' AND f_table_name = '%s' AND f_geometry_column = '%s'",
+                        $field->table->schema->name,
+                        $field->table->name,
+                        $field->name);
     return $out;
 }
 
 sub add_geometry_constraints{
-   my $field = shift;
+    my $field = shift;
 
-   my @constraints = create_geometry_constraints($field);
+    my @constraints = create_geometry_constraints($field);
 
-   my $out = join("\n", map { alter_create_constraint($_); } @constraints);
+    my $out = join("\n", map { alter_create_constraint($_); } @constraints);
 
-   return $out;
+    return $out;
 }
 
 sub drop_geometry_constraints{
-   my $field = shift;
+    my $field = shift;
 
-   my @constraints = create_geometry_constraints($field);
+    my @constraints = create_geometry_constraints($field);
 
-   my $out = join("\n", map { alter_drop_constraint($_); } @constraints);
+    my $out = join("\n", map { alter_drop_constraint($_); } @constraints);
 
-   return $out;
+    return $out;
 }
 
 sub alter_table {
@@ -919,9 +923,9 @@ sub rename_table {
     my $qt = $options->{quote_table_names} || '';
     $options->{alter_table_action} = "RENAME TO $qt$new_table$qt";
 
-   my @geometry_changes;
-   push @geometry_changes, map { drop_geometry_column($_); } grep { is_geometry($_) } $old_table->get_fields;
-   push @geometry_changes, map { add_geometry_column($_, { table => $new_table }); } grep { is_geometry($_) } $old_table->get_fields;
+    my @geometry_changes;
+    push @geometry_changes, map { drop_geometry_column($_); } grep { is_geometry($_) } $old_table->get_fields;
+    push @geometry_changes, map { add_geometry_column($_, { table => $new_table }); } grep { is_geometry($_) } $old_table->get_fields;
 
     $options->{geometry_changes} = join ("\n",@geometry_changes) if scalar(@geometry_changes);
 
@@ -957,7 +961,7 @@ sub alter_drop_constraint {
 
     return sprintf(
         'ALTER TABLE %s DROP CONSTRAINT %s',
-        $qt . $c->table->name . $qt,
+                      $qt . $c->table->name . $qt,
         # attention: Postgres  has a very special naming structure
         # for naming foreign keys, it names them uses the name of
         # the table as prefix and fkey as suffix, concatenated by a underscore
index fd60cb0..cbe8e3b 100644 (file)
@@ -8,7 +8,7 @@ use SQL::Translator::Schema::Constants;
 use Test::SQL::Translator qw(maybe_plan);
 
 BEGIN {
-    maybe_plan(134, 'SQL::Translator::Parser::PostgreSQL');
+    maybe_plan(147, 'SQL::Translator::Parser::PostgreSQL');
     SQL::Translator::Parser::PostgreSQL->import('parse');
 }
 
@@ -40,6 +40,12 @@ my $sql = q{
         check (f_int between 1 and 5)
     );
 
+    create table t_test3 (
+        test_field varchar(25)
+    ) inherits (t_test2);
+
+    create table t_test4 () inherits (t_test3);
+
     CREATE TABLE products_1 (
         product_no integer,
         name text,
@@ -108,7 +114,7 @@ my $schema = $t->schema;
 
 isa_ok( $schema, 'SQL::Translator::Schema', 'Schema object' );
 my @tables = $schema->get_tables;
-is( scalar @tables, 5, 'Five tables' );
+is( scalar @tables, 7, 'Seven tables' );
 
 my $t1 = shift @tables;
 is( $t1->name, 't_test1', 'Table t_test1 exists' );
@@ -296,6 +302,30 @@ is( $t2_c2->type, PRIMARY_KEY, "Constraint is a PK" );
 my $t2_c3 = shift @t2_constraints;
 is( $t2_c3->type, CHECK_C, "Constraint is a 'CHECK'" );
 
+# test table inheritance
+my $t3 = shift @tables;
+is( $t3->name, 't_test3', 'Table t_test3 exists' );
+is_deeply( $t3->extra->{inherits}, ['t_test2'], 'Table t_test3 inherits from t_test2' );
+
+my @t3_fields = $t3->get_fields;
+is( scalar @t3_fields, 1, '1 field in t_test3' );
+
+my $t3_f1 = shift @t3_fields;
+is( $t3_f1->name, 'test_field', 'First field is "test_field"' );
+is( $t3_f1->data_type, 'varchar', 'Field is an varchar' );
+is( $t3_f1->is_nullable, 1, 'Field can be null' );
+is( $t3_f1->size, 25, 'Size is "25"' );
+is( $t3_f1->default_value, undef, 'Default value is undefined' );
+is( $t3_f1->is_primary_key, 0, 'Field is not PK' );
+
+my @t3_constraints = $t3->get_constraints;
+is( scalar @t3_constraints, 0, "No constraints on table" );
+
+my $t4 = shift @tables;
+is( $t4->name, 't_test4', 'Table t_test4 exists' );
+is( scalar $t4->get_fields, undef, 'No fields in t_test4' );
+is_deeply( $t4->extra->{inherits}, ['t_test3'], 'Table t_test4 inherits from t_test3' );
+
 # test temporary tables
 is( exists $schema->get_table('products_1')->extra()->{'temporary'}, "", "Table is NOT temporary");
 is( $schema->get_table('products_2')->extra('temporary'), 1,"Table is TEMP");