From 8bfb06058559b1556c3581d579645a8f89b12837 Mon Sep 17 00:00:00 2001 From: Peter Mottram Date: Mon, 1 Sep 2014 10:25:08 +0200 Subject: [PATCH] initial batch_alter_table for ::Producer::PostgreSQL --- lib/SQL/Translator/Producer/PostgreSQL.pm | 66 +++++++++++++++++++++ t/30sqlt-new-diff-pgsql.t | 24 ++++---- t/postgresql-rename-table-and-field.t | 71 +++++++++++++++++++++++ 3 files changed, 149 insertions(+), 12 deletions(-) create mode 100644 t/postgresql-rename-table-and-field.t diff --git a/lib/SQL/Translator/Producer/PostgreSQL.pm b/lib/SQL/Translator/Producer/PostgreSQL.pm index 5f898acec..1843883ee 100644 --- a/lib/SQL/Translator/Producer/PostgreSQL.pm +++ b/lib/SQL/Translator/Producer/PostgreSQL.pm @@ -1019,6 +1019,72 @@ sub drop_table { return $out; } +sub batch_alter_table { + my ( $table, $diff_hash, $options ) = @_; + my $qt = $options->{quote_table_names} || ''; + $generator->quote_chars([$qt]); + + # as long as we're not renaming the table we don't need to be here + if ( @{$diff_hash->{rename_table}} == 0 ) { + return map { + if (@{ $diff_hash->{$_} || [] }) { + my $meth = __PACKAGE__->can($_) or die __PACKAGE__ . " cant $_"; + map { $meth->( (ref $_ eq 'ARRAY' ? @$_ : $_), $options ) } + @{ $diff_hash->{$_} } + } + else { () } + } qw/alter_drop_constraint + alter_drop_index + drop_field + add_field + alter_field + rename_field + alter_create_index + alter_create_constraint + alter_table/; + } + + # first we need to perform drops which are on old table + my @sql = map { + if (@{ $diff_hash->{$_} || [] }) { + my $meth = __PACKAGE__->can($_) or die __PACKAGE__ . " cant $_"; + map { $meth->( (ref $_ eq 'ARRAY' ? @$_ : $_), $options ) } + @{ $diff_hash->{$_} } + } + else { () } + } qw/alter_drop_constraint + alter_drop_index + drop_field/; + + # next comes the rename_table + my $old_table = $diff_hash->{rename_table}[0][0]; + push @sql, rename_table( $old_table, $table, $options ); + + # for alter_field (and so also rename_field) we need to make sure old + # field has table name set to new table otherwise calling alter_field dies + $diff_hash->{alter_field} = + [map { $_->[0]->table($table) && $_ } @{$diff_hash->{alter_field}}]; + $diff_hash->{rename_field} = + [map { $_->[0]->table($table) && $_ } @{$diff_hash->{rename_field}}]; + + # now add everything else + push @sql, map { + if (@{ $diff_hash->{$_} || [] }) { + my $meth = __PACKAGE__->can($_) or die __PACKAGE__ . " cant $_"; + map { $meth->( (ref $_ eq 'ARRAY' ? @$_ : $_), $options ) } + @{ $diff_hash->{$_} } + } + else { () } + } qw/add_field + alter_field + rename_field + alter_create_index + alter_create_constraint + alter_table/; + + return @sql; +} + 1; # ------------------------------------------------------------------- diff --git a/t/30sqlt-new-diff-pgsql.t b/t/30sqlt-new-diff-pgsql.t index ba61ee5b1..716af4b7c 100644 --- a/t/30sqlt-new-diff-pgsql.t +++ b/t/30sqlt-new-diff-pgsql.t @@ -55,18 +55,21 @@ CREATE TABLE added ( id bigint ); -ALTER TABLE old_name RENAME TO new_name; - ALTER TABLE employee DROP CONSTRAINT FK5302D47D93FE702E; -ALTER TABLE person DROP CONSTRAINT UC_age_name; +ALTER TABLE employee DROP COLUMN job_title; -DROP INDEX u_name; +ALTER TABLE employee ADD CONSTRAINT FK5302D47D93FE702E_diff FOREIGN KEY (employee_id) + REFERENCES person (person_id) DEFERRABLE; -ALTER TABLE employee DROP COLUMN job_title; +ALTER TABLE old_name RENAME TO new_name; ALTER TABLE new_name ADD COLUMN new_field integer; +ALTER TABLE person DROP CONSTRAINT UC_age_name; + +DROP INDEX u_name; + ALTER TABLE person ADD COLUMN is_rock_star smallint DEFAULT 1; ALTER TABLE person ALTER COLUMN person_id TYPE serial; @@ -85,9 +88,6 @@ ALTER TABLE person RENAME COLUMN description TO physical_description; ALTER TABLE person ADD CONSTRAINT unique_name UNIQUE (name); -ALTER TABLE employee ADD CONSTRAINT FK5302D47D93FE702E_diff FOREIGN KEY (employee_id) - REFERENCES person (person_id) DEFERRABLE; - ALTER TABLE person ADD CONSTRAINT UC_person_id UNIQUE (person_id); ALTER TABLE person ADD CONSTRAINT UC_age_name UNIQUE (age, name); @@ -118,14 +118,14 @@ CREATE TABLE added ( id bigint ); -ALTER TABLE old_name RENAME TO new_name; - -ALTER TABLE person DROP CONSTRAINT UC_age_name; - ALTER TABLE employee DROP COLUMN job_title; +ALTER TABLE old_name RENAME TO new_name; + ALTER TABLE new_name ADD COLUMN new_field integer; +ALTER TABLE person DROP CONSTRAINT UC_age_name; + ALTER TABLE person ADD COLUMN is_rock_star smallint DEFAULT 1; ALTER TABLE person ALTER COLUMN person_id TYPE serial; diff --git a/t/postgresql-rename-table-and-field.t b/t/postgresql-rename-table-and-field.t new file mode 100644 index 000000000..f83ba35d1 --- /dev/null +++ b/t/postgresql-rename-table-and-field.t @@ -0,0 +1,71 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Test::More; +use Test::SQL::Translator; +use SQL::Translator; +use SQL::Translator::Diff; + +maybe_plan(10, 'DBD::Pg', 'Test::PostgreSQL'); + +my ( $pgsql, $dbh , $ddl, $ret ); + +no warnings "once"; +$pgsql = Test::PostgreSQL->new() or die $Test::PostgreSQL::errstr; +$dbh = DBI->connect($pgsql->dsn,'','', { RaiseError => 1 }) or die $DBI::errstr; +use warnings "once"; + +my $source_ddl = <do($source_ddl), "create table" ); + +ok( $ret = $dbh->do(q| INSERT INTO foo (bar) VALUES ('buzz') |), "insert data" ); + +cmp_ok( $ret, '==', 1, "one row inserted" ); + +my $target_ddl = <new( + no_comments => 1, + parser => 'SQL::Translator::Parser::PostgreSQL', +)->translate(\$source_ddl); + +my $target_sqlt = SQL::Translator->new( + no_comments => 1, + parser => 'SQL::Translator::Parser::PostgreSQL', +)->translate(\$target_ddl); + +my $table = $target_sqlt->get_table('fluff'); +$table->extra( renamed_from => 'foo' ); +my $field = $table->get_field('biff'); +$field->extra( renamed_from => 'bar' ); + +my @diff = SQL::Translator::Diff->new({ + output_db => 'PostgreSQL', + source_schema => $source_sqlt, + target_schema => $target_sqlt, +})->compute_differences->produce_diff_sql; + +foreach my $line (@diff) { + $line =~ s/\n//g; + next if $line =~ /^--/; + ok( $dbh->do($line), "$line" ); +} + +ok ( $ret = $dbh->selectall_arrayref(q(SELECT biff FROM fluff), { Slice => {} }), "query DB for data" ); + +cmp_ok( scalar(@$ret), '==', 1, "Got 1 row"); + +cmp_ok( $ret->[0]->{biff}, 'eq', 'buzz', "col biff has value buzz" );