Commit | Line | Data |
c3734b46 |
1 | use MooseX::Declare; |
2 | role SQL::Translator::Grammar::SQLite { |
5ea69034 |
3 | # ------------------------------------------------------------------- |
4 | # Copyright (C) 2002-2009 SQLFairy Authors |
5 | # |
6 | # This program is free software; you can redistribute it and/or |
7 | # modify it under the terms of the GNU General Public License as |
8 | # published by the Free Software Foundation; version 2. |
9 | # |
10 | # This program is distributed in the hope that it will be useful, but |
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | # General Public License for more details. |
14 | # |
15 | # You should have received a copy of the GNU General Public License |
16 | # along with this program; if not, write to the Free Software |
17 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
18 | # 02111-1307 USA |
19 | # ------------------------------------------------------------------- |
20 | |
c3734b46 |
21 | method _build_grammar { |
22 | return q! |
23 | { |
5ea69034 |
24 | my ( %tables, $table_order, @table_comments, @views, @triggers ); |
5ea69034 |
25 | } |
26 | |
c3734b46 |
27 | # |
28 | # The "eofile" rule makes the parser fail if any "statement" rule |
29 | # fails. Otherwise, the first successful match by a "statement" |
30 | # won't cause the failure needed to know that the parse, as a whole, |
31 | # failed. -ky |
32 | # |
33 | startrule : statement(s) eofile { |
34 | $return = { |
35 | tables => \%tables, |
36 | views => \@views, |
37 | triggers => \@triggers, |
38 | } |
5ea69034 |
39 | } |
40 | |
c3734b46 |
41 | eofile : /^\Z/ |
5ea69034 |
42 | |
c3734b46 |
43 | statement : begin_transaction |
44 | | commit |
45 | | drop |
46 | | comment |
47 | | create |
48 | | <error> |
5ea69034 |
49 | |
c3734b46 |
50 | begin_transaction : /begin/i TRANSACTION(?) SEMICOLON |
5ea69034 |
51 | |
c3734b46 |
52 | commit : /commit/i SEMICOLON |
5ea69034 |
53 | |
c3734b46 |
54 | drop : /drop/i (tbl_drop | view_drop | trg_drop) SEMICOLON |
5ea69034 |
55 | |
c3734b46 |
56 | tbl_drop: TABLE <commit> table_name |
5ea69034 |
57 | |
c3734b46 |
58 | view_drop: VIEW if_exists(?) view_name |
5ea69034 |
59 | |
c3734b46 |
60 | trg_drop: TRIGGER if_exists(?) trigger_name |
5ea69034 |
61 | |
c3734b46 |
62 | comment : /^\s*(?:#|-{2}).*\n/ |
63 | { |
64 | my $comment = $item[1]; |
65 | $comment =~ s/^\s*(#|-{2})\s*//; |
66 | $comment =~ s/\s*$//; |
67 | $return = $comment; |
68 | } |
5ea69034 |
69 | |
c3734b46 |
70 | comment : /\/\*/ /[^\*]+/ /\*\// |
71 | { |
72 | my $comment = $item[2]; |
73 | $comment =~ s/^\s*|\s*$//g; |
74 | $return = $comment; |
75 | } |
5ea69034 |
76 | |
c3734b46 |
77 | # |
78 | # Create Index |
79 | # |
80 | create : CREATE TEMPORARY(?) UNIQUE(?) INDEX WORD ON table_name parens_field_list conflict_clause(?) SEMICOLON |
81 | { |
82 | my $db_name = $item[7]->{'db_name'} || ''; |
83 | my $table_name = $item[7]->{'name'}; |
5ea69034 |
84 | |
c3734b46 |
85 | my $index = { |
86 | name => $item[5], |
87 | columns => $item[8], |
88 | on_conflict => $item[9][0], |
89 | is_temporary => $item[2][0] ? 1 : 0, |
90 | }; |
5ea69034 |
91 | |
c3734b46 |
92 | my $is_unique = $item[3][0]; |
5ea69034 |
93 | |
c3734b46 |
94 | if ( $is_unique ) { |
95 | $index->{'type'} = 'unique'; |
96 | push @{ $tables{ $table_name }{'constraints'} }, $index; |
97 | } |
98 | else { |
99 | push @{ $tables{ $table_name }{'indices'} }, $index; |
100 | } |
101 | } |
5ea69034 |
102 | |
c3734b46 |
103 | # |
104 | # Create Table |
105 | # |
106 | create : CREATE TEMPORARY(?) TABLE table_name '(' definition(s /,/) ')' SEMICOLON |
107 | { |
108 | my $db_name = $item[4]->{'db_name'} || ''; |
109 | my $table_name = $item[4]->{'name'}; |
5ea69034 |
110 | |
c3734b46 |
111 | $tables{ $table_name }{'name'} = $table_name; |
112 | $tables{ $table_name }{'is_temporary'} = $item[2][0] ? 1 : 0; |
113 | $tables{ $table_name }{'order'} = ++$table_order; |
5ea69034 |
114 | |
c3734b46 |
115 | for my $def ( @{ $item[6] } ) { |
116 | if ( $def->{'supertype'} eq 'column' ) { |
117 | push @{ $tables{ $table_name }{'columns'} }, $def; |
118 | } |
119 | elsif ( $def->{'supertype'} eq 'constraint' ) { |
120 | push @{ $tables{ $table_name }{'constraints'} }, $def; |
121 | } |
122 | } |
123 | } |
5ea69034 |
124 | |
c3734b46 |
125 | definition : constraint_def | column_def |
5ea69034 |
126 | |
c3734b46 |
127 | column_def: comment(s?) NAME type(?) column_constraint(s?) |
128 | { |
5ea69034 |
129 | my $column = { |
130 | supertype => 'column', |
131 | name => $item[2], |
132 | data_type => $item[3][0]->{'type'}, |
133 | size => $item[3][0]->{'size'}, |
134 | is_nullable => 1, |
135 | is_primary_key => 0, |
136 | is_unique => 0, |
137 | check => '', |
138 | default => undef, |
139 | constraints => $item[4], |
140 | comments => $item[1], |
141 | }; |
142 | |
143 | |
144 | for my $c ( @{ $item[4] } ) { |
145 | if ( $c->{'type'} eq 'not_null' ) { |
146 | $column->{'is_nullable'} = 0; |
147 | } |
148 | elsif ( $c->{'type'} eq 'primary_key' ) { |
149 | $column->{'is_primary_key'} = 1; |
150 | } |
151 | elsif ( $c->{'type'} eq 'unique' ) { |
152 | $column->{'is_unique'} = 1; |
153 | } |
154 | elsif ( $c->{'type'} eq 'check' ) { |
155 | $column->{'check'} = $c->{'expression'}; |
156 | } |
157 | elsif ( $c->{'type'} eq 'default' ) { |
158 | $column->{'default'} = $c->{'value'}; |
159 | } |
160 | } |
161 | |
162 | $column; |
c3734b46 |
163 | } |
5ea69034 |
164 | |
c3734b46 |
165 | type : WORD parens_value_list(?) |
166 | { |
167 | $return = { |
168 | type => $item[1], |
169 | size => $item[2][0], |
170 | } |
171 | } |
5ea69034 |
172 | |
c3734b46 |
173 | column_constraint : NOT_NULL conflict_clause(?) |
174 | { |
175 | $return = { |
176 | type => 'not_null', |
177 | } |
178 | } |
179 | | |
180 | PRIMARY_KEY sort_order(?) conflict_clause(?) |
181 | { |
182 | $return = { |
183 | type => 'primary_key', |
184 | sort_order => $item[2][0], |
185 | on_conflict => $item[2][0], |
186 | } |
187 | } |
188 | | |
189 | UNIQUE conflict_clause(?) |
190 | { |
191 | $return = { |
192 | type => 'unique', |
193 | on_conflict => $item[2][0], |
194 | } |
195 | } |
196 | | |
197 | CHECK_C '(' expr ')' conflict_clause(?) |
198 | { |
199 | $return = { |
200 | type => 'check', |
201 | expression => $item[3], |
202 | on_conflict => $item[5][0], |
203 | } |
204 | } |
205 | | |
206 | DEFAULT VALUE |
207 | { |
208 | $return = { |
209 | type => 'default', |
210 | value => $item[2], |
211 | } |
212 | } |
5ea69034 |
213 | |
c3734b46 |
214 | constraint_def : PRIMARY_KEY parens_field_list conflict_clause(?) |
215 | { |
5ea69034 |
216 | $return = { |
217 | supertype => 'constraint', |
218 | type => 'primary_key', |
219 | columns => $item[2], |
220 | on_conflict => $item[3][0], |
221 | } |
c3734b46 |
222 | } |
223 | | |
224 | UNIQUE parens_field_list conflict_clause(?) |
225 | { |
5ea69034 |
226 | $return = { |
227 | supertype => 'constraint', |
228 | type => 'unique', |
229 | columns => $item[2], |
230 | on_conflict => $item[3][0], |
231 | } |
c3734b46 |
232 | } |
233 | | |
234 | CHECK_C '(' expr ')' conflict_clause(?) |
235 | { |
5ea69034 |
236 | $return = { |
237 | supertype => 'constraint', |
238 | type => 'check', |
239 | expression => $item[3], |
240 | on_conflict => $item[5][0], |
241 | } |
c3734b46 |
242 | } |
5ea69034 |
243 | |
c3734b46 |
244 | table_name : qualified_name |
245 | |
246 | qualified_name : NAME |
247 | { $return = { name => $item[1] } } |
5ea69034 |
248 | |
c3734b46 |
249 | qualified_name : /(\w+)\.(\w+)/ |
250 | { $return = { db_name => $1, name => $2 } } |
5ea69034 |
251 | |
c3734b46 |
252 | field_name : NAME |
5ea69034 |
253 | |
c3734b46 |
254 | conflict_clause : /on conflict/i conflict_algorigthm |
5ea69034 |
255 | |
c3734b46 |
256 | conflict_algorigthm : /(rollback|abort|fail|ignore|replace)/i |
5ea69034 |
257 | |
c3734b46 |
258 | parens_field_list : '(' column_list ')' |
259 | { $item[2] } |
5ea69034 |
260 | |
c3734b46 |
261 | column_list : field_name(s /,/) |
5ea69034 |
262 | |
c3734b46 |
263 | parens_value_list : '(' VALUE(s /,/) ')' |
264 | { $item[2] } |
5ea69034 |
265 | |
c3734b46 |
266 | expr : /[^)]+/ |
5ea69034 |
267 | |
c3734b46 |
268 | sort_order : /(ASC|DESC)/i |
5ea69034 |
269 | |
c3734b46 |
270 | # |
271 | # Create Trigger |
5ea69034 |
272 | |
c3734b46 |
273 | create : CREATE TEMPORARY(?) TRIGGER NAME before_or_after(?) database_event ON table_name trigger_action SEMICOLON |
274 | { |
275 | my $table_name = $item[8]->{'name'}; |
276 | push @triggers, { |
277 | name => $item[4], |
278 | is_temporary => $item[2][0] ? 1 : 0, |
279 | when => $item[5][0], |
280 | instead_of => 0, |
281 | db_events => [ $item[6] ], |
282 | action => $item[9], |
283 | on_table => $table_name, |
284 | } |
285 | } |
5ea69034 |
286 | |
c3734b46 |
287 | create : CREATE TEMPORARY(?) TRIGGER NAME instead_of database_event ON view_name trigger_action |
288 | { |
289 | my $table_name = $item[8]->{'name'}; |
290 | push @triggers, { |
291 | name => $item[4], |
292 | is_temporary => $item[2][0] ? 1 : 0, |
293 | when => undef, |
294 | instead_of => 1, |
295 | db_events => [ $item[6] ], |
296 | action => $item[9], |
297 | on_table => $table_name, |
298 | } |
299 | } |
5ea69034 |
300 | |
c3734b46 |
301 | database_event : /(delete|insert|update)/i |
5ea69034 |
302 | |
c3734b46 |
303 | database_event : /update of/i column_list |
5ea69034 |
304 | |
c3734b46 |
305 | trigger_action : for_each(?) when(?) BEGIN_C trigger_step(s) END_C |
306 | { |
307 | $return = { |
308 | for_each => $item[1][0], |
309 | when => $item[2][0], |
310 | steps => $item[4], |
311 | } |
312 | } |
5ea69034 |
313 | |
c3734b46 |
314 | for_each : /FOR EACH ROW/i |
5ea69034 |
315 | |
c3734b46 |
316 | when : WHEN expr { $item[2] } |
5ea69034 |
317 | |
c3734b46 |
318 | string : |
319 | /'(\\.|''|[^\\\'])*'/ |
5ea69034 |
320 | |
c3734b46 |
321 | nonstring : /[^;\'"]+/ |
5ea69034 |
322 | |
c3734b46 |
323 | statement_body : string | nonstring |
5ea69034 |
324 | |
c3734b46 |
325 | trigger_step : /(select|delete|insert|update)/i statement_body(s?) SEMICOLON |
326 | { |
327 | $return = join( ' ', $item[1], join ' ', @{ $item[2] || [] } ) |
328 | } |
5ea69034 |
329 | |
c3734b46 |
330 | before_or_after : /(before|after)/i { $return = lc $1 } |
5ea69034 |
331 | |
c3734b46 |
332 | instead_of : /instead of/i |
5ea69034 |
333 | |
c3734b46 |
334 | if_exists : /if exists/i |
5ea69034 |
335 | |
c3734b46 |
336 | view_name : qualified_name |
5ea69034 |
337 | |
c3734b46 |
338 | trigger_name : qualified_name |
5ea69034 |
339 | |
c3734b46 |
340 | # |
341 | # Create View |
342 | # |
343 | create : CREATE TEMPORARY(?) VIEW view_name AS select_statement |
344 | { |
345 | push @views, { |
346 | name => $item[4]->{'name'}, |
347 | sql => $item[6], |
348 | is_temporary => $item[2][0] ? 1 : 0, |
349 | } |
350 | } |
5ea69034 |
351 | |
c3734b46 |
352 | select_statement : SELECT /[^;]+/ SEMICOLON |
353 | { |
354 | $return = join( ' ', $item[1], $item[2] ); |
355 | } |
5ea69034 |
356 | |
c3734b46 |
357 | # |
358 | # Tokens |
359 | # |
360 | BEGIN_C : /begin/i |
5ea69034 |
361 | |
c3734b46 |
362 | END_C : /end/i |
5ea69034 |
363 | |
c3734b46 |
364 | TRANSACTION: /transaction/i |
5ea69034 |
365 | |
c3734b46 |
366 | CREATE : /create/i |
5ea69034 |
367 | |
c3734b46 |
368 | TEMPORARY : /temp(orary)?/i { 1 } |
5ea69034 |
369 | |
c3734b46 |
370 | TABLE : /table/i |
5ea69034 |
371 | |
c3734b46 |
372 | INDEX : /index/i |
5ea69034 |
373 | |
c3734b46 |
374 | NOT_NULL : /not null/i |
5ea69034 |
375 | |
c3734b46 |
376 | PRIMARY_KEY : /primary key/i |
5ea69034 |
377 | |
c3734b46 |
378 | CHECK_C : /check/i |
5ea69034 |
379 | |
c3734b46 |
380 | DEFAULT : /default/i |
5ea69034 |
381 | |
c3734b46 |
382 | TRIGGER : /trigger/i |
5ea69034 |
383 | |
c3734b46 |
384 | VIEW : /view/i |
5ea69034 |
385 | |
c3734b46 |
386 | SELECT : /select/i |
5ea69034 |
387 | |
c3734b46 |
388 | ON : /on/i |
5ea69034 |
389 | |
c3734b46 |
390 | AS : /as/i |
5ea69034 |
391 | |
c3734b46 |
392 | WORD : /\w+/ |
5ea69034 |
393 | |
c3734b46 |
394 | WHEN : /when/i |
5ea69034 |
395 | |
c3734b46 |
396 | UNIQUE : /unique/i { 1 } |
5ea69034 |
397 | |
c3734b46 |
398 | SEMICOLON : ';' |
5ea69034 |
399 | |
c3734b46 |
400 | NAME : /'?(\w+)'?/ { $return = $1 } |
5ea69034 |
401 | |
c3734b46 |
402 | VALUE : /[-+]?\.?\d+(?:[eE]\d+)?/ |
403 | { $item[1] } |
404 | | /'.*?'/ |
405 | { |
5ea69034 |
406 | # remove leading/trailing quotes |
407 | my $val = $item[1]; |
408 | $val =~ s/^['"]|['"]$//g; |
409 | $return = $val; |
c3734b46 |
410 | } |
411 | | /NULL/ |
412 | { 'NULL' } |
413 | | /CURRENT_TIMESTAMP/i |
414 | { 'CURRENT_TIMESTAMP' } |
415 | !; |
5ea69034 |
416 | } |
5ea69034 |
417 | } |