diff --git a/lib/SQL/Translator/Producer/PostgreSQL.pm b/lib/SQL/Translator/Producer/PostgreSQL.pm index 455f745df..5f7f55104 100644 --- a/lib/SQL/Translator/Producer/PostgreSQL.pm +++ b/lib/SQL/Translator/Producer/PostgreSQL.pm @@ -602,6 +602,11 @@ sub create_constraint push @fks, "$def"; } + # This seemed preferable to doing it in every junction of that if + if ($c->deferrable and $c->type ne FOREIGN_KEY) { + $constraint_defs[-1] .= ' DEFERRABLE'; + } + return \@constraint_defs, \@fks; } diff --git a/lib/SQL/Translator/Schema/Constraint.pm b/lib/SQL/Translator/Schema/Constraint.pm index 7742bf777..9dd7da41f 100644 --- a/lib/SQL/Translator/Schema/Constraint.pm +++ b/lib/SQL/Translator/Schema/Constraint.pm @@ -56,6 +56,7 @@ Object constructor. match_type => 'full', # how to match on_delete => 'cascade', # what to do on deletes on_update => '', # what to do on updates + deferrable => 0, # whether to set DEFERRABLE, if supported by the database ); =cut @@ -78,9 +79,10 @@ around BUILDARGS => sub { =head2 deferrable -Get or set whether the constraint is deferrable. If not defined, -then returns "1." The argument is evaluated by Perl for True or -False, so the following are equivalent: +Get or set whether the constraint is deferrable. The default is based on the +constraint type. Foreign keys are deferrable by default, for backward +compatibility; all other types are not. The argument is evaluated by Perl for +True or False, so the following are equivalent: $deferrable = $field->deferrable(0); $deferrable = $field->deferrable(''); @@ -91,7 +93,10 @@ False, so the following are equivalent: has deferrable => ( is => 'rw', coerce => quote_sub(q{ $_[0] ? 1 : 0 }), - default => quote_sub(q{ 1 }), + lazy => 1, + default => sub { + $_[0]->type eq FOREIGN_KEY + }, ); =head2 expression diff --git a/lib/Test/SQL/Translator.pm b/lib/Test/SQL/Translator.pm index 0181156a1..bbd31544d 100644 --- a/lib/Test/SQL/Translator.pm +++ b/lib/Test/SQL/Translator.pm @@ -49,7 +49,7 @@ my %ATTRIBUTES = ( constraint => { name => '', type => '', - deferrable => 1, + deferrable => 0, expression => '', is_valid => 1, fields => [], @@ -135,6 +135,11 @@ sub default_attribs { $hashref->{ $attr } = $ATTRIBUTES{ $object_type }{ $attr } } + # Special case + if ($object_type eq 'constraint' and $hashref->{type} eq FOREIGN_KEY) { + $hashref->{deferrable} = 1; + } + return $hashref; } diff --git a/t/17sqlfxml-producer.t b/t/17sqlfxml-producer.t index 7989e35d4..f559a0264 100644 --- a/t/17sqlfxml-producer.t +++ b/t/17sqlfxml-producer.t @@ -87,10 +87,10 @@ $ans = < - + - + diff --git a/t/23json.t b/t/23json.t index 7ed847ea3..416a7c608 100644 --- a/t/23json.t +++ b/t/23json.t @@ -24,7 +24,7 @@ my $json = from_json(< { constraints => [ { - deferrable => 1, + deferrable => 0, expression => "", fields => [ "modified" diff --git a/t/47postgres-producer.t b/t/47postgres-producer.t index 9c50db736..08a043bc6 100644 --- a/t/47postgres-producer.t +++ b/t/47postgres-producer.t @@ -22,6 +22,16 @@ BEGIN { use Test::Differences; use SQL::Translator; +# normalised-space-is, i.e. are these the same after normalising whitespace? +sub ns_is { + my ($first, $second) = (shift, shift); + $first =~ s/\s+/ /g; + $second =~ s/\s+/ /g; + + # avoid prototype + &is( $first, $second, @_ ); +} + my $PRODUCER = \&SQL::Translator::Producer::PostgreSQL::create_field; { @@ -146,32 +156,39 @@ for my $name ( 'foo', undef ) { type => 'FOREIGN_KEY', reference_table => $table2, reference_fields => [qw(myfield_2)], + deferrable => 0, ); - my ($fk_constraint_def_ref, $fk_constraint_fk_ref ) = SQL::Translator::Producer::PostgreSQL::create_constraint($fk_constraint); + for my $constraint ($fk_constraint, $fk_constraint_2) { + my ($fk_constraint_def_ref, $fk_constraint_fk_ref ) = SQL::Translator::Producer::PostgreSQL::create_constraint($constraint); - ok(@{$fk_constraint_def_ref} == 0 && @{$fk_constraint_fk_ref} == 1, 'precheck of create_Foreign Key constraint'); + ok(@{$fk_constraint_def_ref} == 0 && @{$fk_constraint_fk_ref} == 1, 'precheck of create_Foreign Key constraint'); - if ( $name ) { - is($fk_constraint_fk_ref->[0], "ALTER TABLE mytable ADD CONSTRAINT $name FOREIGN KEY (myfield) - REFERENCES mytable2 (myfield_2) DEFERRABLE", 'Create Foreign Key Constraint works'); + my $deferrable = $constraint->deferrable ? ' DEFERRABLE' : ''; - # ToDo: may we should check if the constraint name was valid, or if next - # unused_name created has choosen a different one - my $alter_fk_constraint = SQL::Translator::Producer::PostgreSQL::alter_drop_constraint($fk_constraint); - is($alter_fk_constraint, "ALTER TABLE mytable DROP CONSTRAINT $name", 'Alter drop Foreign Key constraint works'); - } - else { - is($fk_constraint_fk_ref->[0], 'ALTER TABLE mytable ADD FOREIGN KEY (myfield) - REFERENCES mytable2 (myfield_2) DEFERRABLE', 'Create un-named Foreign Key Constraint works'); + if ( $name ) { - my $alter_fk_constraint = SQL::Translator::Producer::PostgreSQL::alter_drop_constraint($fk_constraint); - is($alter_fk_constraint, 'ALTER TABLE mytable DROP CONSTRAINT mytable_myfield_fkey', 'Alter drop un-named Foreign Key constraint works'); + ns_is($fk_constraint_fk_ref->[0], "ALTER TABLE mytable ADD CONSTRAINT $name FOREIGN KEY (myfield) + REFERENCES mytable2 (myfield_2)$deferrable", 'Create Foreign Key Constraint works'); + + # ToDo: may we should check if the constraint name was valid, or if next + # unused_name created has choosen a different one + my $alter_fk_constraint = SQL::Translator::Producer::PostgreSQL::alter_drop_constraint($constraint); + is($alter_fk_constraint, "ALTER TABLE mytable DROP CONSTRAINT $name", 'Alter drop Foreign Key constraint works'); + } + else { + ns_is($fk_constraint_fk_ref->[0], "ALTER TABLE mytable ADD FOREIGN KEY (myfield) + REFERENCES mytable2 (myfield_2)$deferrable", 'Create un-named Foreign Key Constraint works'); + + my $alter_fk_constraint = SQL::Translator::Producer::PostgreSQL::alter_drop_constraint($constraint); + is($alter_fk_constraint, 'ALTER TABLE mytable DROP CONSTRAINT mytable_myfield_fkey', 'Alter drop un-named Foreign Key constraint works'); + } } } # check named, and unnamed primary keys for my $name ( 'foo', undef ) { + # PK defaults to deferrable => 0 my $pk_constraint = SQL::Translator::Schema::Constraint->new( table => $table, name => $name, @@ -183,23 +200,27 @@ for my $name ( 'foo', undef ) { name => $name, fields => [qw(myfield)], type => 'PRIMARY_KEY', + deferrable => 1, ); - my ($pk_constraint_def_ref, $pk_constraint_pk_ref ) = SQL::Translator::Producer::PostgreSQL::create_constraint($pk_constraint); + for my $pk ($pk_constraint, $pk_constraint_2) { + my ($pk_constraint_def_ref, $pk_constraint_pk_ref ) = SQL::Translator::Producer::PostgreSQL::create_constraint($pk); - if ( $name ) { - is($pk_constraint_def_ref->[0], "CONSTRAINT $name PRIMARY KEY (myfield)", 'Create Primary Key Constraint works'); + my $deferrable = $pk->deferrable ? ' DEFERRABLE' : ''; + if ( $name ) { + is($pk_constraint_def_ref->[0], "CONSTRAINT $name PRIMARY KEY (myfield)$deferrable", 'Create Primary Key Constraint works'); - # ToDo: may we should check if the constraint name was valid, or if next - # unused_name created has choosen a different one - my $alter_pk_constraint = SQL::Translator::Producer::PostgreSQL::alter_drop_constraint($pk_constraint); - is($alter_pk_constraint, "ALTER TABLE mytable DROP CONSTRAINT $name", 'Alter drop Primary Key constraint works'); - } - else { - is($pk_constraint_def_ref->[0], 'PRIMARY KEY (myfield)', 'Create un-named Primary Key Constraint works'); + # ToDo: may we should check if the constraint name was valid, or if next + # unused_name created has choosen a different one + my $alter_pk_constraint = SQL::Translator::Producer::PostgreSQL::alter_drop_constraint($pk); + is($alter_pk_constraint, "ALTER TABLE mytable DROP CONSTRAINT $name", 'Alter drop Primary Key constraint works'); + } + else { + is($pk_constraint_def_ref->[0], "PRIMARY KEY (myfield)$deferrable", 'Create un-named Primary Key Constraint works'); - my $alter_pk_constraint = SQL::Translator::Producer::PostgreSQL::alter_drop_constraint($pk_constraint); - is($alter_pk_constraint, 'ALTER TABLE mytable DROP CONSTRAINT mytable_pkey', 'Alter drop un-named Foreign Key constraint works'); + my $alter_pk_constraint = SQL::Translator::Producer::PostgreSQL::alter_drop_constraint($pk); + is($alter_pk_constraint, 'ALTER TABLE mytable DROP CONSTRAINT mytable_pkey', 'Alter drop un-named Foreign Key constraint works'); + } } } diff --git a/t/data/diff/pgsql/create1.yml b/t/data/diff/pgsql/create1.yml index d31314641..c77e3743c 100644 --- a/t/data/diff/pgsql/create1.yml +++ b/t/data/diff/pgsql/create1.yml @@ -45,7 +45,7 @@ schema: - person_id reference_table: person type: FOREIGN KEY - - deferrable: 1 + - deferrable: 0 expression: '' fields: - position @@ -111,7 +111,7 @@ schema: order: 4 person: constraints: - - deferrable: 1 + - deferrable: 0 expression: '' fields: - person_id diff --git a/t/data/diff/pgsql/create2.yml b/t/data/diff/pgsql/create2.yml index b58fdee8d..2a27df649 100644 --- a/t/data/diff/pgsql/create2.yml +++ b/t/data/diff/pgsql/create2.yml @@ -35,7 +35,7 @@ schema: - person_id reference_table: person type: FOREIGN KEY - - deferrable: 1 + - deferrable: 0 expression: '' fields: - employee_id @@ -96,7 +96,7 @@ schema: order: 4 person: constraints: - - deferrable: 1 + - deferrable: 0 expression: '' fields: - person_id @@ -108,7 +108,7 @@ schema: reference_fields: [] reference_table: '' type: PRIMARY KEY - - deferrable: 1 + - deferrable: 0 expression: '' fields: - person_id diff --git a/t/data/template/testresult_basic.txt b/t/data/template/testresult_basic.txt index 8a7d2e95c..cc1f0f2c8 100644 --- a/t/data/template/testresult_basic.txt +++ b/t/data/template/testresult_basic.txt @@ -152,7 +152,7 @@ Constraints match_type: reference_fields: reference_table: - deferrable: 1 + deferrable: 0 on_delete: on_update: options: @@ -165,7 +165,7 @@ Constraints match_type: reference_fields: reference_table: - deferrable: 1 + deferrable: 0 on_delete: on_update: options: @@ -178,7 +178,7 @@ Constraints match_type: reference_fields: reference_table: - deferrable: 1 + deferrable: 0 on_delete: on_update: options: @@ -240,7 +240,7 @@ Constraints match_type: reference_fields: reference_table: - deferrable: 1 + deferrable: 0 on_delete: on_update: options: diff --git a/t/data/xml/schema.xml b/t/data/xml/schema.xml index 048d8577f..295891907 100644 --- a/t/data/xml/schema.xml +++ b/t/data/xml/schema.xml @@ -49,12 +49,12 @@ Created on Fri Aug 15 15:08:18 2003 - - + + @@ -81,7 +81,7 @@ Created on Fri Aug 15 15:08:18 2003