From bc769efaadf170fde55df126432a108620c1ebb1 Mon Sep 17 00:00:00 2001 From: Ed J Date: Sat, 4 Jan 2020 20:31:20 +0000 Subject: [PATCH 1/7] SQLite parser to understand CHECK with parens --- lib/SQL/Translator/Parser/SQLite.pm | 3 ++- t/data/sqlite/named.sql | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/SQL/Translator/Parser/SQLite.pm b/lib/SQL/Translator/Parser/SQLite.pm index d1c7b6fff..8f09464b2 100644 --- a/lib/SQL/Translator/Parser/SQLite.pm +++ b/lib/SQL/Translator/Parser/SQLite.pm @@ -477,7 +477,8 @@ column_list : field_name(s /,/) parens_value_list : '(' VALUE(s /,/) ')' { $item[2] } -expr : /[^)]+/ +expr : /[^)]* \( [^)]+ \) [^)]*/x # parens, balanced one deep + | /[^)]+/ sort_order : /(ASC|DESC)/i diff --git a/t/data/sqlite/named.sql b/t/data/sqlite/named.sql index 36546906b..a48686f25 100644 --- a/t/data/sqlite/named.sql +++ b/t/data/sqlite/named.sql @@ -8,6 +8,6 @@ create table pet ( constraint fk_person_id_3 references person(person_id) on update NO ACTION, "name" varchar(30), "age" int, - constraint age_under_100 check ( age < 100 ), + constraint age_under_100 check ( age < 100 and age not in (101, 102) ), constraint pk_pet primary key (pet_id, person_id) ); From bebf0aab9405a7d50c0a577808d4c8ca16fe100c Mon Sep 17 00:00:00 2001 From: Ed J Date: Fri, 31 Aug 2018 04:24:37 +0100 Subject: [PATCH 2/7] Test::Differences less context = more comprehend --- t/17sqlfxml-producer.t | 2 +- t/30sqlt-new-diff-mysql.t | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/t/17sqlfxml-producer.t b/t/17sqlfxml-producer.t index 7989e35d4..85042ce97 100644 --- a/t/17sqlfxml-producer.t +++ b/t/17sqlfxml-producer.t @@ -48,7 +48,7 @@ sub xml_equals my ($got, $expect, $msg) = (@_, "XML looks right"); $got =~ s/^ +//gm; $expect =~ s/^ +//gm; - eq_or_diff $got, $expect, $msg; + eq_or_diff $got, $expect, $msg, { context => 1 }; } # diff --git a/t/30sqlt-new-diff-mysql.t b/t/30sqlt-new-diff-mysql.t index 706c2752a..14aea88be 100644 --- a/t/30sqlt-new-diff-mysql.t +++ b/t/30sqlt-new-diff-mysql.t @@ -47,7 +47,7 @@ ok( @out, 'Got a list' ); my $out = join('', @out); -eq_or_diff($out, <<'## END OF DIFF', "Diff as expected"); +eq_or_diff($out, <<'## END OF DIFF', "Diff as expected", { context => 1 }); -- Convert schema 'create1.yml' to 'create2.yml':; BEGIN; @@ -109,7 +109,7 @@ $out = SQL::Translator::Diff::schema_diff($source_schema, 'MySQL', $target_schem producer_args => { quote_identifiers => 0 }, }); -eq_or_diff($out, <<'## END OF DIFF', "Diff as expected"); +eq_or_diff($out, <<'## END OF DIFF', "Diff as expected", { context => 1 }); -- Convert schema 'create1.yml' to 'create2.yml':; BEGIN; @@ -151,7 +151,7 @@ COMMIT; # Test for sameness $out = SQL::Translator::Diff::schema_diff($source_schema, 'MySQL', $source_schema, 'MySQL' ); -eq_or_diff($out, <<'## END OF DIFF', "No differences found"); +eq_or_diff($out, <<'## END OF DIFF', "No differences found", { context => 1 }); -- Convert schema 'create1.yml' to 'create1.yml':; -- No differences found; @@ -179,7 +179,7 @@ eq_or_diff($out, <<'## END OF DIFF', "No differences found"); $field->data_type('integer'); $field->size(0); $out = SQL::Translator::Diff::schema_diff($schema, 'MySQL', $target_schema, 'MySQL', { producer_args => { quote_identifiers => 0 } } ); - eq_or_diff($out, <<'## END OF DIFF', "No differences found"); + eq_or_diff($out, <<'## END OF DIFF', "No differences found", { context => 1 }); -- Convert schema 'create.sql' to 'create2.yml':; BEGIN; @@ -251,7 +251,7 @@ COMMIT; my $out = SQL::Translator::Diff::schema_diff($s1, 'MySQL', $s2, 'MySQL' ); - eq_or_diff($out, <<'## END OF DIFF', "Batch alter of constraints work for InnoDB"); + eq_or_diff($out, <<'## END OF DIFF', "Batch alter of constraints work for InnoDB", { context => 1 }); -- Convert schema 'Schema 1' to 'Schema 2':; BEGIN; @@ -302,7 +302,7 @@ COMMIT; ); my $out = SQL::Translator::Diff::schema_diff($s1, 'MySQL', $s2, 'MySQL' ); - eq_or_diff($out, <<'## END OF DIFF', "Alter/drop constraints works with rename table"); + eq_or_diff($out, <<'## END OF DIFF', "Alter/drop constraints works with rename table", { context => 1 }); -- Convert schema 'Schema 3' to 'Schema 4':; BEGIN; @@ -324,7 +324,7 @@ COMMIT; $out = SQL::Translator::Diff::schema_diff($s1, 'MySQL', $s2, 'MySQL', { producer_args => { quote_identifiers => 1 } } ); - eq_or_diff($out, <<'## END OF DIFF', "Quoting can be turned on"); + eq_or_diff($out, <<'## END OF DIFF', "Quoting can be turned on", { context => 1 }); -- Convert schema 'Schema 3' to 'Schema 4':; BEGIN; From 55fa814722ea1e01872298e62783896dae1996b6 Mon Sep 17 00:00:00 2001 From: Ed J Date: Sat, 29 Feb 2020 00:07:38 +0000 Subject: [PATCH 3/7] zap unused var --- t/30sqlt-new-diff-mysql.t | 2 +- t/30sqlt-new-diff-pgsql.t | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/t/30sqlt-new-diff-mysql.t b/t/30sqlt-new-diff-mysql.t index 14aea88be..25064d4f2 100644 --- a/t/30sqlt-new-diff-mysql.t +++ b/t/30sqlt-new-diff-mysql.t @@ -19,7 +19,7 @@ use_ok('SQL::Translator::Diff') or die "Cannot continue\n"; my $tr = SQL::Translator->new; -my ( $source_schema, $target_schema, $parsed_sql_schema ) = map { +my ( $source_schema, $target_schema ) = map { my $t = SQL::Translator->new; $t->parser( 'YAML' ) or die $tr->error; diff --git a/t/30sqlt-new-diff-pgsql.t b/t/30sqlt-new-diff-pgsql.t index 5bc782038..471dc8f43 100644 --- a/t/30sqlt-new-diff-pgsql.t +++ b/t/30sqlt-new-diff-pgsql.t @@ -19,7 +19,7 @@ use_ok('SQL::Translator::Diff') or die "Cannot continue\n"; my $tr = SQL::Translator->new; -my ( $source_schema, $target_schema, $parsed_sql_schema ) = map { +my ( $source_schema, $target_schema ) = map { my $t = SQL::Translator->new; $t->parser( 'YAML' ) or die $tr->error; From ae95e48e16c26a573063628ea56c2d54040e88f9 Mon Sep 17 00:00:00 2001 From: Ed J Date: Sat, 29 Feb 2020 00:38:41 +0000 Subject: [PATCH 4/7] alter_drop_constraint drop constraint, not index --- lib/SQL/Translator/Producer/MySQL.pm | 2 +- t/30sqlt-new-diff-mysql.t | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/SQL/Translator/Producer/MySQL.pm b/lib/SQL/Translator/Producer/MySQL.pm index c8586a93f..9e06b54d7 100644 --- a/lib/SQL/Translator/Producer/MySQL.pm +++ b/lib/SQL/Translator/Producer/MySQL.pm @@ -716,7 +716,7 @@ sub alter_drop_constraint push @out, $c->type; } else { - push @out, ($c->type eq FOREIGN_KEY ? $c->type : "INDEX"), + push @out, ($c->type eq FOREIGN_KEY ? $c->type : "CONSTRAINT"), $generator->quote($c->name); } return join(' ',@out); diff --git a/t/30sqlt-new-diff-mysql.t b/t/30sqlt-new-diff-mysql.t index 25064d4f2..ee6674e74 100644 --- a/t/30sqlt-new-diff-mysql.t +++ b/t/30sqlt-new-diff-mysql.t @@ -64,7 +64,7 @@ ALTER TABLE old_name RENAME TO new_name; ALTER TABLE employee DROP FOREIGN KEY FK5302D47D93FE702E; -ALTER TABLE person DROP INDEX UC_age_name; +ALTER TABLE person DROP CONSTRAINT UC_age_name; ALTER TABLE person DROP INDEX u_name; @@ -127,7 +127,7 @@ ALTER TABLE employee DROP COLUMN job_title; ALTER TABLE old_name RENAME TO new_name, ADD COLUMN new_field integer NULL; -ALTER TABLE person DROP INDEX UC_age_name, +ALTER TABLE person DROP CONSTRAINT UC_age_name, ADD COLUMN is_rock_star tinyint(4) NULL DEFAULT 1, CHANGE COLUMN person_id person_id integer(11) NOT NULL auto_increment, CHANGE COLUMN name name varchar(20) NOT NULL, @@ -196,7 +196,7 @@ ALTER TABLE employee DROP FOREIGN KEY FK5302D47D93FE702E, DROP COLUMN job_title, ADD CONSTRAINT FK5302D47D93FE702E_diff FOREIGN KEY (employee_id) REFERENCES person (person_id); -ALTER TABLE person DROP INDEX UC_age_name, +ALTER TABLE person DROP CONSTRAINT UC_age_name, DROP INDEX u_name, ADD COLUMN is_rock_star tinyint(4) NULL DEFAULT 1, ADD COLUMN value double(8, 2) NULL DEFAULT 0.00, From 51fe887de4cd72f1d5949eb333e3d06d47ddc6dd Mon Sep 17 00:00:00 2001 From: Ed J Date: Fri, 28 Feb 2020 23:57:03 +0000 Subject: [PATCH 5/7] Parser::MySQL to handle CHECK CONSTRAINT --- lib/SQL/Translator/Parser/MySQL.pm | 24 ++++++++++++++++++++++++ lib/SQL/Translator/Producer/MySQL.pm | 19 ++++++++++++++++++- t/30sqlt-new-diff-mysql.t | 1 + t/data/mysql/create.sql | 1 + t/data/mysql/create2.sql | 1 + 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/lib/SQL/Translator/Parser/MySQL.pm b/lib/SQL/Translator/Parser/MySQL.pm index df63744d7..c3a60bb5c 100644 --- a/lib/SQL/Translator/Parser/MySQL.pm +++ b/lib/SQL/Translator/Parser/MySQL.pm @@ -700,8 +700,31 @@ primary_key : /primary/i /key/i { 1 } constraint : primary_key_def | unique_key_def | foreign_key_def + | check_def | +expr : /[^)]* \( [^)]+ \) [^)]*/x # parens, balanced one deep + | /[^)]+/ + +check_def : check_def_begin '(' expr ')' + { + $return = { + supertype => 'constraint', + type => 'check', + name => $item[1], + expression => $item[3], + } + } + +check_def_begin : /constraint/i /check/i NAME + { $return = $item[3] } + | + /constraint/i NAME /check/i + { $return = $item[2] } + | + /constraint/i /check/i + { $return = '' } + foreign_key_def : foreign_key_def_begin parens_field_list reference_definition { $return = { @@ -1017,6 +1040,7 @@ sub parse { name => $cdata->{'name'}, type => $cdata->{'type'}, fields => $cdata->{'fields'}, + expression => $cdata->{'expression'}, reference_table => $cdata->{'reference_table'}, reference_fields => $cdata->{'reference_fields'}, match_type => $cdata->{'match_type'} || '', diff --git a/lib/SQL/Translator/Producer/MySQL.pm b/lib/SQL/Translator/Producer/MySQL.pm index 9e06b54d7..5106dfb3e 100644 --- a/lib/SQL/Translator/Producer/MySQL.pm +++ b/lib/SQL/Translator/Producer/MySQL.pm @@ -743,12 +743,14 @@ sub create_constraint my $reference_table_name = $generator->quote($c->reference_table); - my @fields = $c->fields or return; + my @fields = $c->fields; if ( $c->type eq PRIMARY_KEY ) { + return unless @fields; return 'PRIMARY KEY (' . join(", ", map { $generator->quote($_) } @fields) . ')'; } elsif ( $c->type eq UNIQUE ) { + return unless @fields; return sprintf 'UNIQUE %s(%s)', ((defined $c->name && $c->name) ? $generator->quote( @@ -760,6 +762,7 @@ sub create_constraint ; } elsif ( $c->type eq FOREIGN_KEY ) { + return unless @fields; # # Make sure FK field is indexed or MySQL complains. # @@ -813,6 +816,20 @@ sub create_constraint } return $def; } + elsif ( $c->type eq CHECK_C ) { + my $table = $c->table; + my $c_name = truncate_id_uniquely( $c->name, $options->{max_id_length} || $DEFAULT_MAX_ID_LENGTH ); + + my $def = join(' ', + 'CONSTRAINT', + ($c_name ? $generator->quote($c_name) : () ), + 'CHECK' + ); + + + $def .= ' ('. $c->expression . ')'; + return $def; + } return undef; } diff --git a/t/30sqlt-new-diff-mysql.t b/t/30sqlt-new-diff-mysql.t index ee6674e74..79df23f22 100644 --- a/t/30sqlt-new-diff-mysql.t +++ b/t/30sqlt-new-diff-mysql.t @@ -193,6 +193,7 @@ CREATE TABLE added ( SET foreign_key_checks=1; ALTER TABLE employee DROP FOREIGN KEY FK5302D47D93FE702E, + DROP CONSTRAINT demo_constraint, DROP COLUMN job_title, ADD CONSTRAINT FK5302D47D93FE702E_diff FOREIGN KEY (employee_id) REFERENCES person (person_id); diff --git a/t/data/mysql/create.sql b/t/data/mysql/create.sql index 642d9a233..37a3c8519 100644 --- a/t/data/mysql/create.sql +++ b/t/data/mysql/create.sql @@ -15,6 +15,7 @@ create table employee ( employee_id integer, job_title varchar(255), CONSTRAINT FK5302D47D93FE702E FOREIGN KEY (employee_id) REFERENCES person (person_id), + CONSTRAINT `demo_constraint` CHECK (`employee_id` > 0 and `employee_id` IS NOT NULL), PRIMARY KEY (position, employee_id) USING BTREE ) ENGINE=InnoDB; diff --git a/t/data/mysql/create2.sql b/t/data/mysql/create2.sql index 54cf4bb68..037b182a0 100644 --- a/t/data/mysql/create2.sql +++ b/t/data/mysql/create2.sql @@ -16,6 +16,7 @@ create table employee ( position varchar(50), employee_id INTEGER, CONSTRAINT FK5302D47D93FE702E_diff FOREIGN KEY (employee_id) REFERENCES person (person_id), + CONSTRAINT `demo_constraint` CHECK (`employee_id` > 0 and `employee_id` IS NOT NULL and `employee_id` not in (0)), PRIMARY KEY (employee_id, position) ) ENGINE=InnoDB; From 2282f0e27d4b459f2967e272dd114a77e0ea3b10 Mon Sep 17 00:00:00 2001 From: Ed J Date: Sat, 7 Mar 2020 20:09:54 +0000 Subject: [PATCH 6/7] whitespace --- t/data/mysql/create.sql | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/t/data/mysql/create.sql b/t/data/mysql/create.sql index 37a3c8519..64ab68593 100644 --- a/t/data/mysql/create.sql +++ b/t/data/mysql/create.sql @@ -11,12 +11,12 @@ create table person ( create unique index u_name on person (name); create table employee ( - position varchar(50), - employee_id integer, + position varchar(50), + employee_id integer, job_title varchar(255), - CONSTRAINT FK5302D47D93FE702E FOREIGN KEY (employee_id) REFERENCES person (person_id), - CONSTRAINT `demo_constraint` CHECK (`employee_id` > 0 and `employee_id` IS NOT NULL), - PRIMARY KEY (position, employee_id) USING BTREE + CONSTRAINT FK5302D47D93FE702E FOREIGN KEY (employee_id) REFERENCES person (person_id), + CONSTRAINT `demo_constraint` CHECK (`employee_id` > 0 and `employee_id` IS NOT NULL), + PRIMARY KEY (position, employee_id) USING BTREE ) ENGINE=InnoDB; create table deleted ( From 70aafeedd6733a3a8e17d90f86b66c62663438e5 Mon Sep 17 00:00:00 2001 From: Ed J Date: Sat, 7 Mar 2020 20:16:24 +0000 Subject: [PATCH 7/7] MariaDB allows defaults of column names --- lib/SQL/Translator/Parser/MySQL.pm | 5 +++++ t/data/mysql/create.sql | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/SQL/Translator/Parser/MySQL.pm b/lib/SQL/Translator/Parser/MySQL.pm index c3a60bb5c..eedf5839a 100644 --- a/lib/SQL/Translator/Parser/MySQL.pm +++ b/lib/SQL/Translator/Parser/MySQL.pm @@ -692,6 +692,11 @@ default_val : { $return = $item[2]; } + | + /default/i NAME # column value, allowed in MariaDB + { + $return = $item[2]; + } auto_inc : /auto_increment/i { 1 } diff --git a/t/data/mysql/create.sql b/t/data/mysql/create.sql index 64ab68593..e13eab026 100644 --- a/t/data/mysql/create.sql +++ b/t/data/mysql/create.sql @@ -4,7 +4,7 @@ create table person ( age integer, weight double(11,2), iq tinyint default '0', - description text, + description text default `name`, UNIQUE KEY UC_age_name (age) ) ENGINE=MyISAM;