* All parser grammars are now precompiled for speed
+* Fixes to SQLite foreign keys production (patch from Johan Viklund)
+ closes RT#16412, RT#44769
+* ON DELETE/UPDATE actions for SQLite (patch from Lukas Thiemeier)
+ closes RT#70734, RT#71283, RT#70378
* Proper quoting support in SQLite
* Support for triggers in PostgreSQL producer and parser
* Correct Data Type in SQLT::Parser::DBI::PostgreSQL (patch from Andrew Pam)
}
}
|
- REFERENCES ref_def
+ REFERENCES ref_def cascade_def(?)
{
$return = {
type => 'foreign_key',
reference_table => $item[2]{'reference_table'},
reference_fields => $item[2]{'reference_fields'},
+ on_delete => $item[3][0]{'on_delete'},
+ on_update => $item[3][0]{'on_update'},
}
}
|
}
}
|
- FOREIGN_KEY parens_field_list REFERENCES ref_def
+ FOREIGN_KEY parens_field_list REFERENCES ref_def cascade_def(?)
{
$return = {
supertype => 'constraint',
fields => $item[2],
reference_table => $item[4]{'reference_table'},
reference_fields => $item[4]{'reference_fields'},
+ on_delete => $item[5][0]{'on_delete'},
+ on_update => $item[5][0]{'on_update'},
}
}
ref_def : table_name parens_field_list
{ $return = { reference_table => $item[1]{name}, reference_fields => $item[2] } }
+cascade_def : cascade_update_def cascade_delete_def(?)
+ { $return = { on_update => $item[1], on_delete => $item[2][0] } }
+ |
+ cascade_delete_def cascade_update_def(?)
+ { $return = { on_delete => $item[1], on_update => $item[2][0] } }
+
+cascade_delete_def : /on\s+delete\s+(\w+)/i
+ { $return = $1}
+
+cascade_update_def : /on\s+update\s+(\w+)/i
+ { $return = $1}
+
table_name : qualified_name
qualified_name : NAME
sub create_foreignkey {
my $c = shift;
- my $field = $util->quote($c->{fields}[0]);
- my $table = $c->{reference_table} ? $util->quote($c->{reference_table}) : '';
- my $ref = $c->{reference_fields}[0] ? $util->quote($c->{reference_fields}[0]) : '';
- my $fk_sql = "FOREIGN KEY($field) REFERENCES ";
- $fk_sql .= "$table($ref)";
+ my @fields = $c->fields;
+ my @rfields = map { $_ || () } $c->reference_fields;
+ unless ( @rfields ) {
+ my $rtable_name = $c->reference_table;
+ if ( my $ref_table = $c->schema->get_table( $rtable_name ) ) {
+ push @rfields, $ref_table->primary_key;
+
+ die "FK constraint on " . $rtable_name . '.' . join('', @fields) . " has no reference fields\n"
+ unless @rfields;
+ }
+ else {
+ die "Can't find reference table '$rtable_name' in schema\n";
+ }
+ }
+
+ my $fk_sql = sprintf 'FOREIGN KEY (%s) REFERENCES %s(%s)',
+ join (', ', map { $util->quote($_) } @fields ),
+ $util->quote($c->reference_table),
+ join (', ', map { $util->quote($_) } @rfields )
+ ;
+
+ $fk_sql .= " ON DELETE " . $c->{on_delete} if $c->{on_delete};
+ $fk_sql .= " ON UPDATE " . $c->{on_update} if $c->{on_update};
return $fk_sql;
}
use SQL::Translator::Schema::Constants;
BEGIN {
- maybe_plan(19,
+ maybe_plan(21,
'SQL::Translator::Parser::SQLite');
}
SQL::Translator::Parser::SQLite->import('parse');
is( $c1->type, 'FOREIGN KEY', 'FK constraint' );
is( $c1->reference_table, 'person', 'References person table' );
is( $c1->name, 'fk_person_id', 'Constraint name fk_person_id' );
+ is( $c1->on_delete, 'RESTRICT', 'On delete restrict' );
+ is( $c1->on_update, 'CASCADE', 'On update cascade' );
is( join(',', $c1->reference_fields), 'person_id',
'References person_id field' );
'position' varchar(50) NOT NULL,
'employee_id' int(11) NOT NULL,
PRIMARY KEY ('position', 'employee_id'),
- FOREIGN KEY('employee_id') REFERENCES 'person'('person_id')
+ FOREIGN KEY ('employee_id') REFERENCES 'person'('person_id')
);
INSERT INTO 'employee_temp_alter' SELECT 'position', 'employee_id' FROM 'employee';
'position' varchar(50) NOT NULL,
'employee_id' int(11) NOT NULL,
PRIMARY KEY ('position', 'employee_id'),
- FOREIGN KEY('employee_id') REFERENCES 'person'('person_id')
+ FOREIGN KEY ('employee_id') REFERENCES 'person'('person_id')
);
INSERT INTO 'employee' SELECT 'position', 'employee_id' FROM 'employee_temp_alter';
'emptytagdef' varchar DEFAULT '',
'another_id' int(10) DEFAULT 2,
'timest' timestamp,
- FOREIGN KEY('another_id') REFERENCES 'Another'()
+ FOREIGN KEY ('another_id') REFERENCES 'Another'('id')
);
CREATE INDEX 'titleindex' ON 'Basic' ('title');
'emptytagdef' varchar DEFAULT '',
'another_id' int(10) DEFAULT 2,
'timest' timestamp,
- FOREIGN KEY('another_id') REFERENCES 'Another'()
+ FOREIGN KEY ('another_id') REFERENCES 'Another'('id')
)>,
q<CREATE INDEX 'titleindex' ON 'Basic' ('title')>,
q<CREATE UNIQUE INDEX 'emailuniqueindex' ON 'Basic' ('email')>,
# vim: set ft=perl:
use strict;
-use Test::More tests => 2;
+use Test::More tests => 3;
use Test::SQL::Translator qw(maybe_plan);
use FindBin qw/$Bin/;
use SQL::Translator::Schema::View;
+use SQL::Translator::Schema::Table;
use SQL::Translator::Producer::SQLite;
{
SELECT id, name FROM thing" ];
is_deeply($view1_sql2, $view_sql_noreplace, 'correct "CREATE VIEW" SQL');
}
+{
+ my $create_opts;
+
+ my $table = SQL::Translator::Schema::Table->new(
+ name => 'foo_table',
+ );
+ $table->add_field(
+ name => 'foreign_key',
+ data_type => 'integer',
+ );
+ my $constraint = SQL::Translator::Schema::Constraint->new(
+ table => $table,
+ name => 'fk',
+ type => 'FOREIGN_KEY',
+ fields => ['foreign_key'],
+ reference_fields => ['id'],
+ reference_table => 'foo',
+ on_delete => 'RESTRICT',
+ on_update => 'CASCADE',
+ );
+
+ my $expected = [ "FOREIGN KEY ('foreign_key') REFERENCES 'foo'('id') ON DELETE RESTRICT ON UPDATE CASCADE"];
+ my $result = [SQL::Translator::Producer::SQLite::create_foreignkey($constraint,$create_opts)];
+ is_deeply($result, $expected, 'correct "FOREIGN KEY"');
+}
create table pet (
"pet_id" int,
"person_id" int
- constraint fk_person_id references person(person_id),
+ constraint fk_person_id references person(person_id) on update CASCADE on delete RESTRICT,
"name" varchar(30),
"age" int,
constraint age_under_100 check ( age < 100 ),