2 role SQL::Translator::Grammar::SQLite {
4 method _build_grammar {
7 my ( %tables, $table_order, @table_comments, @views, @triggers );
11 # The "eofile" rule makes the parser fail if any "statement" rule
12 # fails. Otherwise, the first successful match by a "statement"
13 # won't cause the failure needed to know that the parse, as a whole,
16 startrule : statement(s) eofile {
20 triggers => \@triggers,
26 statement : begin_transaction
33 begin_transaction : /begin/i TRANSACTION(?) SEMICOLON
35 commit : /commit/i SEMICOLON
37 drop : /drop/i (tbl_drop | view_drop | trg_drop) SEMICOLON
39 tbl_drop: TABLE <commit> table_name
41 view_drop: VIEW if_exists(?) view_name
43 trg_drop: TRIGGER if_exists(?) trigger_name
45 comment : /^\s*(?:#|-{2}).*\n/
47 my $comment = $item[1];
48 $comment =~ s/^\s*(#|-{2})\s*//;
53 comment : /\/\*/ /[^\*]+/ /\*\//
55 my $comment = $item[2];
56 $comment =~ s/^\s*|\s*$//g;
63 create : CREATE TEMPORARY(?) UNIQUE(?) INDEX NAME ON table_name parens_field_list conflict_clause(?) SEMICOLON
65 my $db_name = $item[7]->{'db_name'} || '';
66 my $table_name = $item[7]->{'name'};
71 on_conflict => $item[9][0],
72 is_temporary => $item[2][0] ? 1 : 0,
75 my $is_unique = $item[3][0];
78 $index->{'type'} = 'unique';
79 push @{ $tables{ $table_name }{'constraints'} }, $index;
82 push @{ $tables{ $table_name }{'indices'} }, $index;
89 create : CREATE TEMPORARY(?) TABLE table_name '(' definition(s /,/) ')' SEMICOLON
91 my $db_name = $item[4]->{'db_name'} || '';
92 my $table_name = $item[4]->{'name'};
94 $tables{ $table_name }{'name'} = $table_name;
95 $tables{ $table_name }{'is_temporary'} = $item[2][0] ? 1 : 0;
96 $tables{ $table_name }{'order'} = ++$table_order;
98 for my $def ( @{ $item[6] } ) {
99 if ( $def->{'supertype'} eq 'column' ) {
100 push @{ $tables{ $table_name }{'fields'} }, $def;
102 elsif ( $def->{'supertype'} eq 'constraint' ) {
103 push @{ $tables{ $table_name }{'constraints'} }, $def;
108 definition : constraint_def | column_def
110 column_def: comment(s?) NAME type(?) column_constraint(s?)
113 supertype => 'column',
115 data_type => $item[3][0]->{'type'},
116 size => $item[3][0]->{'size'},
122 constraints => $item[4],
123 comments => $item[1],
127 for my $c ( @{ $item[4] } ) {
128 if ( $c->{'type'} eq 'not_null' ) {
129 $column->{'is_nullable'} = 0;
131 elsif ( $c->{'type'} eq 'primary_key' ) {
132 $column->{'is_primary_key'} = 1;
134 elsif ( $c->{'type'} eq 'unique' ) {
135 $column->{'is_unique'} = 1;
137 elsif ( $c->{'type'} eq 'check' ) {
138 $column->{'check'} = $c->{'expression'};
140 elsif ( $c->{'type'} eq 'default' ) {
141 $column->{'default'} = $c->{'value'};
143 elsif ( $c->{'type'} eq 'autoincrement' ) {
144 $column->{'is_auto_inc'} = 1;
151 type : WORD parens_value_list(?)
159 column_constraint : NOT_NULL conflict_clause(?)
166 PRIMARY_KEY sort_order(?) conflict_clause(?)
169 type => 'primary_key',
170 sort_order => $item[2][0],
171 on_conflict => $item[2][0],
175 UNIQUE conflict_clause(?)
179 on_conflict => $item[2][0],
183 CHECK_C '(' expr ')' conflict_clause(?)
187 expression => $item[3],
188 on_conflict => $item[5][0],
203 type => 'foreign_key',
204 reference_table => $item[2]{'reference_table'},
205 reference_fields => $item[2]{'reference_fields'},
212 type => 'autoincrement',
216 constraint_def : PRIMARY_KEY parens_field_list conflict_clause(?)
219 supertype => 'constraint',
220 type => 'primary_key',
222 on_conflict => $item[3][0],
226 UNIQUE parens_field_list conflict_clause(?)
229 supertype => 'constraint',
232 on_conflict => $item[3][0],
236 CHECK_C '(' expr ')' conflict_clause(?)
239 supertype => 'constraint',
241 expression => $item[3],
242 on_conflict => $item[5][0],
246 ref_def : /(\w+)\s*\((\w+)\)/
247 { $return = { reference_table => $1, reference_fields => $2 } }
249 table_name : qualified_name
251 qualified_name : NAME
252 { $return = { name => $item[1] } }
254 qualified_name : /(\w+)\.(\w+)/
255 { $return = { db_name => $1, name => $2 } }
259 conflict_clause : /on conflict/i conflict_algorigthm
261 conflict_algorigthm : /(rollback|abort|fail|ignore|replace)/i
263 parens_field_list : '(' column_list ')'
266 column_list : field_name(s /,/)
268 parens_value_list : '(' VALUE(s /,/) ')'
273 sort_order : /(ASC|DESC)/i
278 create : CREATE TEMPORARY(?) TRIGGER NAME before_or_after(?) database_event ON table_name trigger_action SEMICOLON
280 my $table_name = $item[8]->{'name'};
283 is_temporary => $item[2][0] ? 1 : 0,
286 db_events => [ $item[6] ],
288 on_table => $table_name,
292 create : CREATE TEMPORARY(?) TRIGGER NAME instead_of database_event ON view_name trigger_action
294 my $table_name = $item[8]->{'name'};
297 is_temporary => $item[2][0] ? 1 : 0,
300 db_events => [ $item[6] ],
302 on_table => $table_name,
306 database_event : /(delete|insert|update)/i
308 database_event : /update of/i column_list
310 trigger_action : for_each(?) when(?) BEGIN_C trigger_step(s) END_C
313 for_each => $item[1][0],
319 for_each : /FOR EACH ROW/i
321 when : WHEN expr { $item[2] }
324 /'(\\.|''|[^\\\'])*'/
326 nonstring : /[^;\'"]+/
328 statement_body : string | nonstring
330 trigger_step : /(select|delete|insert|update)/i statement_body(s?) SEMICOLON
332 $return = join( ' ', $item[1], join ' ', @{ $item[2] || [] } )
335 before_or_after : /(before|after)/i { $return = lc $1 }
337 instead_of : /instead of/i
339 if_exists : /if exists/i
341 view_name : qualified_name
343 trigger_name : qualified_name
348 create : CREATE TEMPORARY(?) VIEW view_name AS select_statement
351 name => $item[4]->{'name'},
353 is_temporary => $item[2][0] ? 1 : 0,
357 select_statement : SELECT /[^;]+/ SEMICOLON
359 $return = join( ' ', $item[1], $item[2] );
369 TRANSACTION: /transaction/i
373 TEMPORARY : /temp(orary)?/i { 1 }
379 NOT_NULL : /not null/i
381 PRIMARY_KEY : /primary key/i
401 REFERENCES : /references/i
403 AUTOINCREMENT : /autoincrement/i
405 UNIQUE : /unique/i { 1 }
409 NAME : /["']?(\w+)["']?/ { $return = $1 }
411 VALUE : /[-+]?\.?\d+(?:[eE]\d+)?/
415 # remove leading/trailing quotes
417 $val =~ s/^['"]|['"]$//g;
422 | /CURRENT_TIMESTAMP/i
423 { 'CURRENT_TIMESTAMP' }