+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*** INCOMPATIBLE CHANGES:
* SQLT no longer supports setting separate conflicting values for the now
deprecated 'quote_table_names' and 'quote_field_names'. Instead their values
specified the default is TRUE as before. If only one is specified - default
to its value for everything, and if both are specified with a conflicting
value an exception is thrown.
-
+* Partial quoting support has been added in SQLite. It is currently disabled by
+ default, you need to request is explicitly with quote_identifiers => 1. In a
+ future version of SQL::Translator *THIS DEFAULT BEHAVIOR WILL CHANGE*.
+ If you do NOT WANT quoting, set quote_identifiers to a false value to
+ protect yourself from changes in a future release.
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* 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
* Fix data preservation on SQLite diffs involving adding/dropping columns
* Support for triggers in PostgreSQL producer and parser
* Correct Data Type in SQLT::Parser::DBI::PostgreSQL (patch from Andrew Pam)
}
if (my @tables = @{ $self->tables_to_create } ) {
- my $translator = new SQL::Translator(
+ my $translator = SQL::Translator->new(
producer_type => $self->output_db,
add_drop_table => 0,
no_comments => 1,
use Moo;
use SQL::Translator::Generator::Utils;
+has quote_chars => (is=>'ro', default=>sub { +[qw(" ")] } );
+
with 'SQL::Translator::Generator::Role::Quote';
with 'SQL::Translator::Generator::Role::DDL';
-sub quote_chars { [qw(" ")] }
sub name_sep { q(.) }
sub _build_type_map {
use Data::Dumper;
use SQL::Translator::Schema::Constants;
use SQL::Translator::Utils qw(debug header_comment parse_dbms_version);
-use SQL::Translator::Generator::Utils;
-my $util = SQL::Translator::Generator::Utils->new( quote_chars => q(") );
use SQL::Translator::Generator::DDL::SQLite;
our ( $DEBUG, $WARN );
our $max_id_length = 30;
my %global_names;
-my $future = SQL::Translator::Generator::DDL::SQLite->new();
+
+# HIDEOUS TEMPORARY DEFAULT WITHOUT QUOTING!
+our $NO_QUOTES = 1;
+{
+
+ my ($quoting_generator, $nonquoting_generator);
+ sub _generator {
+ $NO_QUOTES
+ ? $nonquoting_generator ||= SQL::Translator::Generator::DDL::SQLite->new(quote_chars => [])
+ : $quoting_generator ||= SQL::Translator::Generator::DDL::SQLite->new
+ }
+}
sub produce {
my $translator = shift;
%global_names = (); #reset
+ # only quote if quotes were requested for real
+ # 0E0 indicates "the default of true" was assumed
+ local $NO_QUOTES = 0
+ if $translator->quote_identifiers and $translator->quote_identifiers ne '0E0';
my $head = (header_comment() . "\n") unless $no_comments;
}
$scope->{ $name }++;
- return $util->quote($name);
+ return _generator()->quote($name);
}
sub create_view {
my ($view, $options) = @_;
my $add_drop_view = $options->{add_drop_view};
- my $view_name = $util->quote($view->name);
+ my $view_name = _generator()->quote($view->name);
$global_names{$view->name} = 1;
debug("PKG: Looking at view '${view_name}'\n");
{
my ($table, $options) = @_;
- my $table_name = $util->quote($table->name);
+ my $table_name = _generator()->quote($table->name);
$global_names{$table->name} = 1;
my $no_comments = $options->{no_comments};
||
( @pk_fields && !grep /INTEGER PRIMARY KEY/, @field_defs )
) {
- push @field_defs, 'PRIMARY KEY (' . join(', ', map $util->quote($_), @pk_fields ) . ')';
+ push @field_defs, 'PRIMARY KEY (' . join(', ', map _generator()->quote($_), @pk_fields ) . ')';
}
#
}
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 )
+ join (', ', map { _generator()->quote($_) } @fields ),
+ _generator()->quote($c->reference_table),
+ join (', ', map { _generator()->quote($_) } @rfields )
;
$fk_sql .= " ON DELETE " . $c->{on_delete} if $c->{on_delete};
return $fk_sql;
}
-sub create_field { return $future->field($_[0]) }
+sub create_field { return _generator()->field($_[0]) }
sub create_index
{
my $type = $index->type eq 'UNIQUE' ? "UNIQUE " : '';
# strip any field size qualifiers as SQLite doesn't like these
- my @fields = map { s/\(\d+\)$//; $util->quote($_) } $index->fields;
+ my @fields = map { s/\(\d+\)$//; _generator()->quote($_) } $index->fields;
(my $index_table_name = $index->table->name) =~ s/^.+?\.//; # table name may not specify schema
- $index_table_name = $util->quote($index_table_name);
+ $index_table_name = _generator()->quote($index_table_name);
warn "removing schema name from '" . $index->table->name . "' to make '$index_table_name'\n" if $WARN;
my $index_def =
"CREATE ${type}INDEX $name ON " . $index_table_name .
my $name = $c->name;
$name = mk_name($name);
- my @fields = map $util->quote($_), $c->fields;
+ my @fields = map _generator()->quote($_), $c->fields;
(my $index_table_name = $c->table->name) =~ s/^.+?\.//; # table name may not specify schema
- $index_table_name = $util->quote($index_table_name);
+ $index_table_name = _generator()->quote($index_table_name);
warn "removing schema name from '" . $c->table->name . "' to make '$index_table_name'\n" if $WARN;
my $c_def =
"creating trigger '$trig_name' for the '$evt' event.\n" if $WARN;
}
- $trig_name = $util->quote($trig_name);
+ $trig_name = _generator()->quote($trig_name);
push @statements, "DROP TRIGGER IF EXISTS $trig_name" if $add_drop;
$trig_name,
$trigger->perform_action_when,
$evt,
- $util->quote($trigger->on_table),
+ _generator()->quote($trigger->on_table),
$action
);
}
my ($field) = @_;
return sprintf("ALTER TABLE %s ADD COLUMN %s",
- $util->quote($field->table->name), create_field($field))
+ _generator()->quote($field->table->name), create_field($field))
}
sub alter_create_index {
my ($constraint) = @_;
return sprintf("DROP INDEX %s",
- $util->quote($constraint->name));
+ _generator()->quote($constraint->name));
}
sub batch_alter_table {
%temp_table_fields = map { $_ => 1} $table->get_fields;
};
- push @sql, "INSERT INTO @{[$util->quote($table_name.'_temp_alter')]}( @{[ join(', ', map $util->quote($_), grep { $temp_table_fields{$_} } $old_table->get_fields)]}) SELECT @{[ join(', ', map $util->quote($_), grep { $temp_table_fields{$_} } $old_table->get_fields)]} FROM @{[$util->quote($old_table)]}",
- "DROP TABLE @{[$util->quote($old_table)]}",
+ push @sql, "INSERT INTO @{[_generator()->quote($table_name.'_temp_alter')]}( @{[ join(', ', map _generator()->quote($_), grep { $temp_table_fields{$_} } $old_table->get_fields)]}) SELECT @{[ join(', ', map _generator()->quote($_), grep { $temp_table_fields{$_} } $old_table->get_fields)]} FROM @{[_generator()->quote($old_table)]}",
+ "DROP TABLE @{[_generator()->quote($old_table)]}",
create_table($table, { no_comments => 1 }),
- "INSERT INTO @{[$util->quote($table_name)]} SELECT @{[ join(', ', map $util->quote($_), $table->get_fields)]} FROM @{[$util->quote($table_name.'_temp_alter')]}",
- "DROP TABLE @{[$util->quote($table_name.'_temp_alter')]}";
+ "INSERT INTO @{[_generator()->quote($table_name)]} SELECT @{[ join(', ', map _generator()->quote($_), $table->get_fields)]} FROM @{[_generator()->quote($table_name.'_temp_alter')]}",
+ "DROP TABLE @{[_generator()->quote($table_name.'_temp_alter')]}";
return @sql;
# return join("", @sql, "");
}
sub drop_table {
my ($table) = @_;
- $table = $util->quote($table);
+ $table = _generator()->quote($table);
return "DROP TABLE $table";
}
sub rename_table {
my ($old_table, $new_table, $options) = @_;
- $old_table = $util->quote($old_table);
- $new_table = $util->quote($new_table);
+ $old_table = _generator()->quote($old_table);
+ $new_table = _generator()->quote($new_table);
return "ALTER TABLE $old_table RENAME TO $new_table";
BEGIN;
-CREATE TABLE "added" (
- "id" int(11)
+CREATE TABLE added (
+ id int(11)
);
-ALTER TABLE "old_name" RENAME TO "new_name";
+ALTER TABLE old_name RENAME TO new_name;
-DROP INDEX "FK5302D47D93FE702E";
+DROP INDEX FK5302D47D93FE702E;
-DROP INDEX "UC_age_name";
+DROP INDEX UC_age_name;
-DROP INDEX "u_name";
+DROP INDEX u_name;
-- SQL::Translator::Producer::SQLite cant drop_field;
-ALTER TABLE "new_name" ADD COLUMN "new_field" int;
+ALTER TABLE new_name ADD COLUMN new_field int;
-ALTER TABLE "person" ADD COLUMN "is_rock_star" tinyint(4) DEFAULT 1;
+ALTER TABLE person ADD COLUMN is_rock_star tinyint(4) DEFAULT 1;
-- SQL::Translator::Producer::SQLite cant alter_field;
-- SQL::Translator::Producer::SQLite cant rename_field;
-CREATE UNIQUE INDEX "unique_name" ON "person" ("name");
+CREATE UNIQUE INDEX unique_name ON person (name);
-CREATE UNIQUE INDEX "UC_person_id" ON "person" ("person_id");
+CREATE UNIQUE INDEX UC_person_id ON person (person_id);
-CREATE UNIQUE INDEX "UC_age_name" ON "person" ("age", "name");
+CREATE UNIQUE INDEX UC_age_name ON person (age, name);
-DROP TABLE "deleted";
+DROP TABLE deleted;
COMMIT;
BEGIN;
-CREATE TABLE "added" (
- "id" int(11)
+CREATE TABLE added (
+ id int(11)
);
-CREATE TEMPORARY TABLE "employee_temp_alter" (
- "position" varchar(50) NOT NULL,
- "employee_id" int(11) NOT NULL,
- PRIMARY KEY ("position", "employee_id"),
- FOREIGN KEY ("employee_id") REFERENCES "person"("person_id")
+CREATE TEMPORARY TABLE employee_temp_alter (
+ position varchar(50) NOT NULL,
+ employee_id int(11) NOT NULL,
+ PRIMARY KEY (position, employee_id),
+ FOREIGN KEY (employee_id) REFERENCES person(person_id)
);
-INSERT INTO "employee_temp_alter"( "position", "employee_id") SELECT "position", "employee_id" FROM "employee";
+INSERT INTO employee_temp_alter( position, employee_id) SELECT position, employee_id FROM employee;
-DROP TABLE "employee";
+DROP TABLE employee;
-CREATE TABLE "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")
+CREATE TABLE 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)
);
-INSERT INTO "employee" SELECT "position", "employee_id" FROM "employee_temp_alter";
+INSERT INTO employee SELECT position, employee_id FROM employee_temp_alter;
-DROP TABLE "employee_temp_alter";
+DROP TABLE employee_temp_alter;
-ALTER TABLE "old_name" RENAME TO "new_name";
+ALTER TABLE old_name RENAME TO new_name;
-ALTER TABLE "new_name" ADD COLUMN "new_field" int;
+ALTER TABLE new_name ADD COLUMN new_field int;
-CREATE TEMPORARY TABLE "person_temp_alter" (
- "person_id" INTEGER PRIMARY KEY NOT NULL,
- "name" varchar(20) NOT NULL,
- "age" int(11) DEFAULT 18,
- "weight" double(11,2),
- "iq" int(11) DEFAULT 0,
- "is_rock_star" tinyint(4) DEFAULT 1,
- "physical_description" text
+CREATE TEMPORARY TABLE person_temp_alter (
+ person_id INTEGER PRIMARY KEY NOT NULL,
+ name varchar(20) NOT NULL,
+ age int(11) DEFAULT 18,
+ weight double(11,2),
+ iq int(11) DEFAULT 0,
+ is_rock_star tinyint(4) DEFAULT 1,
+ physical_description text
);
-INSERT INTO "person_temp_alter"( "person_id", "name", "age", "weight", "iq", "is_rock_star", "physical_description") SELECT "person_id", "name", "age", "weight", "iq", "is_rock_star", "physical_description" FROM "person";
+INSERT INTO person_temp_alter( person_id, name, age, weight, iq, is_rock_star, physical_description) SELECT person_id, name, age, weight, iq, is_rock_star, physical_description FROM person;
-DROP TABLE "person";
+DROP TABLE person;
-CREATE TABLE "person" (
- "person_id" INTEGER PRIMARY KEY NOT NULL,
- "name" varchar(20) NOT NULL,
- "age" int(11) DEFAULT 18,
- "weight" double(11,2),
- "iq" int(11) DEFAULT 0,
- "is_rock_star" tinyint(4) DEFAULT 1,
- "physical_description" text
+CREATE TABLE person (
+ person_id INTEGER PRIMARY KEY NOT NULL,
+ name varchar(20) NOT NULL,
+ age int(11) DEFAULT 18,
+ weight double(11,2),
+ iq int(11) DEFAULT 0,
+ is_rock_star tinyint(4) DEFAULT 1,
+ physical_description text
);
-CREATE UNIQUE INDEX "unique_name02" ON "person" ("name");
+CREATE UNIQUE INDEX unique_name02 ON person (name);
-CREATE UNIQUE INDEX "UC_person_id02" ON "person" ("person_id");
+CREATE UNIQUE INDEX UC_person_id02 ON person (person_id);
-CREATE UNIQUE INDEX "UC_age_name02" ON "person" ("age", "name");
+CREATE UNIQUE INDEX UC_age_name02 ON person (age, name);
-INSERT INTO "person" SELECT "person_id", "name", "age", "weight", "iq", "is_rock_star", "physical_description" FROM "person_temp_alter";
+INSERT INTO person SELECT person_id, name, age, weight, iq, is_rock_star, physical_description FROM person_temp_alter;
-DROP TABLE "person_temp_alter";
+DROP TABLE person_temp_alter;
-DROP TABLE "deleted";
+DROP TABLE deleted;
COMMIT;
my $sqlt;
$sqlt = SQL::Translator->new(
+ quote_identifiers => 1,
no_comments => 1,
show_warnings => 0,
add_drop_table => 1,
use SQL::Translator::Schema::View;
use SQL::Translator::Schema::Table;
use SQL::Translator::Producer::SQLite;
+$SQL::Translator::Producer::SQLite::NO_QUOTES = 0;
{
my $view1 = SQL::Translator::Schema::View->new( name => 'view_foo',
my $view1_sql1 =
[ SQL::Translator::Producer::SQLite::create_view( $view1, $create_opts ) ];
- my $view_sql_replace = [ 'CREATE TEMPORARY VIEW IF NOT EXISTS "view_foo" AS
+ my $view_sql_replace = [ 'CREATE TEMPORARY VIEW IF NOT EXISTS view_foo AS
SELECT id, name FROM thing' ];
is_deeply( $view1_sql1, $view_sql_replace, 'correct "CREATE TEMPORARY VIEW" SQL' );
my $view1_sql2 =
[ SQL::Translator::Producer::SQLite::create_view( $view2, $create_opts ) ];
- my $view_sql_noreplace = [ 'CREATE VIEW "view_foo" AS
+ my $view_sql_noreplace = [ 'CREATE VIEW view_foo AS
SELECT id, name FROM thing' ];
is_deeply( $view1_sql2, $view_sql_noreplace, 'correct "CREATE VIEW" SQL' );
}
BEGIN;
-CREATE TEMPORARY TABLE "Foo_temp_alter" (
- "foo" INTEGER PRIMARY KEY NOT NULL,
- "bar" VARCHAR(10) NOT NULL,
- "baz" VARCHAR(10),
- "doomed" VARCHAR(10)
+CREATE TEMPORARY TABLE Foo_temp_alter (
+ foo INTEGER PRIMARY KEY NOT NULL,
+ bar VARCHAR(10) NOT NULL,
+ baz VARCHAR(10),
+ doomed VARCHAR(10)
);
-INSERT INTO "Foo_temp_alter"( "foo", "bar") SELECT "foo", "bar" FROM "Foo";
+INSERT INTO Foo_temp_alter( foo, bar) SELECT foo, bar FROM Foo;
-DROP TABLE "Foo";
+DROP TABLE Foo;
-CREATE TABLE "Foo" (
- "foo" INTEGER PRIMARY KEY NOT NULL,
- "bar" VARCHAR(10) NOT NULL,
- "baz" VARCHAR(10),
- "doomed" VARCHAR(10)
+CREATE TABLE Foo (
+ foo INTEGER PRIMARY KEY NOT NULL,
+ bar VARCHAR(10) NOT NULL,
+ baz VARCHAR(10),
+ doomed VARCHAR(10)
);
-INSERT INTO "Foo" SELECT "foo", "bar", "baz", "doomed" FROM "Foo_temp_alter";
+INSERT INTO Foo SELECT foo, bar, baz, doomed FROM Foo_temp_alter;
-DROP TABLE "Foo_temp_alter";
+DROP TABLE Foo_temp_alter;
COMMIT;
--- /dev/null
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+use Test::Exception;
+use Test::Differences;
+use SQL::Translator;
+use SQL::Translator::Parser::SQLite;
+use SQL::Translator::Diff;
+
+my $ddl = <<DDL;
+CREATE TABLE "Foo" (
+ "foo" INTEGER PRIMARY KEY NOT NULL,
+ "bar" VARCHAR(10) NOT NULL,
+ "biff" VARCHAR(10)
+);
+DDL
+
+my %common_args = (
+ no_comments => 1,
+ from => 'SQLite',
+ to => 'SQLite');
+
+my $unquoted = SQL::Translator
+ ->new(%common_args)
+ ->translate(\$ddl);
+
+eq_or_diff($unquoted, <<'DDL', 'DDL with default quoting');
+BEGIN TRANSACTION;
+
+CREATE TABLE Foo (
+ foo INTEGER PRIMARY KEY NOT NULL,
+ bar VARCHAR(10) NOT NULL,
+ biff VARCHAR(10)
+);
+
+COMMIT;
+DDL
+
+dies_ok { SQL::Translator
+ ->new(%common_args, quote_table_names=>0, quote_field_names => 1)
+ ->translate(\$ddl) } 'mix and match quotes is asinine';
+
+my $quoteall = SQL::Translator
+ ->new(%common_args, quote_identifiers=>1)
+ ->translate(\$ddl);
+
+eq_or_diff($quoteall, <<'DDL', 'DDL with quoting');
+BEGIN TRANSACTION;
+
+CREATE TABLE "Foo" (
+ "foo" INTEGER PRIMARY KEY NOT NULL,
+ "bar" VARCHAR(10) NOT NULL,
+ "biff" VARCHAR(10)
+);
+
+COMMIT;
+DDL
+
+=begin FOR TODO
+
+# FIGURE OUT HOW TO DO QUOTED DIFFS EVEN WHEN QUOTING IS DEFAULT OFF
+#
+
+eq_or_diff($upgrade_sql, <<'## END OF DIFF', "Diff as expected");
+-- Convert schema '' to '':;
+
+BEGIN;
+
+CREATE TEMPORARY TABLE "Foo_temp_alter" (
+ "foo" INTEGER PRIMARY KEY NOT NULL,
+ "bar" VARCHAR(10) NOT NULL,
+ "baz" VARCHAR(10),
+ "doomed" VARCHAR(10)
+);
+
+INSERT INTO "Foo_temp_alter"( "foo", "bar") SELECT "foo", "bar" FROM "Foo";
+
+DROP TABLE "Foo";
+
+CREATE TABLE "Foo" (
+ "foo" INTEGER PRIMARY KEY NOT NULL,
+ "bar" VARCHAR(10) NOT NULL,
+ "baz" VARCHAR(10),
+ "doomed" VARCHAR(10)
+);
+
+INSERT INTO "Foo" SELECT "foo", "bar", "baz", "doomed" FROM "Foo_temp_alter";
+
+DROP TABLE "Foo_temp_alter";
+
+
+COMMIT;
+
+## END OF DIFF
+
+=cut
+
+