From 325173f57ef3e2b0c3f358f427e3d3282e25283b Mon Sep 17 00:00:00 2001 From: Alastair McGowan-Douglas Date: Mon, 2 Nov 2015 15:05:11 +0000 Subject: [PATCH 1/7] Honour the deferred flag on *all* constraints --- lib/SQL/Translator/Producer/PostgreSQL.pm | 5 +++++ t/30sqlt-new-diff-pgsql.t | 11 +++++++---- t/46xml-to-pg.t | 8 ++++---- t/47postgres-producer.t | 18 ++++++++--------- t/63-spacial-pgsql.t | 24 +++++++++++------------ 5 files changed, 37 insertions(+), 29 deletions(-) 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/t/30sqlt-new-diff-pgsql.t b/t/30sqlt-new-diff-pgsql.t index 5bc782038..9df3bce9e 100644 --- a/t/30sqlt-new-diff-pgsql.t +++ b/t/30sqlt-new-diff-pgsql.t @@ -46,6 +46,9 @@ my $out = SQL::Translator::Diff::schema_diff( } ); +# NOTE that unique_name is an index and not a constraint (create2.yml) and +# therefore does not have the DEFERRABLE flag on it + eq_or_diff($out, <<'## END OF DIFF', "Diff as expected"); -- Convert schema 'create1.yml' to 'create2.yml':; @@ -88,9 +91,9 @@ ALTER TABLE "person" RENAME COLUMN "description" TO "physical_description"; ALTER TABLE "person" ADD CONSTRAINT "unique_name" UNIQUE ("name"); -ALTER TABLE "person" ADD CONSTRAINT "UC_person_id" UNIQUE ("person_id"); +ALTER TABLE "person" ADD CONSTRAINT "UC_person_id" UNIQUE ("person_id") DEFERRABLE; -ALTER TABLE "person" ADD CONSTRAINT "UC_age_name" UNIQUE ("age", "name"); +ALTER TABLE "person" ADD CONSTRAINT "UC_age_name" UNIQUE ("age", "name") DEFERRABLE; DROP TABLE "deleted" CASCADE; @@ -141,9 +144,9 @@ ALTER TABLE person ALTER COLUMN nickname TYPE character varying(24); ALTER TABLE person RENAME COLUMN description TO physical_description; -ALTER TABLE person ADD CONSTRAINT UC_person_id UNIQUE (person_id); +ALTER TABLE person ADD CONSTRAINT UC_person_id UNIQUE (person_id) DEFERRABLE; -ALTER TABLE person ADD CONSTRAINT UC_age_name UNIQUE (age, name); +ALTER TABLE person ADD CONSTRAINT UC_age_name UNIQUE (age, name) DEFERRABLE; DROP TABLE deleted CASCADE; diff --git a/t/46xml-to-pg.t b/t/46xml-to-pg.t index d9dd3277b..18809ec88 100644 --- a/t/46xml-to-pg.t +++ b/t/46xml-to-pg.t @@ -46,9 +46,9 @@ CREATE TABLE "Basic" ( "emptytagdef" character varying DEFAULT '', "another_id" integer DEFAULT 2, "timest" timestamp, - PRIMARY KEY ("id"), - CONSTRAINT "emailuniqueindex" UNIQUE ("email"), - CONSTRAINT "very_long_index_name_on_title_field_which_should_be_truncated_for_various_rdbms" UNIQUE ("title") + PRIMARY KEY ("id") DEFERRABLE, + CONSTRAINT "emailuniqueindex" UNIQUE ("email") DEFERRABLE, + CONSTRAINT "very_long_index_name_on_title_field_which_should_be_truncated_for_various_rdbms" UNIQUE ("title") DEFERRABLE ); CREATE INDEX "titleindex" on "Basic" ("title"); @@ -56,7 +56,7 @@ DROP TABLE "Another" CASCADE; CREATE TABLE "Another" ( "id" serial NOT NULL, "num" numeric(10,2), - PRIMARY KEY ("id") + PRIMARY KEY ("id") DEFERRABLE ); DROP VIEW "email_list"; diff --git a/t/47postgres-producer.t b/t/47postgres-producer.t index 9c50db736..6e2f2a0cf 100644 --- a/t/47postgres-producer.t +++ b/t/47postgres-producer.t @@ -112,7 +112,7 @@ my $pk_constraint = SQL::Translator::Schema::Constraint->new( my ($pk_constraint_def_ref, $pk_constraint_fk_ref ) = SQL::Translator::Producer::PostgreSQL::create_constraint($pk_constraint); ok(@{$pk_constraint_def_ref} == 1 && @{$pk_constraint_fk_ref} == 0, 'precheck of create_Primary Key constraint'); -is($pk_constraint_def_ref->[0], 'CONSTRAINT foo PRIMARY KEY (myfield)', 'Create Primary Key Constraint works'); +is($pk_constraint_def_ref->[0], 'CONSTRAINT foo PRIMARY KEY (myfield) DEFERRABLE', 'Create 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 foo', 'Alter drop Primary Key constraint works'); @@ -188,7 +188,7 @@ for my $name ( 'foo', undef ) { my ($pk_constraint_def_ref, $pk_constraint_pk_ref ) = SQL::Translator::Producer::PostgreSQL::create_constraint($pk_constraint); if ( $name ) { - is($pk_constraint_def_ref->[0], "CONSTRAINT $name PRIMARY KEY (myfield)", 'Create Primary Key Constraint works'); + 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 @@ -196,7 +196,7 @@ for my $name ( 'foo', undef ) { 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'); + 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'); @@ -658,25 +658,25 @@ is($view2_sql1, $view2_sql_replace, 'correct "CREATE OR REPLACE VIEW" SQL 2'); { my $constr = $table->add_constraint(name => 'constr', type => UNIQUE, fields => ['foo']); my ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr); - is($def->[0], 'CONSTRAINT constr UNIQUE (foo)', 'constraint created'); + is($def->[0], 'CONSTRAINT constr UNIQUE (foo) DEFERRABLE', 'constraint created'); ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr, $quote); - is($def->[0], 'CONSTRAINT "constr" UNIQUE ("foo")', 'constraint created w/ quotes'); + is($def->[0], 'CONSTRAINT "constr" UNIQUE ("foo") DEFERRABLE', 'constraint created w/ quotes'); } { my $constr = $table->add_constraint(name => 'constr', type => UNIQUE, fields => ['lower(foo)']); my ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr); - is($def->[0], 'CONSTRAINT constr UNIQUE (lower(foo))', 'constraint created'); + is($def->[0], 'CONSTRAINT constr UNIQUE (lower(foo)) DEFERRABLE', 'constraint created'); ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr, $quote); - is($def->[0], 'CONSTRAINT "constr" UNIQUE (lower(foo))', 'constraint created w/ quotes'); + is($def->[0], 'CONSTRAINT "constr" UNIQUE (lower(foo)) DEFERRABLE', 'constraint created w/ quotes'); } { my $constr = $table->add_constraint(name => 'constr', type => UNIQUE, fields => ['bar', 'lower(foo)']); my ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr); - is($def->[0], 'CONSTRAINT constr UNIQUE (bar, lower(foo))', 'constraint created'); + is($def->[0], 'CONSTRAINT constr UNIQUE (bar, lower(foo)) DEFERRABLE', 'constraint created'); ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr, $quote); - is($def->[0], 'CONSTRAINT "constr" UNIQUE ("bar", lower(foo))', 'constraint created w/ quotes'); + is($def->[0], 'CONSTRAINT "constr" UNIQUE ("bar", lower(foo)) DEFERRABLE', 'constraint created w/ quotes'); } { diff --git a/t/63-spacial-pgsql.t b/t/63-spacial-pgsql.t index cf7c24b4f..d7362441c 100644 --- a/t/63-spacial-pgsql.t +++ b/t/63-spacial-pgsql.t @@ -52,9 +52,9 @@ is($field1_geocol, "INSERT INTO geometry_columns VALUES ('','myschema','my''tabl my $field1_geocon = SQL::Translator::Producer::PostgreSQL::add_geometry_constraints($field1, $options); -is($field1_geocon, qq[ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_myfield" CHECK ((ST_NDims("myfield") = 2)); -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_myfield" CHECK ((ST_SRID("myfield") = -1)); -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_myfield" CHECK ((GeometryType("myfield") = 'POINT'::text OR "myfield" IS NULL))], +is($field1_geocon, qq[ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_myfield" CHECK ((ST_NDims("myfield") = 2)) DEFERRABLE; +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_myfield" CHECK ((ST_SRID("myfield") = -1)) DEFERRABLE; +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_myfield" CHECK ((GeometryType("myfield") = 'POINT'::text OR "myfield" IS NULL)) DEFERRABLE], 'Add geometry constraints works'); my $field2 = SQL::Translator::Schema::Field->new( name => 'myfield', @@ -82,9 +82,9 @@ my $alter_field2 = SQL::Translator::Producer::PostgreSQL::alter_field($field2, is($alter_field2, qq[ALTER TABLE "my'table" ALTER COLUMN "myfield" DROP NOT NULL; ALTER TABLE "my'table" ALTER COLUMN "myfield" TYPE geometry; INSERT INTO geometry_columns VALUES ('','myschema','my''table','myfield','2','-1','POINT'); -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_myfield" CHECK ((ST_NDims("myfield") = 2)); -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_myfield" CHECK ((ST_SRID("myfield") = -1)); -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_myfield" CHECK ((GeometryType("myfield") = 'POINT'::text OR "myfield" IS NULL))], +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_myfield" CHECK ((ST_NDims("myfield") = 2)) DEFERRABLE; +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_myfield" CHECK ((ST_SRID("myfield") = -1)) DEFERRABLE; +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_myfield" CHECK ((GeometryType("myfield") = 'POINT'::text OR "myfield" IS NULL)) DEFERRABLE], 'Alter field non geometry to geometry works'); $field1->name('field3'); @@ -92,9 +92,9 @@ my $add_field = SQL::Translator::Producer::PostgreSQL::add_field($field1, $optio is($add_field, qq[ALTER TABLE "my'table" ADD COLUMN "field3" geometry; INSERT INTO geometry_columns VALUES ('','myschema','my''table','field3','2','-1','POINT'); -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_field3" CHECK ((ST_NDims("field3") = 2)); -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_field3" CHECK ((ST_SRID("field3") = -1)); -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_field3" CHECK ((GeometryType("field3") = 'POINT'::text OR "field3" IS NULL))], +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_field3" CHECK ((ST_NDims("field3") = 2)) DEFERRABLE; +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_field3" CHECK ((ST_SRID("field3") = -1)) DEFERRABLE; +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_field3" CHECK ((GeometryType("field3") = 'POINT'::text OR "field3" IS NULL)) DEFERRABLE], 'Add geometry field works'); my $drop_field = SQL::Translator::Producer::PostgreSQL::drop_field($field1, $options); @@ -125,9 +125,9 @@ is($create_table,qq[-- CREATE TABLE "my'table" ( "field3" geometry, "field4" geography(POINT,-1), - CONSTRAINT "enforce_dims_field3" CHECK ((ST_NDims("field3") = 2)), - CONSTRAINT "enforce_srid_field3" CHECK ((ST_SRID("field3") = -1)), - CONSTRAINT "enforce_geotype_field3" CHECK ((GeometryType("field3") = 'POINT'::text OR "field3" IS NULL)) + CONSTRAINT "enforce_dims_field3" CHECK ((ST_NDims("field3") = 2)) DEFERRABLE, + CONSTRAINT "enforce_srid_field3" CHECK ((ST_SRID("field3") = -1)) DEFERRABLE, + CONSTRAINT "enforce_geotype_field3" CHECK ((GeometryType("field3") = 'POINT'::text OR "field3" IS NULL)) DEFERRABLE ); INSERT INTO geometry_columns VALUES ('','myschema','my''table','field3','2','-1','POINT')], 'Create table with geometry works.'); From f32a8dcf4d74586d620aa8085854c2d6806b60b8 Mon Sep 17 00:00:00 2001 From: Alastair McGowan-Douglas Date: Tue, 3 Nov 2015 10:21:40 +0000 Subject: [PATCH 2/7] Set deferrable default to 1 for FK, 0 otherwise --- lib/SQL/Translator/Schema/Constraint.pm | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/SQL/Translator/Schema/Constraint.pm b/lib/SQL/Translator/Schema/Constraint.pm index 7742bf777..adcc7a91d 100644 --- a/lib/SQL/Translator/Schema/Constraint.pm +++ b/lib/SQL/Translator/Schema/Constraint.pm @@ -78,9 +78,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 +92,9 @@ False, so the following are equivalent: has deferrable => ( is => 'rw', coerce => quote_sub(q{ $_[0] ? 1 : 0 }), - default => quote_sub(q{ 1 }), + default => sub { + $_[0]->type eq FOREIGN_KEY + }, ); =head2 expression From f5ce080441e4672587ec2c8e8c7ad5603d453cee Mon Sep 17 00:00:00 2001 From: Alastair McGowan-Douglas Date: Tue, 3 Nov 2015 10:38:19 +0000 Subject: [PATCH 3/7] Set deferrable lazy, so default can see type attribute --- lib/SQL/Translator/Schema/Constraint.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/SQL/Translator/Schema/Constraint.pm b/lib/SQL/Translator/Schema/Constraint.pm index adcc7a91d..e92afc19e 100644 --- a/lib/SQL/Translator/Schema/Constraint.pm +++ b/lib/SQL/Translator/Schema/Constraint.pm @@ -92,6 +92,7 @@ True or False, so the following are equivalent: has deferrable => ( is => 'rw', coerce => quote_sub(q{ $_[0] ? 1 : 0 }), + lazy => 1, default => sub { $_[0]->type eq FOREIGN_KEY }, From 11106a0cc8960cc55fb790c39d103da4114d1e5c Mon Sep 17 00:00:00 2001 From: Alastair McGowan-Douglas Date: Tue, 3 Nov 2015 10:39:00 +0000 Subject: [PATCH 4/7] Special-case the default deferrable value in tests --- lib/Test/SQL/Translator.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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; } From b9fe3b4a00b076ec18cbecb221c7efaa95c7b982 Mon Sep 17 00:00:00 2001 From: Alastair McGowan-Douglas Date: Tue, 3 Nov 2015 11:01:57 +0000 Subject: [PATCH 5/7] Fix all tests where deferrable has become relevant --- t/17sqlfxml-producer.t | 4 ++-- t/23json.t | 8 ++++---- t/24yaml.t | 8 ++++---- t/30sqlt-new-diff-pgsql.t | 11 ++++------- t/39-filter-globals.t | 2 +- t/46xml-to-pg.t | 8 ++++---- t/47postgres-producer.t | 18 +++++++++--------- t/63-spacial-pgsql.t | 24 ++++++++++++------------ t/data/diff/pgsql/create1.yml | 6 +++--- t/data/diff/pgsql/create2.yml | 8 ++++---- t/data/template/testresult_basic.txt | 8 ++++---- t/data/xml/schema.xml | 8 ++++---- 12 files changed, 55 insertions(+), 58 deletions(-) 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/46xml-to-pg.t b/t/46xml-to-pg.t index 18809ec88..d9dd3277b 100644 --- a/t/46xml-to-pg.t +++ b/t/46xml-to-pg.t @@ -46,9 +46,9 @@ CREATE TABLE "Basic" ( "emptytagdef" character varying DEFAULT '', "another_id" integer DEFAULT 2, "timest" timestamp, - PRIMARY KEY ("id") DEFERRABLE, - CONSTRAINT "emailuniqueindex" UNIQUE ("email") DEFERRABLE, - CONSTRAINT "very_long_index_name_on_title_field_which_should_be_truncated_for_various_rdbms" UNIQUE ("title") DEFERRABLE + PRIMARY KEY ("id"), + CONSTRAINT "emailuniqueindex" UNIQUE ("email"), + CONSTRAINT "very_long_index_name_on_title_field_which_should_be_truncated_for_various_rdbms" UNIQUE ("title") ); CREATE INDEX "titleindex" on "Basic" ("title"); @@ -56,7 +56,7 @@ DROP TABLE "Another" CASCADE; CREATE TABLE "Another" ( "id" serial NOT NULL, "num" numeric(10,2), - PRIMARY KEY ("id") DEFERRABLE + PRIMARY KEY ("id") ); DROP VIEW "email_list"; diff --git a/t/47postgres-producer.t b/t/47postgres-producer.t index 6e2f2a0cf..9c50db736 100644 --- a/t/47postgres-producer.t +++ b/t/47postgres-producer.t @@ -112,7 +112,7 @@ my $pk_constraint = SQL::Translator::Schema::Constraint->new( my ($pk_constraint_def_ref, $pk_constraint_fk_ref ) = SQL::Translator::Producer::PostgreSQL::create_constraint($pk_constraint); ok(@{$pk_constraint_def_ref} == 1 && @{$pk_constraint_fk_ref} == 0, 'precheck of create_Primary Key constraint'); -is($pk_constraint_def_ref->[0], 'CONSTRAINT foo PRIMARY KEY (myfield) DEFERRABLE', 'Create Primary Key Constraint works'); +is($pk_constraint_def_ref->[0], 'CONSTRAINT foo PRIMARY KEY (myfield)', 'Create 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 foo', 'Alter drop Primary Key constraint works'); @@ -188,7 +188,7 @@ for my $name ( 'foo', undef ) { my ($pk_constraint_def_ref, $pk_constraint_pk_ref ) = SQL::Translator::Producer::PostgreSQL::create_constraint($pk_constraint); if ( $name ) { - is($pk_constraint_def_ref->[0], "CONSTRAINT $name PRIMARY KEY (myfield) DEFERRABLE", 'Create Primary Key Constraint works'); + is($pk_constraint_def_ref->[0], "CONSTRAINT $name PRIMARY KEY (myfield)", '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 @@ -196,7 +196,7 @@ for my $name ( 'foo', undef ) { 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'); + is($pk_constraint_def_ref->[0], 'PRIMARY KEY (myfield)', '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'); @@ -658,25 +658,25 @@ is($view2_sql1, $view2_sql_replace, 'correct "CREATE OR REPLACE VIEW" SQL 2'); { my $constr = $table->add_constraint(name => 'constr', type => UNIQUE, fields => ['foo']); my ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr); - is($def->[0], 'CONSTRAINT constr UNIQUE (foo) DEFERRABLE', 'constraint created'); + is($def->[0], 'CONSTRAINT constr UNIQUE (foo)', 'constraint created'); ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr, $quote); - is($def->[0], 'CONSTRAINT "constr" UNIQUE ("foo") DEFERRABLE', 'constraint created w/ quotes'); + is($def->[0], 'CONSTRAINT "constr" UNIQUE ("foo")', 'constraint created w/ quotes'); } { my $constr = $table->add_constraint(name => 'constr', type => UNIQUE, fields => ['lower(foo)']); my ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr); - is($def->[0], 'CONSTRAINT constr UNIQUE (lower(foo)) DEFERRABLE', 'constraint created'); + is($def->[0], 'CONSTRAINT constr UNIQUE (lower(foo))', 'constraint created'); ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr, $quote); - is($def->[0], 'CONSTRAINT "constr" UNIQUE (lower(foo)) DEFERRABLE', 'constraint created w/ quotes'); + is($def->[0], 'CONSTRAINT "constr" UNIQUE (lower(foo))', 'constraint created w/ quotes'); } { my $constr = $table->add_constraint(name => 'constr', type => UNIQUE, fields => ['bar', 'lower(foo)']); my ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr); - is($def->[0], 'CONSTRAINT constr UNIQUE (bar, lower(foo)) DEFERRABLE', 'constraint created'); + is($def->[0], 'CONSTRAINT constr UNIQUE (bar, lower(foo))', 'constraint created'); ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr, $quote); - is($def->[0], 'CONSTRAINT "constr" UNIQUE ("bar", lower(foo)) DEFERRABLE', 'constraint created w/ quotes'); + is($def->[0], 'CONSTRAINT "constr" UNIQUE ("bar", lower(foo))', 'constraint created w/ quotes'); } { diff --git a/t/63-spacial-pgsql.t b/t/63-spacial-pgsql.t index d7362441c..cf7c24b4f 100644 --- a/t/63-spacial-pgsql.t +++ b/t/63-spacial-pgsql.t @@ -52,9 +52,9 @@ is($field1_geocol, "INSERT INTO geometry_columns VALUES ('','myschema','my''tabl my $field1_geocon = SQL::Translator::Producer::PostgreSQL::add_geometry_constraints($field1, $options); -is($field1_geocon, qq[ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_myfield" CHECK ((ST_NDims("myfield") = 2)) DEFERRABLE; -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_myfield" CHECK ((ST_SRID("myfield") = -1)) DEFERRABLE; -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_myfield" CHECK ((GeometryType("myfield") = 'POINT'::text OR "myfield" IS NULL)) DEFERRABLE], +is($field1_geocon, qq[ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_myfield" CHECK ((ST_NDims("myfield") = 2)); +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_myfield" CHECK ((ST_SRID("myfield") = -1)); +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_myfield" CHECK ((GeometryType("myfield") = 'POINT'::text OR "myfield" IS NULL))], 'Add geometry constraints works'); my $field2 = SQL::Translator::Schema::Field->new( name => 'myfield', @@ -82,9 +82,9 @@ my $alter_field2 = SQL::Translator::Producer::PostgreSQL::alter_field($field2, is($alter_field2, qq[ALTER TABLE "my'table" ALTER COLUMN "myfield" DROP NOT NULL; ALTER TABLE "my'table" ALTER COLUMN "myfield" TYPE geometry; INSERT INTO geometry_columns VALUES ('','myschema','my''table','myfield','2','-1','POINT'); -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_myfield" CHECK ((ST_NDims("myfield") = 2)) DEFERRABLE; -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_myfield" CHECK ((ST_SRID("myfield") = -1)) DEFERRABLE; -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_myfield" CHECK ((GeometryType("myfield") = 'POINT'::text OR "myfield" IS NULL)) DEFERRABLE], +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_myfield" CHECK ((ST_NDims("myfield") = 2)); +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_myfield" CHECK ((ST_SRID("myfield") = -1)); +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_myfield" CHECK ((GeometryType("myfield") = 'POINT'::text OR "myfield" IS NULL))], 'Alter field non geometry to geometry works'); $field1->name('field3'); @@ -92,9 +92,9 @@ my $add_field = SQL::Translator::Producer::PostgreSQL::add_field($field1, $optio is($add_field, qq[ALTER TABLE "my'table" ADD COLUMN "field3" geometry; INSERT INTO geometry_columns VALUES ('','myschema','my''table','field3','2','-1','POINT'); -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_field3" CHECK ((ST_NDims("field3") = 2)) DEFERRABLE; -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_field3" CHECK ((ST_SRID("field3") = -1)) DEFERRABLE; -ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_field3" CHECK ((GeometryType("field3") = 'POINT'::text OR "field3" IS NULL)) DEFERRABLE], +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_field3" CHECK ((ST_NDims("field3") = 2)); +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_field3" CHECK ((ST_SRID("field3") = -1)); +ALTER TABLE "my'table" ADD CONSTRAINT "enforce_geotype_field3" CHECK ((GeometryType("field3") = 'POINT'::text OR "field3" IS NULL))], 'Add geometry field works'); my $drop_field = SQL::Translator::Producer::PostgreSQL::drop_field($field1, $options); @@ -125,9 +125,9 @@ is($create_table,qq[-- CREATE TABLE "my'table" ( "field3" geometry, "field4" geography(POINT,-1), - CONSTRAINT "enforce_dims_field3" CHECK ((ST_NDims("field3") = 2)) DEFERRABLE, - CONSTRAINT "enforce_srid_field3" CHECK ((ST_SRID("field3") = -1)) DEFERRABLE, - CONSTRAINT "enforce_geotype_field3" CHECK ((GeometryType("field3") = 'POINT'::text OR "field3" IS NULL)) DEFERRABLE + CONSTRAINT "enforce_dims_field3" CHECK ((ST_NDims("field3") = 2)), + CONSTRAINT "enforce_srid_field3" CHECK ((ST_SRID("field3") = -1)), + CONSTRAINT "enforce_geotype_field3" CHECK ((GeometryType("field3") = 'POINT'::text OR "field3" IS NULL)) ); INSERT INTO geometry_columns VALUES ('','myschema','my''table','field3','2','-1','POINT')], 'Create table with geometry works.'); diff --git a/t/data/diff/pgsql/create1.yml b/t/data/diff/pgsql/create1.yml index d31314641..3bea5f9eb 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 @@ -123,7 +123,7 @@ schema: reference_fields: [] reference_table: '' type: PRIMARY KEY - - deferrable: 1 + - deferrable: 0 expression: '' fields: - age diff --git a/t/data/diff/pgsql/create2.yml b/t/data/diff/pgsql/create2.yml index b58fdee8d..ab510d431 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 @@ -120,7 +120,7 @@ schema: reference_fields: [] reference_table: '' type: UNIQUE - - deferrable: 1 + - deferrable: 0 expression: '' fields: - age 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 From bb565d58903fba7df1d4a94c2edaf9806bb2fa80 Mon Sep 17 00:00:00 2001 From: Alastair McGowan-Douglas Date: Tue, 3 Nov 2015 12:04:59 +0000 Subject: [PATCH 6/7] Test deferrable on/off - not very thorough yet --- t/30sqlt-new-diff-pgsql.t | 4 +- t/47postgres-producer.t | 75 ++++++++++++++++++++++------------- t/data/diff/pgsql/create1.yml | 2 +- t/data/diff/pgsql/create2.yml | 2 +- 4 files changed, 52 insertions(+), 31 deletions(-) diff --git a/t/30sqlt-new-diff-pgsql.t b/t/30sqlt-new-diff-pgsql.t index 5bc782038..07e51ed8d 100644 --- a/t/30sqlt-new-diff-pgsql.t +++ b/t/30sqlt-new-diff-pgsql.t @@ -90,7 +90,7 @@ ALTER TABLE "person" ADD CONSTRAINT "unique_name" UNIQUE ("name"); ALTER TABLE "person" ADD CONSTRAINT "UC_person_id" UNIQUE ("person_id"); -ALTER TABLE "person" ADD CONSTRAINT "UC_age_name" UNIQUE ("age", "name"); +ALTER TABLE "person" ADD CONSTRAINT "UC_age_name" UNIQUE ("age", "name") DEFERRABLE; DROP TABLE "deleted" CASCADE; @@ -143,7 +143,7 @@ ALTER TABLE person RENAME COLUMN description TO physical_description; ALTER TABLE person ADD CONSTRAINT UC_person_id UNIQUE (person_id); -ALTER TABLE person ADD CONSTRAINT UC_age_name UNIQUE (age, name); +ALTER TABLE person ADD CONSTRAINT UC_age_name UNIQUE (age, name) DEFERRABLE; DROP TABLE deleted CASCADE; 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 3bea5f9eb..c77e3743c 100644 --- a/t/data/diff/pgsql/create1.yml +++ b/t/data/diff/pgsql/create1.yml @@ -123,7 +123,7 @@ schema: reference_fields: [] reference_table: '' type: PRIMARY KEY - - deferrable: 0 + - deferrable: 1 expression: '' fields: - age diff --git a/t/data/diff/pgsql/create2.yml b/t/data/diff/pgsql/create2.yml index ab510d431..2a27df649 100644 --- a/t/data/diff/pgsql/create2.yml +++ b/t/data/diff/pgsql/create2.yml @@ -120,7 +120,7 @@ schema: reference_fields: [] reference_table: '' type: UNIQUE - - deferrable: 0 + - deferrable: 1 expression: '' fields: - age From da2e927cfdf0a0a748fad37b5afb8abe7fb48ba0 Mon Sep 17 00:00:00 2001 From: Alastair McGowan-Douglas Date: Tue, 3 Nov 2015 13:16:59 +0000 Subject: [PATCH 7/7] Constraint now mentions deferrable in constructor doc --- lib/SQL/Translator/Schema/Constraint.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/SQL/Translator/Schema/Constraint.pm b/lib/SQL/Translator/Schema/Constraint.pm index e92afc19e..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