Skip Menu |

This queue is for tickets about the SQL-Translator CPAN distribution.

Report information
The Basics
Id: 44769
Status: resolved
Priority: 0/
Queue: SQL-Translator

People
Owner: Nobody in particular
Requestors: ddascalescu+perl [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 0.09004
Fixed in: 0.11011



Subject: SQLite producer should output FOREIGN KEY / REFERENCES constraints
"FOREIGN KEY constraints are parsed but are not enforced." -- http://www.sqlite.org/omitted.html Producing the foreign keys and having them stored in the DB (even though they are not enforced) would be very useful for DB exploration programs that can build an ER diagram out of the database (e.g. SQLite Maestro, DbVisualizer).
It's a great idea. I agree that having the FKs can be useful if SQLite keeps them around. MySQL's MyISAM tables also parse the FKs, but do not keep them or re-emit them for evaluation by visualization tools like "sqlt-graph." It would be terrific if you could copy the section from an existing producer that goes over $schema->get_constraints and have the SQLite producer emit the proper syntax.
From: thomas.low [...] ovgu.de
It's actually quite easy to add. I've attached a simple patch. Tested with visual query builders SQLite Code Factory from SQLMaestro and SPSS from IBM. Cheers. On Fri Apr 03 09:58:02 2009, KCLARK wrote: Show quoted text
> It's a great idea. I agree that having the FKs can be useful if SQLite > keeps them around. MySQL's MyISAM tables also parse the FKs, but do not > keep them or re-emit them for evaluation by visualization tools like > "sqlt-graph." It would be terrific if you could copy the section from > an existing producer that goes over $schema->get_constraints and have > the SQLite producer emit the proper syntax.
Subject: SQLite.pm.diff
Index: trunk/sqlfairy/lib/SQL/Translator/Producer/SQLite.pm =================================================================== --- trunk/sqlfairy/lib/SQL/Translator/Producer/SQLite.pm (revision 1727) +++ trunk/sqlfairy/lib/SQL/Translator/Producer/SQLite.pm (working copy) @@ -234,6 +234,9 @@ # my $c_name_default = 'A'; for my $c ( $table->get_constraints ) { + if ($c->type eq "FOREIGN KEY") { + push @field_defs, create_foreignkey($c); + } next unless $c->type eq UNIQUE; push @constraint_defs, create_constraint($c); } @@ -243,6 +246,15 @@ return (@create, $create_table, @index_defs, @constraint_defs ); } +sub create_foreignkey { + my $c = shift; + + my $fk_sql = "FOREIGN KEY(".$c->{fields}[0].") REFERENCES "; + $fk_sql .= $c->{reference_table}."(".$c->{reference_fields}[0].")"; + + return $fk_sql; +} + sub create_field { my ($field, $options) = @_;
On Tue Jan 11 11:10:07 2011, https://launchpad.net/~thomaslow wrote: Show quoted text
> It's actually quite easy to add. I've attached a simple patch. Tested > with visual query builders SQLite Code Factory from SQLMaestro and SPSS > from IBM. Cheers.
I applied your patch and it produced something that looks to me to be correct. However, the tests weren't updated to match (tests 30sqlt-new-diff-sqlite.t, 48xml-to-sqlite.t, and 60roundtrip.t, specifically), so I'm not going to commit this until the tests are updated.
On Tue Jan 11 16:55:02 2011, DARREN wrote: Show quoted text
> On Tue Jan 11 11:10:07 2011, https://launchpad.net/~thomaslow wrote:
> > It's actually quite easy to add. I've attached a simple patch.
> Tested
> > with visual query builders SQLite Code Factory from SQLMaestro and
> SPSS
> > from IBM. Cheers.
> > I applied your patch and it produced something that looks to me to be > correct. However, the > tests weren't updated to match (tests 30sqlt-new-diff-sqlite.t, 48xml- > to-sqlite.t, and > 60roundtrip.t, specifically), so I'm not going to commit this until > the tests are updated.
Patch applied, working on tests now
This patch is an evolution of the previous patch for this bug and the patch for #16412. The test suite is also updated to reflect this. No new tests are added, they are merely (slightly) modified to pass. I've only added the FOREIGN KEY info to the expected output of the following test files: t/30sqlt-new-diff-sqlite.t t/48xml-to-sqlite.t The test t/60roundtrip.t didn't need to be modified since both the Parser and Producer is updated. /viklund
Subject: sqlfairy_sqlite_foreign_keys.patch
diff --git a/lib/SQL/Translator/Parser/SQLite.pm b/lib/SQL/Translator/Parser/SQLite.pm index 0188028..4f46c8b 100644 --- a/lib/SQL/Translator/Parser/SQLite.pm +++ b/lib/SQL/Translator/Parser/SQLite.pm @@ -433,6 +433,17 @@ table_constraint : PRIMARY_KEY parens_field_list conflict_clause(?) on_conflict => $item[5][0], } } + | + FOREIGN_KEY parens_field_list REFERENCES ref_def + { + $return = { + supertype => 'constraint', + type => 'foreign_key', + fields => $item[2], + reference_table => $item[4]{'reference_table'}, + reference_fields => $item[4]{'reference_fields'}, + } + } ref_def : /(\w+)\s*\((\w+)\)/ { $return = { reference_table => $1, reference_fields => $2 } } @@ -573,6 +584,8 @@ NOT_NULL : /not null/i PRIMARY_KEY : /primary key/i +FOREIGN_KEY : /foreign key/i + CHECK_C : /check/i DEFAULT : /default/i diff --git a/lib/SQL/Translator/Producer/SQLite.pm b/lib/SQL/Translator/Producer/SQLite.pm index 2e7aa6a..8ebea42 100644 --- a/lib/SQL/Translator/Producer/SQLite.pm +++ b/lib/SQL/Translator/Producer/SQLite.pm @@ -234,6 +234,9 @@ sub create_table # my $c_name_default = 'A'; for my $c ( $table->get_constraints ) { + if ($c->type eq FOREIGN_KEY) { + push @field_defs, create_foreignkey($c, $options); + } next unless $c->type eq UNIQUE; push @constraint_defs, create_constraint($c); } @@ -243,6 +246,39 @@ sub create_table return (@create, $create_table, @index_defs, @constraint_defs ); } +sub create_foreignkey { + my ($c, $options) = @_; + + my @fields = $c->fields; + my $fk_sql = "FOREIGN KEY (" . join(', ',@fields) . ") REFERENCES "; + $fk_sql .= $c->{reference_table}; + + # Copied from Producer::MySQL::create_constraint and slightly adjusted + my $table = $c->table; + my @rfields = map { $_ || () } $c->reference_fields; + unless ( @rfields ) { + my $rtable_name = $c->reference_table; + if ( my $ref_table = $table->schema->get_table( $rtable_name ) ) { + push @rfields, $ref_table->primary_key; + } + else { + warn "Can't find reference table '$rtable_name' " . + "in schema\n" if $options->{show_warnings}; + } + } + + if ( @rfields ) { + $fk_sql .= '(' . join( ", ", @rfields ) . ')'; + } + else { + warn "FK constraint on " . $table->name . '.' . + join('', @fields) . " has no reference fields\n" + if $options->{show_warnings}; + } + + return $fk_sql; +} + sub create_field { my ($field, $options) = @_; diff --git a/t/30sqlt-new-diff-sqlite.t b/t/30sqlt-new-diff-sqlite.t index 5d6ad92..3778779 100644 --- a/t/30sqlt-new-diff-sqlite.t +++ b/t/30sqlt-new-diff-sqlite.t @@ -98,7 +98,8 @@ CREATE TABLE added ( CREATE TEMPORARY TABLE employee_temp_alter ( position varchar(50) NOT NULL, employee_id int(11) NOT NULL, - PRIMARY KEY (position, employee_id) + PRIMARY KEY (position, employee_id), + FOREIGN KEY (employee_id) REFERENCES person(person_id) ); INSERT INTO employee_temp_alter SELECT position, employee_id FROM employee; @@ -108,7 +109,8 @@ DROP TABLE employee; CREATE TABLE employee ( position varchar(50) NOT NULL, employee_id int(11) NOT NULL, - PRIMARY KEY (position, employee_id) + PRIMARY KEY (position, employee_id), + FOREIGN KEY (employee_id) REFERENCES person(person_id) ); INSERT INTO employee SELECT position, employee_id FROM employee_temp_alter; diff --git a/t/48xml-to-sqlite.t b/t/48xml-to-sqlite.t index 8f38fec..5d76841 100644 --- a/t/48xml-to-sqlite.t +++ b/t/48xml-to-sqlite.t @@ -48,7 +48,8 @@ CREATE TABLE Basic ( -- Hello emptytagdef emptytagdef varchar DEFAULT '', another_id int(10) DEFAULT 2, - timest timestamp + timest timestamp, + FOREIGN KEY (another_id) REFERENCES Another(id) ); CREATE INDEX titleindex ON Basic (title); @@ -105,7 +106,8 @@ eq_or_diff(\@sql, -- Hello emptytagdef emptytagdef varchar DEFAULT \'\', another_id int(10) DEFAULT 2, - timest timestamp + timest timestamp, + FOREIGN KEY (another_id) REFERENCES Another(id) )', 'CREATE INDEX titleindex ON Basic (title)', 'CREATE UNIQUE INDEX emailuniqueindex ON Basic (email)',
Committed as part of ea4a3ecc5de1c8f062fef3bab51e1cc7a2c23235, with some changes (fatalize no-ref definitions). Proper release coming soon. Cheers!