line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package SQL::Translator::Producer::SQLite; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
SQL::Translator::Producer::SQLite - SQLite producer for SQL::Translator |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use SQL::Translator; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
my $t = SQL::Translator->new( parser => '...', producer => 'SQLite' ); |
12
|
|
|
|
|
|
|
$t->translate; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 DESCRIPTION |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
This module will produce text output of the schema suitable for SQLite. |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=cut |
19
|
|
|
|
|
|
|
|
20
|
8
|
|
|
8
|
|
5445
|
use strict; |
|
8
|
|
|
|
|
21
|
|
|
8
|
|
|
|
|
377
|
|
21
|
8
|
|
|
8
|
|
50
|
use warnings; |
|
8
|
|
|
|
|
25
|
|
|
8
|
|
|
|
|
229
|
|
22
|
8
|
|
|
8
|
|
1350
|
use Data::Dumper; |
|
8
|
|
|
|
|
14101
|
|
|
8
|
|
|
|
|
471
|
|
23
|
8
|
|
|
8
|
|
59
|
use SQL::Translator::Schema::Constants; |
|
8
|
|
|
|
|
19
|
|
|
8
|
|
|
|
|
658
|
|
24
|
8
|
|
|
8
|
|
56
|
use SQL::Translator::Utils qw(debug header_comment parse_dbms_version batch_alter_table_statements); |
|
8
|
|
|
|
|
21
|
|
|
8
|
|
|
|
|
560
|
|
25
|
8
|
|
|
8
|
|
3744
|
use SQL::Translator::Generator::DDL::SQLite; |
|
8
|
|
|
|
|
28
|
|
|
8
|
|
|
|
|
26866
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
our ( $DEBUG, $WARN ); |
28
|
|
|
|
|
|
|
our $VERSION = '1.63'; |
29
|
|
|
|
|
|
|
$DEBUG = 0 unless defined $DEBUG; |
30
|
|
|
|
|
|
|
$WARN = 0 unless defined $WARN; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
our $max_id_length = 30; |
33
|
|
|
|
|
|
|
my %global_names; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# HIDEOUS TEMPORARY DEFAULT WITHOUT QUOTING! |
36
|
|
|
|
|
|
|
our $NO_QUOTES = 1; |
37
|
|
|
|
|
|
|
{ |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
my ($quoting_generator, $nonquoting_generator); |
40
|
|
|
|
|
|
|
sub _generator { |
41
|
304
|
100
|
66
|
304
|
|
2729
|
$NO_QUOTES |
|
|
|
66
|
|
|
|
|
42
|
|
|
|
|
|
|
? $nonquoting_generator ||= SQL::Translator::Generator::DDL::SQLite->new(quote_chars => []) |
43
|
|
|
|
|
|
|
: $quoting_generator ||= SQL::Translator::Generator::DDL::SQLite->new |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub produce { |
48
|
7
|
|
|
7
|
0
|
20
|
my $translator = shift; |
49
|
7
|
|
|
|
|
39
|
local $DEBUG = $translator->debug; |
50
|
7
|
|
|
|
|
207
|
local $WARN = $translator->show_warnings; |
51
|
7
|
|
|
|
|
216
|
my $no_comments = $translator->no_comments; |
52
|
7
|
|
|
|
|
189
|
my $add_drop_table = $translator->add_drop_table; |
53
|
7
|
|
|
|
|
244
|
my $schema = $translator->schema; |
54
|
7
|
|
|
|
|
214
|
my $producer_args = $translator->producer_args; |
55
|
|
|
|
|
|
|
my $sqlite_version = parse_dbms_version( |
56
|
7
|
|
|
|
|
51
|
$producer_args->{sqlite_version}, 'perl' |
57
|
|
|
|
|
|
|
); |
58
|
7
|
|
|
|
|
27
|
my $no_txn = $producer_args->{no_transaction}; |
59
|
|
|
|
|
|
|
|
60
|
7
|
|
|
|
|
34
|
debug("PKG: Beginning production\n"); |
61
|
|
|
|
|
|
|
|
62
|
7
|
|
|
|
|
33
|
%global_names = (); #reset |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# only quote if quotes were requested for real |
65
|
|
|
|
|
|
|
# 0E0 indicates "the default of true" was assumed |
66
|
7
|
100
|
66
|
|
|
174
|
local $NO_QUOTES = 0 |
67
|
|
|
|
|
|
|
if $translator->quote_identifiers and $translator->quote_identifiers ne '0E0'; |
68
|
|
|
|
|
|
|
|
69
|
7
|
|
|
|
|
109
|
my $head; |
70
|
7
|
100
|
|
|
|
33
|
$head = (header_comment() . "\n") unless $no_comments; |
71
|
|
|
|
|
|
|
|
72
|
7
|
|
|
|
|
26
|
my @create = (); |
73
|
|
|
|
|
|
|
|
74
|
7
|
100
|
|
|
|
111
|
push @create, "BEGIN TRANSACTION" unless $no_txn; |
75
|
|
|
|
|
|
|
|
76
|
7
|
|
|
|
|
44
|
for my $table ( $schema->get_tables ) { |
77
|
13
|
|
|
|
|
96
|
push @create, create_table($table, { no_comments => $no_comments, |
78
|
|
|
|
|
|
|
sqlite_version => $sqlite_version, |
79
|
|
|
|
|
|
|
add_drop_table => $add_drop_table,}); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
7
|
|
|
|
|
55
|
for my $view ( $schema->get_views ) { |
83
|
2
|
|
|
|
|
14
|
push @create, create_view($view, { |
84
|
|
|
|
|
|
|
add_drop_view => $add_drop_table, |
85
|
|
|
|
|
|
|
no_comments => $no_comments, |
86
|
|
|
|
|
|
|
}); |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
7
|
|
|
|
|
44
|
for my $trigger ( $schema->get_triggers ) { |
90
|
7
|
|
|
|
|
34
|
push @create, create_trigger($trigger, { |
91
|
|
|
|
|
|
|
add_drop_trigger => $add_drop_table, |
92
|
|
|
|
|
|
|
no_comments => $no_comments, |
93
|
|
|
|
|
|
|
}); |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
7
|
100
|
|
|
|
35
|
push @create, "COMMIT" unless $no_txn; |
97
|
|
|
|
|
|
|
|
98
|
7
|
100
|
|
|
|
26
|
if (wantarray) { |
99
|
2
|
|
33
|
|
|
16
|
return ($head||(), @create); |
100
|
|
|
|
|
|
|
} else { |
101
|
5
|
|
66
|
|
|
77
|
return join ('', |
102
|
|
|
|
|
|
|
$head||(), |
103
|
|
|
|
|
|
|
join(";\n\n", @create ), |
104
|
|
|
|
|
|
|
";\n", |
105
|
|
|
|
|
|
|
); |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub mk_name { |
110
|
22
|
|
|
22
|
0
|
153
|
my ($name, $scope, $critical) = @_; |
111
|
|
|
|
|
|
|
|
112
|
22
|
|
50
|
|
|
109
|
$scope ||= \%global_names; |
113
|
22
|
100
|
|
|
|
74
|
if ( my $prev = $scope->{ $name } ) { |
114
|
5
|
|
|
|
|
20
|
my $name_orig = $name; |
115
|
5
|
|
|
|
|
25
|
$name .= sprintf( "%02d", ++$prev ); |
116
|
5
|
50
|
|
|
|
21
|
substr($name, $max_id_length - 3) = "00" |
117
|
|
|
|
|
|
|
if length( $name ) > $max_id_length; |
118
|
|
|
|
|
|
|
|
119
|
5
|
50
|
|
|
|
24
|
warn "The name '$name_orig' has been changed to ", |
120
|
|
|
|
|
|
|
"'$name' to make it unique.\n" if $WARN; |
121
|
|
|
|
|
|
|
|
122
|
5
|
|
|
|
|
17
|
$scope->{ $name_orig }++; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
22
|
|
|
|
|
90
|
$scope->{ $name }++; |
126
|
22
|
|
|
|
|
53
|
return _generator()->quote($name); |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub create_view { |
130
|
6
|
|
|
6
|
0
|
239
|
my ($view, $options) = @_; |
131
|
6
|
|
|
|
|
19
|
my $add_drop_view = $options->{add_drop_view}; |
132
|
|
|
|
|
|
|
|
133
|
6
|
|
|
|
|
21
|
my $view_name = _generator()->quote($view->name); |
134
|
6
|
|
|
|
|
36
|
$global_names{$view->name} = 1; |
135
|
|
|
|
|
|
|
|
136
|
6
|
|
|
|
|
39
|
debug("PKG: Looking at view '${view_name}'\n"); |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# Header. Should this look like what mysqldump produces? |
139
|
6
|
|
|
|
|
157
|
my $extra = $view->extra; |
140
|
6
|
|
|
|
|
12
|
my @create; |
141
|
6
|
100
|
|
|
|
22
|
push @create, "DROP VIEW IF EXISTS $view_name" if $add_drop_view; |
142
|
|
|
|
|
|
|
|
143
|
6
|
|
|
|
|
18
|
my $create_view = 'CREATE'; |
144
|
6
|
50
|
66
|
|
|
29
|
$create_view .= " TEMPORARY" if exists($extra->{temporary}) && $extra->{temporary}; |
145
|
6
|
|
|
|
|
17
|
$create_view .= ' VIEW'; |
146
|
6
|
50
|
66
|
|
|
30
|
$create_view .= " IF NOT EXISTS" if exists($extra->{if_not_exists}) && $extra->{if_not_exists}; |
147
|
6
|
|
|
|
|
15
|
$create_view .= " ${view_name}"; |
148
|
|
|
|
|
|
|
|
149
|
6
|
50
|
|
|
|
27
|
if( my $sql = $view->sql ){ |
150
|
6
|
|
|
|
|
19
|
$create_view .= " AS\n ${sql}"; |
151
|
|
|
|
|
|
|
} |
152
|
6
|
|
|
|
|
14
|
push @create, $create_view; |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# Tack the comment onto the first statement. |
155
|
6
|
50
|
|
|
|
20
|
unless ($options->{no_comments}) { |
156
|
0
|
|
|
|
|
0
|
$create[0] = "--\n-- View: ${view_name}\n--\n" . $create[0]; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
6
|
|
|
|
|
26
|
return @create; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub create_table |
164
|
|
|
|
|
|
|
{ |
165
|
25
|
|
|
25
|
0
|
134
|
my ($table, $options) = @_; |
166
|
|
|
|
|
|
|
|
167
|
25
|
|
|
|
|
70
|
my $table_name = _generator()->quote($table->name); |
168
|
25
|
|
|
|
|
519
|
$global_names{$table->name} = 1; |
169
|
|
|
|
|
|
|
|
170
|
25
|
|
|
|
|
524
|
my $no_comments = $options->{no_comments}; |
171
|
25
|
|
|
|
|
57
|
my $add_drop_table = $options->{add_drop_table}; |
172
|
25
|
|
50
|
|
|
119
|
my $sqlite_version = $options->{sqlite_version} || 0; |
173
|
|
|
|
|
|
|
|
174
|
25
|
|
|
|
|
124
|
debug("PKG: Looking at table '$table_name'\n"); |
175
|
|
|
|
|
|
|
|
176
|
25
|
|
|
|
|
56
|
my ( @index_defs, @constraint_defs ); |
177
|
25
|
50
|
|
|
|
94
|
my @fields = $table->get_fields or die "No fields in $table_name"; |
178
|
|
|
|
|
|
|
|
179
|
25
|
100
|
|
|
|
104
|
my $temp = $options->{temporary_table} ? 'TEMPORARY ' : ''; |
180
|
|
|
|
|
|
|
# |
181
|
|
|
|
|
|
|
# Header. |
182
|
|
|
|
|
|
|
# |
183
|
25
|
50
|
|
|
|
90
|
my $exists = ($sqlite_version >= 3.003) ? ' IF EXISTS' : ''; |
184
|
25
|
|
|
|
|
61
|
my @create; |
185
|
25
|
|
|
|
|
55
|
my ($comment, $create_table) = ""; |
186
|
25
|
100
|
|
|
|
66
|
$comment = "--\n-- Table: $table_name\n--\n" unless $no_comments; |
187
|
25
|
100
|
|
|
|
59
|
if ($add_drop_table) { |
188
|
8
|
|
|
|
|
32
|
push @create, $comment . qq[DROP TABLE$exists $table_name]; |
189
|
|
|
|
|
|
|
} else { |
190
|
17
|
|
|
|
|
38
|
$create_table = $comment; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
25
|
|
|
|
|
85
|
$create_table .= "CREATE ${temp}TABLE $table_name (\n"; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# |
196
|
|
|
|
|
|
|
# Comments |
197
|
|
|
|
|
|
|
# |
198
|
25
|
50
|
33
|
|
|
533
|
if ( $table->comments and !$no_comments ){ |
199
|
0
|
|
|
|
|
0
|
$create_table .= "-- Comments: \n-- "; |
200
|
0
|
|
|
|
|
0
|
$create_table .= join "\n-- ", $table->comments; |
201
|
0
|
|
|
|
|
0
|
$create_table .= "\n--\n\n"; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# |
205
|
|
|
|
|
|
|
# How many fields in PK? |
206
|
|
|
|
|
|
|
# |
207
|
25
|
|
|
|
|
108
|
my $pk = $table->primary_key; |
208
|
25
|
100
|
|
|
|
471
|
my @pk_fields = $pk ? $pk->fields : (); |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# |
211
|
|
|
|
|
|
|
# Fields |
212
|
|
|
|
|
|
|
# |
213
|
25
|
|
|
|
|
68
|
my ( @field_defs, $pk_set ); |
214
|
25
|
|
|
|
|
59
|
for my $field ( @fields ) { |
215
|
83
|
|
|
|
|
218
|
push @field_defs, create_field($field); |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
25
|
100
|
66
|
|
|
243
|
if ( |
|
|
|
66
|
|
|
|
|
219
|
|
|
|
|
|
|
scalar @pk_fields > 1 |
220
|
|
|
|
|
|
|
|| |
221
|
|
|
|
|
|
|
( @pk_fields && !grep /INTEGER PRIMARY KEY/, @field_defs ) |
222
|
|
|
|
|
|
|
) { |
223
|
2
|
|
|
|
|
8
|
push @field_defs, 'PRIMARY KEY (' . join(', ', map _generator()->quote($_), @pk_fields ) . ')'; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# |
227
|
|
|
|
|
|
|
# Indices |
228
|
|
|
|
|
|
|
# |
229
|
25
|
|
|
|
|
136
|
for my $index ( $table->get_indices ) { |
230
|
8
|
|
|
|
|
87
|
push @index_defs, create_index($index); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# |
234
|
|
|
|
|
|
|
# Constraints |
235
|
|
|
|
|
|
|
# |
236
|
25
|
|
|
|
|
85
|
for my $c ( $table->get_constraints ) { |
237
|
31
|
100
|
|
|
|
936
|
if ($c->type eq "FOREIGN KEY") { |
|
|
50
|
|
|
|
|
|
238
|
8
|
|
|
|
|
179
|
push @field_defs, create_foreignkey($c); |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
elsif ($c->type eq "CHECK") { |
241
|
0
|
|
|
|
|
0
|
push @field_defs, create_check_constraint($c); |
242
|
|
|
|
|
|
|
} |
243
|
31
|
100
|
|
|
|
1051
|
next unless $c->type eq UNIQUE; |
244
|
7
|
|
|
|
|
141
|
push @constraint_defs, create_constraint($c); |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
25
|
|
|
|
|
336
|
$create_table .= join(",\n", map { " $_" } @field_defs ) . "\n)"; |
|
93
|
|
|
|
|
282
|
|
248
|
|
|
|
|
|
|
|
249
|
25
|
|
|
|
|
200
|
return (@create, $create_table, @index_defs, @constraint_defs ); |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
sub create_check_constraint { |
253
|
1
|
|
|
1
|
0
|
6
|
my $c = shift; |
254
|
1
|
|
|
|
|
2
|
my $check = ''; |
255
|
1
|
50
|
|
|
|
19
|
$check .= 'CONSTRAINT ' . _generator->quote( $c->name ) . ' ' if $c->name; |
256
|
1
|
|
|
|
|
22
|
$check .= 'CHECK(' . $c->expression . ')'; |
257
|
1
|
|
|
|
|
4
|
return $check; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub create_foreignkey { |
261
|
9
|
|
|
9
|
0
|
52
|
my $c = shift; |
262
|
|
|
|
|
|
|
|
263
|
9
|
|
|
|
|
31
|
my @fields = $c->fields; |
264
|
9
|
50
|
|
|
|
211
|
my @rfields = map { $_ || () } $c->reference_fields; |
|
9
|
|
|
|
|
57
|
|
265
|
9
|
50
|
|
|
|
38
|
unless ( @rfields ) { |
266
|
0
|
|
|
|
|
0
|
my $rtable_name = $c->reference_table; |
267
|
0
|
0
|
|
|
|
0
|
if ( my $ref_table = $c->schema->get_table( $rtable_name ) ) { |
268
|
0
|
|
|
|
|
0
|
push @rfields, $ref_table->primary_key; |
269
|
|
|
|
|
|
|
|
270
|
0
|
0
|
|
|
|
0
|
die "FK constraint on " . $rtable_name . '.' . join('', @fields) . " has no reference fields\n" |
271
|
|
|
|
|
|
|
unless @rfields; |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
else { |
274
|
0
|
|
|
|
|
0
|
die "Can't find reference table '$rtable_name' in schema\n"; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
my $fk_sql = sprintf 'FOREIGN KEY (%s) REFERENCES %s(%s)', |
279
|
9
|
|
|
|
|
25
|
join (', ', map { _generator()->quote($_) } @fields ), |
280
|
|
|
|
|
|
|
_generator()->quote($c->reference_table), |
281
|
9
|
|
|
|
|
23
|
join (', ', map { _generator()->quote($_) } @rfields ) |
|
9
|
|
|
|
|
27
|
|
282
|
|
|
|
|
|
|
; |
283
|
|
|
|
|
|
|
|
284
|
9
|
100
|
|
|
|
48
|
$fk_sql .= " ON DELETE " . $c->{on_delete} if $c->{on_delete}; |
285
|
9
|
100
|
|
|
|
36
|
$fk_sql .= " ON UPDATE " . $c->{on_update} if $c->{on_update}; |
286
|
|
|
|
|
|
|
|
287
|
9
|
|
|
|
|
29
|
return $fk_sql; |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
86
|
|
|
86
|
0
|
186
|
sub create_field { return _generator()->field($_[0]) } |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub create_index |
293
|
|
|
|
|
|
|
{ |
294
|
11
|
|
|
11
|
0
|
42
|
my ($index, $options) = @_; |
295
|
|
|
|
|
|
|
|
296
|
11
|
|
|
|
|
227
|
(my $index_table_name = $index->table->name) =~ s/^.+?\.//; # table name may not specify schema |
297
|
11
|
|
66
|
|
|
419
|
my $name = mk_name($index->name || "${index_table_name}_idx"); |
298
|
|
|
|
|
|
|
|
299
|
11
|
100
|
|
|
|
273
|
my $type = $index->type eq 'UNIQUE' ? "UNIQUE " : ''; |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# strip any field size qualifiers as SQLite doesn't like these |
302
|
11
|
|
|
|
|
459
|
my @fields = map { s/\(\d+\)$//; _generator()->quote($_) } $index->fields; |
|
11
|
|
|
|
|
43
|
|
|
11
|
|
|
|
|
37
|
|
303
|
11
|
|
|
|
|
37
|
$index_table_name = _generator()->quote($index_table_name); |
304
|
11
|
50
|
|
|
|
46
|
warn "removing schema name from '" . $index->table->name . "' to make '$index_table_name'\n" if $WARN; |
305
|
11
|
|
|
|
|
80
|
my $index_def = |
306
|
|
|
|
|
|
|
"CREATE ${type}INDEX $name ON " . $index_table_name . |
307
|
|
|
|
|
|
|
' (' . join( ', ', @fields ) . ')'; |
308
|
|
|
|
|
|
|
|
309
|
11
|
|
|
|
|
43
|
return $index_def; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub create_constraint |
313
|
|
|
|
|
|
|
{ |
314
|
11
|
|
|
11
|
0
|
84
|
my ($c, $options) = @_; |
315
|
|
|
|
|
|
|
|
316
|
11
|
|
|
|
|
210
|
(my $index_table_name = $c->table->name) =~ s/^.+?\.//; # table name may not specify schema |
317
|
11
|
|
66
|
|
|
435
|
my $name = mk_name($c->name || "${index_table_name}_idx"); |
318
|
11
|
|
|
|
|
62
|
my @fields = map _generator()->quote($_), $c->fields; |
319
|
11
|
|
|
|
|
42
|
$index_table_name = _generator()->quote($index_table_name); |
320
|
11
|
50
|
|
|
|
158
|
warn "removing schema name from '" . $c->table->name . "' to make '$index_table_name'\n" if $WARN; |
321
|
|
|
|
|
|
|
|
322
|
11
|
|
|
|
|
65
|
my $c_def = |
323
|
|
|
|
|
|
|
"CREATE UNIQUE INDEX $name ON " . $index_table_name . |
324
|
|
|
|
|
|
|
' (' . join( ', ', @fields ) . ')'; |
325
|
|
|
|
|
|
|
|
326
|
11
|
|
|
|
|
242
|
return $c_def; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
sub create_trigger { |
330
|
9
|
|
|
9
|
0
|
33
|
my ($trigger, $options) = @_; |
331
|
9
|
|
|
|
|
18
|
my $add_drop = $options->{add_drop_trigger}; |
332
|
|
|
|
|
|
|
|
333
|
9
|
|
|
|
|
15
|
my @statements; |
334
|
|
|
|
|
|
|
|
335
|
9
|
|
|
|
|
28
|
my $trigger_name = $trigger->name; |
336
|
9
|
|
|
|
|
29
|
$global_names{$trigger_name} = 1; |
337
|
|
|
|
|
|
|
|
338
|
9
|
|
|
|
|
197
|
my $events = $trigger->database_events; |
339
|
9
|
|
|
|
|
90
|
for my $evt ( @$events ) { |
340
|
|
|
|
|
|
|
|
341
|
10
|
|
|
|
|
20
|
my $trig_name = $trigger_name; |
342
|
10
|
100
|
|
|
|
30
|
if (@$events > 1) { |
343
|
2
|
|
|
|
|
6
|
$trig_name .= "_$evt"; |
344
|
|
|
|
|
|
|
|
345
|
2
|
50
|
|
|
|
6
|
warn "Multiple database events supplied for trigger '$trigger_name', ", |
346
|
|
|
|
|
|
|
"creating trigger '$trig_name' for the '$evt' event.\n" if $WARN; |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
10
|
|
|
|
|
33
|
$trig_name = _generator()->quote($trig_name); |
350
|
10
|
100
|
|
|
|
64
|
push @statements, "DROP TRIGGER IF EXISTS $trig_name" if $add_drop; |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
|
353
|
10
|
|
|
|
|
25
|
$DB::single = 1; |
354
|
10
|
|
|
|
|
25
|
my $action = ""; |
355
|
10
|
100
|
|
|
|
40
|
if (not ref $trigger->action) { |
356
|
6
|
|
|
|
|
18
|
$action = $trigger->action; |
357
|
6
|
100
|
|
|
|
49
|
$action = "BEGIN " . $action . " END" |
358
|
|
|
|
|
|
|
unless $action =~ /^ \s* BEGIN [\s\;] .*? [\s\;] END [\s\;]* $/six; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
else { |
361
|
|
|
|
|
|
|
$action = $trigger->action->{for_each} . " " |
362
|
4
|
50
|
|
|
|
23
|
if $trigger->action->{for_each}; |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
$action = $trigger->action->{when} . " " |
365
|
4
|
50
|
|
|
|
15
|
if $trigger->action->{when}; |
366
|
|
|
|
|
|
|
|
367
|
4
|
|
50
|
|
|
16
|
my $steps = $trigger->action->{steps} || []; |
368
|
|
|
|
|
|
|
|
369
|
4
|
|
|
|
|
10
|
$action .= "BEGIN "; |
370
|
4
|
|
|
|
|
16
|
$action .= $_ . "; " for (@$steps); |
371
|
4
|
|
|
|
|
7
|
$action .= "END"; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
10
|
|
|
|
|
222
|
push @statements, sprintf ( |
375
|
|
|
|
|
|
|
'CREATE TRIGGER %s %s %s on %s %s', |
376
|
|
|
|
|
|
|
$trig_name, |
377
|
|
|
|
|
|
|
$trigger->perform_action_when, |
378
|
|
|
|
|
|
|
$evt, |
379
|
|
|
|
|
|
|
_generator()->quote($trigger->on_table), |
380
|
|
|
|
|
|
|
$action |
381
|
|
|
|
|
|
|
); |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
9
|
|
|
|
|
53
|
return @statements; |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
|
387
|
1
|
|
|
1
|
0
|
27
|
sub alter_table { () } # Noop |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
sub add_field { |
390
|
3
|
|
|
3
|
0
|
18
|
my ($field) = @_; |
391
|
|
|
|
|
|
|
|
392
|
3
|
|
|
|
|
10
|
return sprintf("ALTER TABLE %s ADD COLUMN %s", |
393
|
|
|
|
|
|
|
_generator()->quote($field->table->name), create_field($field)) |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
sub alter_create_index { |
397
|
1
|
|
|
1
|
0
|
10
|
my ($index) = @_; |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
# This might cause name collisions |
400
|
1
|
|
|
|
|
6
|
return create_index($index); |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
sub alter_create_constraint { |
404
|
3
|
|
|
3
|
0
|
23
|
my ($constraint) = @_; |
405
|
|
|
|
|
|
|
|
406
|
3
|
100
|
|
|
|
55
|
return create_constraint($constraint) if $constraint->type eq 'UNIQUE'; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
|
409
|
2
|
|
|
2
|
0
|
18
|
sub alter_drop_constraint { alter_drop_index(@_) } |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
sub alter_drop_index { |
412
|
3
|
|
|
3
|
0
|
11
|
my ($constraint) = @_; |
413
|
|
|
|
|
|
|
|
414
|
3
|
|
|
|
|
9
|
return sprintf("DROP INDEX %s", |
415
|
|
|
|
|
|
|
_generator()->quote($constraint->name)); |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
sub batch_alter_table { |
419
|
5
|
|
|
5
|
0
|
247
|
my ($table, $diffs, $options) = @_; |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# If we have any of the following |
422
|
|
|
|
|
|
|
# |
423
|
|
|
|
|
|
|
# rename_field |
424
|
|
|
|
|
|
|
# alter_field |
425
|
|
|
|
|
|
|
# drop_field |
426
|
|
|
|
|
|
|
# |
427
|
|
|
|
|
|
|
# we need to do the following |
428
|
|
|
|
|
|
|
# |
429
|
|
|
|
|
|
|
# BEGIN TRANSACTION; |
430
|
|
|
|
|
|
|
# CREATE TEMPORARY TABLE t1_backup(a,b); |
431
|
|
|
|
|
|
|
# INSERT INTO t1_backup SELECT a,b FROM t1; |
432
|
|
|
|
|
|
|
# DROP TABLE t1; |
433
|
|
|
|
|
|
|
# CREATE TABLE t1(a,b); |
434
|
|
|
|
|
|
|
# INSERT INTO t1 SELECT a,b FROM t1_backup; |
435
|
|
|
|
|
|
|
# DROP TABLE t1_backup; |
436
|
|
|
|
|
|
|
# COMMIT; |
437
|
|
|
|
|
|
|
# |
438
|
|
|
|
|
|
|
# Fun, eh? |
439
|
|
|
|
|
|
|
# |
440
|
|
|
|
|
|
|
# If we have rename_field we do similarly. |
441
|
|
|
|
|
|
|
# |
442
|
|
|
|
|
|
|
# We create the temporary table as a copy of the new table, copy all data |
443
|
|
|
|
|
|
|
# to temp table, create new table and then copy as appropriate taking note |
444
|
|
|
|
|
|
|
# of renamed fields. |
445
|
|
|
|
|
|
|
|
446
|
5
|
|
|
|
|
103
|
my $table_name = $table->name; |
447
|
|
|
|
|
|
|
|
448
|
5
|
100
|
66
|
|
|
92
|
if ( @{$diffs->{rename_field}} == 0 && |
|
5
|
|
100
|
|
|
21
|
|
449
|
4
|
|
|
|
|
22
|
@{$diffs->{alter_field}} == 0 && |
450
|
4
|
|
|
|
|
15
|
@{$diffs->{drop_field}} == 0 |
451
|
|
|
|
|
|
|
) { |
452
|
2
|
|
|
|
|
9
|
return batch_alter_table_statements($diffs, $options); |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
|
455
|
3
|
|
|
|
|
8
|
my @sql; |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
# $table is the new table but we may need an old one |
458
|
|
|
|
|
|
|
# TODO: this is NOT very well tested at the moment so add more tests |
459
|
|
|
|
|
|
|
|
460
|
3
|
|
|
|
|
7
|
my $old_table = $table; |
461
|
|
|
|
|
|
|
|
462
|
3
|
50
|
33
|
|
|
13
|
if ( $diffs->{rename_table} && @{$diffs->{rename_table}} ) { |
|
3
|
|
|
|
|
12
|
|
463
|
0
|
|
|
|
|
0
|
$old_table = $diffs->{rename_table}[0][0]; |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
|
466
|
3
|
|
|
|
|
8
|
my $temp_table_name = $table_name . '_temp_alter'; |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# CREATE TEMPORARY TABLE t1_backup(a,b); |
469
|
|
|
|
|
|
|
|
470
|
3
|
|
|
|
|
6
|
my %temp_table_fields; |
471
|
3
|
|
|
|
|
5
|
do { |
472
|
3
|
|
|
|
|
9
|
local $table->{name} = $temp_table_name; |
473
|
|
|
|
|
|
|
# We only want the table - don't care about indexes on tmp table |
474
|
3
|
|
|
|
|
18
|
my ($table_sql) = create_table($table, {no_comments => 1, temporary_table => 1}); |
475
|
3
|
|
|
|
|
12
|
push @sql,$table_sql; |
476
|
|
|
|
|
|
|
|
477
|
3
|
|
|
|
|
21
|
%temp_table_fields = map { $_ => 1} $table->get_fields; |
|
14
|
|
|
|
|
32
|
|
478
|
|
|
|
|
|
|
}; |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# record renamed fields for later |
481
|
3
|
|
|
|
|
69
|
my %rename_field = map { $_->[1]->name => $_->[0]->name } @{$diffs->{rename_field}}; |
|
1
|
|
|
|
|
23
|
|
|
3
|
|
|
|
|
13
|
|
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
# drop added fields from %temp_table_fields |
484
|
3
|
|
|
|
|
28
|
delete @temp_table_fields{@{$diffs->{add_field}}}; |
|
3
|
|
|
|
|
13
|
|
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
# INSERT INTO t1_backup SELECT a,b FROM t1; |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
push @sql, sprintf( 'INSERT INTO %s( %s) SELECT %s FROM %s', |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
_generator()->quote( $temp_table_name ), |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
join( ', ', |
493
|
|
|
|
|
|
|
map _generator()->quote($_), |
494
|
14
|
|
|
|
|
217
|
grep { $temp_table_fields{$_} } $table->get_fields ), |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
join( ', ', |
497
|
|
|
|
|
|
|
map _generator()->quote($_), |
498
|
11
|
100
|
|
|
|
562
|
map { $rename_field{$_} ? $rename_field{$_} : $_ } |
499
|
3
|
|
|
|
|
59
|
grep { $temp_table_fields{$_} } $table->get_fields ), |
|
14
|
|
|
|
|
215
|
|
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
_generator()->quote( $old_table->name ) |
502
|
|
|
|
|
|
|
); |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# DROP TABLE t1; |
505
|
|
|
|
|
|
|
|
506
|
3
|
|
|
|
|
17
|
push @sql, sprintf('DROP TABLE %s', _generator()->quote($old_table->name)); |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
# CREATE TABLE t1(a,b); |
509
|
|
|
|
|
|
|
|
510
|
3
|
|
|
|
|
18
|
push @sql, create_table($table, { no_comments => 1 }); |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# INSERT INTO t1 SELECT a,b FROM t1_backup; |
513
|
|
|
|
|
|
|
|
514
|
3
|
|
|
|
|
30
|
push @sql, sprintf('INSERT INTO %s SELECT %s FROM %s', |
515
|
|
|
|
|
|
|
_generator()->quote($table_name), |
516
|
|
|
|
|
|
|
join(', ', map _generator()->quote($_), $table->get_fields), |
517
|
|
|
|
|
|
|
_generator()->quote($temp_table_name) |
518
|
|
|
|
|
|
|
); |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
# DROP TABLE t1_backup; |
521
|
|
|
|
|
|
|
|
522
|
3
|
|
|
|
|
16
|
push @sql, sprintf('DROP TABLE %s', _generator()->quote($temp_table_name)); |
523
|
|
|
|
|
|
|
|
524
|
3
|
50
|
|
|
|
46
|
return wantarray ? @sql : join(";\n", @sql); |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
sub drop_table { |
528
|
2
|
|
|
2
|
0
|
21
|
my ($table) = @_; |
529
|
2
|
|
|
|
|
16
|
$table = _generator()->quote($table); |
530
|
2
|
|
|
|
|
12
|
return "DROP TABLE $table"; |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
sub rename_table { |
534
|
2
|
|
|
2
|
0
|
18
|
my ($old_table, $new_table, $options) = @_; |
535
|
|
|
|
|
|
|
|
536
|
2
|
|
|
|
|
6
|
$old_table = _generator()->quote($old_table); |
537
|
2
|
|
|
|
|
9
|
$new_table = _generator()->quote($new_table); |
538
|
|
|
|
|
|
|
|
539
|
2
|
|
|
|
|
13
|
return "ALTER TABLE $old_table RENAME TO $new_table"; |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
# No-op. Just here to signify that we are a new style parser. |
544
|
|
|
|
0
|
0
|
|
sub preproces_schema { } |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
1; |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
=pod |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=head1 SEE ALSO |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
SQL::Translator, http://www.sqlite.org/. |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
=head1 AUTHOR |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
Ken Youens-Clark C<< >>. |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
Diff code added by Ash Berlin C<< >>. |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
=cut |