File Coverage

blib/lib/SQL/Yapp.pm
Criterion Covered Total %
statement 2384 2945 80.9
branch 715 1112 64.3
condition 146 290 50.3
subroutine 519 723 71.7
pod 0 298 0.0
total 3764 5368 70.1


line stmt bran cond sub pod time code
1             #! /usr/bin/perl -w
2              
3             package SQL::Yapp;
4              
5 5     5   282147 use strict;
  5         37  
  5         114  
6 5     5   18 use warnings;
  5         7  
  5         123  
7 5     5   17 use vars qw($VERSION @EXPORT_OK);
  5         8  
  5         290  
8 5     5   23 use base qw(Exporter);
  5         6  
  5         575  
9 5     5   26 use Carp qw(longmess carp croak confess);
  5         8  
  5         230  
10 5     5   2159 use Hash::Util qw(lock_keys lock_hash);
  5         10528  
  5         24  
11 5     5   340 use Scalar::Util qw(looks_like_number blessed);
  5         7  
  5         161  
12 5     5   491 use Data::Dumper;
  5         5275  
  5         174  
13 5     5   2137 use Filter::Simple;
  5         89369  
  5         27  
14 5     5   277 use Text::Balanced;
  5         13  
  5         391  
15              
16             require v5.8;
17              
18             $VERSION= 2.002;
19              
20             @EXPORT_OK=qw(
21             dbh
22             get_dbh
23             quote
24             quote_identifier
25             check_identifier
26             runtime_check
27             xlat_catalog
28             xlat_schema
29             xlat_table
30             xlat_column
31             xlat_charset
32             xlat_collate
33             xlat_constraint
34             xlat_index
35             xlat_transliteration
36             xlat_transcoding
37             xlat_engine
38             parse
39             ASTERISK
40             QUESTION
41             NULL
42             TRUE
43             FALSE
44             UNKNOWN
45             DEFAULT
46             );
47              
48 5     5   33 use constant SQL_MARK => "\0__SQL__";
  5         12  
  5         302  
49 5     5   23 use constant COMMA_STR => ', ';
  5         8  
  5         179  
50 5     5   21 use constant LARGE_LIMIT_CNT => '18446744073709551615';
  5         6  
  5         196  
51              
52 5     5   21 use constant NOT_IN_LIST => 0;
  5         14  
  5         186  
53 5     5   27 use constant IN_LIST => 1;
  5         22  
  5         207  
54              
55 5     5   23 use constant NO_PARENS => 0;
  5         7  
  5         182  
56 5     5   24 use constant PARENS => 1;
  5         14  
  5         179  
57              
58 5     5   21 use constant NO_SHIFT => 0;
  5         7  
  5         184  
59 5     5   34 use constant SHIFT => 1;
  5         15  
  5         11546  
60              
61             my $get_dbh= undef;
62             my $quote_id= undef;
63             my $quote_val= undef;
64             my $quote_id_default= undef;
65             my $quote_val_default= undef;
66             my $xlat_catalog= sub($) { $_[0] };
67             my $xlat_schema= sub($) { $_[0] };
68             my $xlat_table= sub($) { $_[0] };
69             my $xlat_column= sub($) { $_[0] };
70             my $xlat_charset= sub($) { $_[0] };
71             my $xlat_collate= sub($) { $_[0] };
72             my $xlat_constraint= sub($) { $_[0] };
73             my $xlat_index= sub($) { $_[0] };
74             my $xlat_transliteration= sub($) { $_[0] };
75             my $xlat_transcoding= sub($) { $_[0] };
76             my $xlat_engine= sub($) { $_[0] };
77             my $check_identifier= sub($$$$$) { };
78             my $runtime_check= 0;
79             my $sql_marker= 'sql';
80             my $do_prec= 0;
81             my $debug= 0;
82              
83             my %dialect= ( # known dialects
84             generic => 1,
85             std => 1,
86             mysql => 1,
87             postgresql => 1,
88             oracle => 1,
89             );
90              
91             my $write_dialect= 'generic'; # not well-supported yet, only a few things are done
92             my %read_dialect= (
93             mysql => 1,
94             postgresql => 1,
95             oracle => 1,
96             );
97              
98             # SQL often has several tokens used as one keyword. In order to
99             # simplify the parser, we combine them in the scanner already. This
100             # also produces nicer error messages with more information for the
101             # user (e.g. 'Unexpected IS NOT NULL'...).
102             my %multi_token= (
103             IS => {
104             NULL => {},
105             TRUE => {},
106             FALSE => {},
107             UNKNOWN => {},
108             NORMALIZED => {},
109             NOT => {
110             NULL => {},
111             TRUE => {},
112             FALSE => {},
113             UNKNOWN => {},
114             NORMALIZED => {},
115             A => { SET => {} },
116             OF => {},
117             },
118             DISTINCT => { FROM => {} },
119             A => { SET => {} },
120             OF => {},
121             },
122             GROUP => { BY => {} },
123             ORDER => { BY => {} },
124             WITH => {
125             ROLLUP => {},
126             ORDINALITY => {},
127             LOCAL => { TIME => { ZONE => {} } },
128             TIME => { ZONE => {} },
129             },
130             WITHOUT => { TIME => { ZONE => {} } },
131             FOR => {
132             UPDATE => {},
133             SHARE => {},
134             },
135             LOCK => { IN => { SHARE => { MODE => {} } } },
136              
137             SIMILAR => { TO => {} },
138             BETWEEN => {
139             SYMMETRIC => {},
140             ASYMMETRIC => {},
141             },
142             MEMBER => { OF => {} },
143              
144             PRIMARY => { KEY => {} },
145             FOREIGN => { KEY => {} },
146              
147             CHARACTER => {
148             SET => {},
149             VARYING => {},
150             },
151             NATIONAL => { CHARACTER => {} },
152             NCHAR => {
153             VARYING => {}
154             },
155             DEFAULT => {
156             VALUES => {},
157             CHARACTER => {
158             SET => {}
159             },
160             },
161             ON => {
162             DUPLICATE => { KEY => { UPDATE => {} } },
163             DELETE => {},
164             UPDATE => {},
165             COMMIT => {},
166             },
167             OVERRIDING => {
168             USER => { VALUE => {} },
169             SYSTEM => { VALUE => {} }
170             },
171             CREATE => {
172             TABLE => {},
173             LOCAL => {
174             TABLE => {},
175             TEMPORARY => { TABLE => {} },
176             },
177             GLOBAL => {
178             TABLE => {},
179             TEMPORARY => { TABLE => {} },
180             },
181             INDEX => {},
182             },
183             ALTER => {
184             TABLE => {},
185             IGNORE => { TABLE => {} },
186             ONLINE => {
187             TABLE => {},
188             IGNORE => { TABLE => {} },
189             },
190             OFFLINE => {
191             TABLE => {},
192             IGNORE => { TABLE => {} },
193             },
194             COLUMN => {
195             },
196             },
197             DROP => {
198             TABLE => {},
199             TEMPORARY => { TABLE => {} },
200              
201             SIGN => {},
202             ZEROFILL => {},
203             COLLATE => {},
204             TIME => { ZONE => {} },
205             CHARACTER => { SET => {} },
206              
207             DEFAULT => {},
208             UNIQUE => {},
209             AUTO_INCREMENT => {},
210             UNIQUE => { KEY => {} },
211             PRIMARY => { KEY => {} },
212             FOREIGN => { KEY => {} },
213             KEY => {},
214             INDEX => {},
215              
216             NOT => { NULL => {} },
217              
218             COLUMN => {},
219             CONSTRAINT => {},
220             },
221             NOT => {
222             LIKE => {},
223             CLIKE => {},
224             SIMILAR => { TO => {}, },
225             BETWEEN => {
226             SYMMETRIC => {},
227             ASYMMETRIC => {},
228             },
229             MEMBER => { OF => {} },
230             NULL => {},
231             },
232              
233             NO => {
234             ACTION => {},
235             },
236              
237             BINARY => { VARYING => {} },
238             TEXT => { BINARY => {} },
239             TINYTEXT => { BINARY => {} },
240             MEDIUMTEXT => { BINARY => {} },
241             LONGTEXT => { BINARY => {} },
242             UNIQUE => { KEY => {} },
243             IF => {
244             NOT => { EXISTS => {} }, # Ouch! (should be :if-does-not-exist, of course)
245             EXISTS => {},
246             },
247             SET => {
248             NULL => {},
249             DEFAULT => {},
250             NOT => { NULL => {} },
251             SET => { DATA => { TYPE => {} } },
252             },
253             PRESERVE => { ROWS => {} },
254             DELETE => { ROWS => {} },
255             RENAME => {
256             TO => {},
257             COLUMN => {},
258             },
259             ADD => {
260             COLUMN => {},
261             },
262             MODIFY => {
263             COLUMN => {},
264             },
265             CHANGE => {
266             COLUMN => {},
267             },
268             );
269             my %synonym= (
270             'NORMALISED' => 'NORMALIZED',
271             'CHAR' => 'CHARACTER',
272             'CHAR_LENGTH' => 'CHARACTER_LENGTH',
273             'CHARACTER VARYING' => 'VARCHAR',
274             'NATIONAL CHARACTER' => 'NCHAR',
275             'CHAR LARGE OBJECT' => 'CLOB',
276             'NCHAR LARGE OBJECT' => 'NCLOB',
277             'BINARY LARGE OBJECT' => 'BLOB',
278             'NVARCHAR' => 'NCHAR VARYING',
279             'DEC' => 'DECIMAL',
280             'INTEGER' => 'INT',
281             'BINARY VARYING' => 'VARBINARY',
282             'CHARSET' => 'CHARACTER SET',
283             'TEMP' => 'TEMPORARY',
284             );
285              
286             my %type_spec= ();
287              
288             my @SELECT_INITIAL= (
289             'SELECT',
290             # 'WITH' # NOT YET
291             );
292              
293             my @CREATE_TABLE_INITIAL= (
294             'CREATE TABLE',
295             'CREATE TEMPORARY TABLE',
296             'CREATE LOCAL TABLE',
297             'CREATE GLOBAL TABLE',
298             'CREATE LOCAL TEMPORARY TABLE',
299             'CREATE GLOBAL TEMPORARY TABLE',
300             );
301              
302             my @DROP_TABLE_INITIAL= (
303             'DROP TABLE',
304             'DROP TEMPORARY TABLE',
305             );
306              
307             my @ALTER_TABLE_INITIAL= (
308             'ALTER TABLE',
309             'ALTER IGNORE TABLE',
310             'ALTER ONLINE TABLE',
311             'ALTER ONLINE IGNORE TABLE',
312             'ALTER OFFLINE TABLE',
313             'ALTER OFFLINE IGNORE TABLE',
314             );
315              
316             ######################################################################
317             # Use settings:
318              
319             sub get_set
320             {
321 20     20 0 34 my $var= shift;
322 20         26 my $r= $$var;
323 20 50       42 ($$var)= @_ if scalar(@_);
324 20         40 return $;
325             }
326              
327             sub get_dbh()
328             {
329 0     0 0 0 return $get_dbh->();
330             }
331              
332             sub dbh(;&)
333             {
334 1     1 0 3 get_set (\$get_dbh, @_);
335 1 50       3 if ($get_dbh) {
336 1     0   3 $quote_id_default= sub(@) { $get_dbh->()->quote_identifier(@_); };
  0         0  
337 1     0   3 $quote_val_default= sub($) { $get_dbh->()->quote($_[0]); };
  0         0  
338             }
339             else {
340 0         0 $quote_id_default= undef;
341 0         0 $quote_val_default= undef;
342             }
343             }
344              
345 4     4 0 10 sub quote_identifier(;&) { get_set (\$quote_id, @_); }
346 4     4 0 10 sub quote(;&) { get_set (\$quote_val, @_); }
347 2     2 0 4 sub xlat_catalog(;&) { get_set (\$xlat_catalog, @_); }
348 2     2 0 5 sub xlat_schema(;&) { get_set (\$xlat_schema, @_); }
349 4     4 0 12 sub xlat_table(;&) { get_set (\$xlat_table, @_); }
350 2     2 0 6 sub xlat_column(;&) { get_set (\$xlat_column, @_); }
351 0     0 0 0 sub xlat_charset(;&) { get_set (\$xlat_charset, @_); }
352 0     0 0 0 sub xlat_collate(;&) { get_set (\$xlat_collate, @_); }
353 0     0 0 0 sub xlat_constraint(;&) { get_set (\$xlat_constraint, @_); }
354 0     0 0 0 sub xlat_index(;&) { get_set (\$xlat_index, @_); }
355 0     0 0 0 sub xlat_transcoding(;&) { get_set (\$xlat_transcoding, @_); }
356 0     0 0 0 sub xlat_transliteration(;&) { get_set (\$xlat_transliteration, @_); }
357 0     0 0 0 sub xlat_engine(;&) { get_set (\$xlat_engine, @_); }
358              
359 0     0 0 0 sub check_identifier(;&) { get_set (\$check_identifier, @_); }
360 0     0 0 0 sub runtime_check(;$) { get_set (\$runtime_check, @_); }
361              
362 1     1 0 3 sub sql_marker(;$) { get_set (\$sql_marker, @_); } # used only internally
363              
364 2     2 0 5 sub catalog_prefix($) { my ($p)= @_; xlat_catalog { $p.$_[0] }; }
  2     2   8  
  2         7  
365 2     2 0 4 sub schema_prefix($) { my ($p)= @_; xlat_schema { $p.$_[0] }; }
  2     2   10  
  2         6  
366 124     124 0 245 sub table_prefix($) { my ($p)= @_; xlat_table { $p.$_[0] }; }
  4     4   8  
  4         14  
367 68     68 0 164 sub column_prefix($) { my ($p)= @_; xlat_column { $p.$_[0] }; }
  2     2   474  
  2         10  
368 0     0 0 0 sub constraint_prefix($) { my ($p)= @_; xlat_constraint { $p.$_[0] }; }
  0     0   0  
  0         0  
369              
370 0     0 0 0 sub debug($) { ($debug)= @_; }
371              
372             sub read_dialect1($)
373             {
374 0     0 0 0 my ($s)= @_;
375 0 0       0 if ($s eq 'all') {
376 0         0 for my $s1 (keys %dialect) {
377 0         0 $read_dialect{$s1}= 1;
378             }
379             }
380             else {
381 0 0       0 croak "Unknown dialect: read_dialect=$s" unless $dialect{$s};
382 0         0 $read_dialect{$s}= 1;
383             }
384             }
385              
386             sub read_dialect($)
387             {
388 0     0 0 0 my ($s)= @_;
389 0         0 %read_dialect=();
390 0 0       0 if (!ref($s)) {
    0          
391 0         0 read_dialect1($s);
392             }
393             elsif (ref($s) eq 'ARRAY') {
394 0         0 for my $s1 (@$s) {
395 0         0 read_dialect1($s1);
396             }
397             }
398             else {
399 0         0 die "Illegal reference: ".ref($s);
400             }
401             }
402              
403             sub write_dialect($)
404             {
405 9     9 0 701 my ($s)= @_;
406 9 50       23 croak "Unknown dialect: write_dialect=$s" unless $dialect{$s};
407 9         19 $write_dialect= $s;
408             }
409              
410             sub dialect($)
411             {
412 0     0 0 0 my ($s)= @_;
413 0         0 read_dialect($s);
414 0         0 write_dialect($s);
415             }
416              
417             ######################################################################
418             # Init
419              
420             my %import_handler_nonref= (
421             'marker' => \&sql_marker,
422             'catalog_prefix' => \&catalog_prefix,
423             'schema_prefix' => \&schema_prefix,
424             'table_prefix' => \&table_prefix,
425             'column_prefix' => \&column_prefix,
426             'constraint_prefix' => \&constraint_prefix,
427             'debug' => \&debug,
428             'read_dialect' => \&read_dialect,
429             'write_dialect' => \&write_dialect,
430             'dialect' => \&dialect,
431             );
432             my %import_handler_bool= (
433             'runtime_check' => \&runtime_check,
434             );
435             my %import_handler_ref= (
436             'dbh' => \&dbh,
437             'quote' => \"e,
438             'quote_identifier' => \"e_identifier,
439             'xlat_catalog' => \&xlat_catalog,
440             'xlat_schema' => \&xlat_schema,
441             'xlat_table' => \&xlat_table,
442             'xlat_column' => \&xlat_column,
443             'xlat_charset' => \&xlat_charset,
444             'xlat_collate' => \&xlat_collate,
445             'xlat_constraint' => \&xlat_constraint,
446             'xlat_index' => \&xlat_index,
447             'xlat_transliteration' => \&xlat_transliteration,
448             'xlat_transcoding' => \&xlat_transcoding,
449             'xlat_engine' => \&xlat_engine,
450             'check_identifier' => \&check_identifier,
451             );
452              
453             sub type_spec()
454             {
455             return (
456             'DOUBLE PRECISION' => 'INT',
457             'REAL' => 'INT',
458             'BIGINT' => 'INT',
459             'SMALLINT' => 'INT',
460             'INT' => {
461             },
462              
463             # numbers with 0 or 1 precision marker:
464             'FLOAT' => {
465             prec1 => 1,
466             },
467              
468             # numbers with 0, 1, or 2 precision numbers:
469             'NUMERIC' => 'DECIMAL',
470             'DECIMAL' => {
471             prec1 => 1,
472             prec2 => 1,
473             },
474              
475             # character strings:
476             'VARCHAR' => 'CHARACTER',
477             'CHARACTER' => {
478             prec1 => 1,
479             charset => 1,
480             collate => 1,
481             },
482              
483             # clobs:
484             'CLOB' => {
485             prec1 => 1,
486             prec_mul => 1,
487             prec_unit => 1,
488             charset => 1,
489             collate => 1,
490             },
491              
492             # nchar:
493             'NCHAR VARYING' => 'NCHAR',
494             'NCHAR' => {
495             prec1 => 1,
496             collate => 1,
497             },
498              
499             # nclobs:
500             'NCLOB' => {
501             prec1 => 1,
502             prec_mul => 1,
503             prec_unit => 1,
504             collate => 1,
505             },
506              
507             # binary strings:
508             'VARBINARY' => 'BINARY', # not standard
509             'BINARY' => {
510             prec1 => 1,
511             },
512              
513             # blobs:
514             'BLOB' => {
515             prec1 => 1,
516             prec_mul => 1,
517             prec_unit => 1,
518             },
519              
520             # simple types without further attributes or lengths:
521             'SERIAL' => 'BOOLEAN', # column spec, but handled as type for simplicity reasons
522             'BOOLEAN' => {
523             },
524              
525             # date/time:
526             'DATE' => 'TIME',
527             'TIMESTAMP' => 'TIME',
528             'TIME' => {
529             timezone => 1
530             },
531              
532             # Dialects come last because they may redefine above settings:
533             # If two dialects are contracting, you must find a common solution
534             # and put it at the end of this list:
535             ($read_dialect{mysql} ?
536             (
537             'SMALLINT' => 'INT',
538             'BIGINT' => 'INT',
539             'TINYINT' => 'INT',
540             'MEDIUMINT' => 'INT',
541             'BIT' => 'INT',
542             'BIT VARYING' => 'INT',
543             'FLOAT' => 'INT',
544             'INT' => {
545             prec1 => 1,
546             zerofill => 1,
547             sign => 1,
548             },
549              
550             'FLOAT' => 'NUMERIC',
551             'DECIMAL' => 'NUMERIC',
552             'REAL' => 'NUMERIC',
553             'DOUBLE' => 'NUMERIC',
554             'NUMERIC' => {
555             prec1 => 1,
556             prec2 => 1,
557             zerofill => 1,
558             sign => 1,
559             },
560              
561             'DATETIME' => 'TIME',
562             'YEAR' => 'TIME',
563              
564             'TINYBLOB' => 'BINARY',
565             'MEDIUMBLOB' => 'BINARY',
566             'LONGBLOB' => 'BINARY',
567              
568             'TINYTEXT' => 'CHARACTER',
569             'MEDIUMTEXT' => 'CHARACTER',
570             'LONGTEXT' => 'CHARACTER',
571             'TEXT' => 'CHARACTER',
572              
573             'TINYTEXT BINARY' => 'CHARACTER',
574             'MEDIUMTEXT BINARY' => 'CHARACTER',
575             'LONGTEXT BINARY' => 'CHARACTER',
576             'TEXT BINARY' => 'CHARACTER',
577              
578             'ENUM' => {
579             value_list => 1,
580             charset => 1,
581             collate => 1,
582             },
583              
584             'SET' => {
585             value_list => 1,
586             charset => 1,
587             collate => 1,
588             },
589             )
590             : ()
591             ),
592             ($read_dialect{postgresql} ?
593             (
594             'BYTEA' => 'BINARY',
595             'INT2' => 'INT',
596             'INT4' => 'INT',
597             'INT8' => 'INT',
598             'POINT' => 'BOOLEAN',
599             'LINE' => 'BOOLEAN',
600             'LSEG' => 'BOOLEAN',
601             'BOX' => 'BOOLEAN',
602             'PATH' => 'BOOLEAN',
603             'POLYGON' => 'BOOLEAN',
604             'CIRCLE' => 'BOOLEAN',
605             'MONEY' => 'BOOLEAN',
606             'IRDR' => 'BOOLEAN',
607             'INET' => 'BOOLEAN',
608             'MACADDR' => 'BOOLEAN',
609             'UUID' => 'BOOLEAN',
610             'TEXT' => 'CHARACTER',
611             'SERIAL4' => 'SERIAL',
612             'SERIAL8' => 'SERIAL',
613             'BIGSERIAL' => 'SERIAL',
614             )
615             : ()
616             ),
617             ($read_dialect{oracle} ?
618             (
619 5 50   5 0 394 'NUMBER' => 'NUMERIC'
    50          
    50          
620             )
621             : ()
622             ),
623             );
624             }
625              
626             sub import
627             {
628             my ($pack, @opt)= @_;
629             my @super_param= ();
630             my $i=0;
631             while ($i < scalar(@opt)) {
632             my $k= $opt[$i];
633             if ($i+1 < scalar(@opt)) {
634             my $v= $opt[$i+1];
635             if (my $handler= $import_handler_nonref{$k}) {
636             $handler->($v);
637             $i++;
638             }
639             elsif ($v eq '0' || $v eq '1') {
640             if (my $handler= $import_handler_bool{$k}) {
641             $handler->($v);
642             $i++;
643             }
644             else {
645             croak "Error: Unrecognised package option for ".__PACKAGE__.": $k\n";
646             }
647             }
648             elsif (ref($v)) {
649             if (my $handler= $import_handler_ref{$k}) {
650             $handler->($v);
651             $i++;
652             }
653             else {
654             croak "Error: Unrecognised package option for ".__PACKAGE__.": $k\n";
655             }
656             }
657             else {
658             push @super_param, $k;
659             }
660             }
661             else {
662             push @super_param, $k;
663             }
664             $i++;
665             }
666              
667             &Exporter::import($pack,@super_param);
668              
669             %type_spec= type_spec();
670             }
671              
672             ######################################################################
673             # Tools
674              
675             sub my_dumper($)
676             {
677 1     1 0 3 my ($x)= @_;
678              
679 1         10 my $d= Data::Dumper->new([$x],['x']);
680 1         45 $d->Terse(1);
681 1         16 $d->Purity(1);
682 1         8 $d->Indent(1);
683              
684 1         15 my $s= $d->Dump;
685 1 50       194 return $s
686             if length($s) <= 400;
687              
688 0         0 return substr($s,0,400).'...';
689             }
690              
691             # longmess gives me: bizarre copy of hash. So confess does not work.
692             # Don't ask me why, I spent some time to debug this, but now I am
693             # sick of it. So here's my primitive version:
694             sub my_longmess()
695             {
696 3     3 0 5 my $i= 2;
697 3         4 my @mess= ();
698 3         24 while (my ($pack, $file, $line, $function)= caller($i)) {
699 61         133 push @mess, "\t$file:$line: ${pack}::${function}\n";
700 61         194 $i++;
701             }
702 3         68 return "Call Stack:\n".join('', reverse @mess);
703             }
704              
705             sub my_confess(;$)
706             {
707 3   50 3 0 10 die my_longmess.'DIED: '.($_[0] || 'Error');
708             }
709              
710             ######################################################################
711             # Non-trivial access to module variables:
712              
713             sub get_quote_val()
714             {
715             return
716             $quote_val ||
717             $quote_val_default ||
718 205   33 205 0 525 do {
719             croak "Error: No quote() function set.\n".
720             "\tUse ".__PACKAGE__."::quote() or ".__PACKAGE__."::dbh().\n";
721             };
722             }
723              
724             sub get_quote_id()
725             {
726             return
727             $quote_id ||
728             $quote_id_default ||
729 379   33 379 0 981 do {
730             croak "Error: No quote_identifier() function set.\n".
731             "\tUse ".__PACKAGE__."::quote_identifier() or ".__PACKAGE__."::dbh().\n";
732             };
733             }
734              
735             ######################################################################
736             # Recursive Descent parser:
737              
738             # This is pure theory, because it will probably not occur, but:
739             #
740             # Assume:
741             # not b + c == not (b + c) ; just like in SQL
742             # a * b + c == (a * b) + c
743             #
744             # => a * not b + c == (a * not b) + c ; illegal in SQL for another reason, but
745             # ; still. Assume it was ok and numeric
746             # ; and boolean could be mixed at will.
747             #
748             # => parsing of the + sign is influenced not only by the immediate predecessor
749             # operator 'sin', but also by '*'.
750             #
751             # This is currently not so. Instead a * not b + c is parsed as a * not(b + c).
752             # I checked this with the Perl parser, which does the same:
753             #
754             # my $a= 1 && not 0 || 1; # ==> $a == 0
755             #
756             # Anyway, precedences are currently disabled, because of so much confusion, and
757             # particularly because of different precedences of the = operator in different
758             # positions.
759              
760 5     5   35 use constant ASSOC_NON => undef;
  5         5  
  5         308  
761 5     5   23 use constant ASSOC_LEFT => -1;
  5         8  
  5         232  
762 5     5   26 use constant ASSOC_RIGHT => +1;
  5         6  
  5         30164  
763              
764             sub make_op($$;%)
765             {
766 461     461 0 678 my ($value, $type, %opt)= @_;
767 461   66     945 my $read_value= $opt{read_value} || $value;
768 461   66     743 my $read_type= $opt{read_type} || $type;
769             my $result= {
770             read_value => $read_value,
771             value => $value,
772             value2 => $opt{value2}, # for infix3
773             read_type => $read_type, # how to parse?
774             type => $type, # how to print?
775             result0 => $opt{result0}, # for 'infix()' operators invoked with 0 arguments
776             # if undef => error to invoke with 0 arguments
777             prec => $opt{prec},
778             assoc => $opt{assoc},
779             rhs => $opt{rhs} || 'expr',
780             rhs_map => $opt{rhs_map} || {},
781             comparison => $opt{comparison}, # for checking ANY/SOME and ALL
782             dialect => $opt{dialect} || {},
783             accept => $opt{accept},
784             allow_when => $opt{allow_when},
785 461   100     3229 };
      50        
      100        
786 461         1028 lock_hash %$result;
787 461         11225 return $result;
788             }
789              
790             sub declare_op($$;%)
791             {
792 385     385 0 658 my ($value, $type, %opt)= @_;
793 385         584 my $result= make_op($value, $type, %opt);
794 385         1168 return ($result->{read_value} => $result);
795             }
796              
797             # There are two ways of normalising a functor:
798             # (a) Accepting a secondary form for an otherwise standard, and widely supported
799             # functor. Example: the power function. The std say it's called 'POWER',
800             # and this is how we want to always normalise it. To accept the MySQL form
801             # with infixed ^, use the read_value attribute:
802             #
803             # declare_op('POWER', 'funcall', ... read_value => '^');
804             #
805             # The 'dialect' hash keys should not defined were because there's a perfect
806             # normalisation for all dialects and accepting ^ is only a convenience.
807             #
808             # These normalisations will *always* be done.
809             #
810             # (b) Translating between non-standard or unavailable operators: here, we need
811             # to know which dialect we produce. It we don't, we keep what the user
812             # wrote and pass the syntax on as is. For translation, use the 'dialect'
813             # hash table to define how to write the operator in different output modes.
814             # if the output more is not found, the operator will not be touched:
815             #
816             # declare_op('||', 'infix()', ...
817             # dialect => {
818             # mysql => make_op('CONCAT', 'funcall')
819             # }
820             # ),
821             #
822             # If the current print dialect is not found, nothing is changed, otherwise
823             # the settings are taken from the corresponding hash entry. If a '-default'
824             # is given, then that one is used for default normalisation.
825             # If the value of a hash entry is 1 instead of a ref(), then the functor
826             # is not normalised for that dialect.
827             #
828             # For reducing input acception, use the 'accept' list: e.g. to accept the
829             # XOR operator only in MySQL and Postgres modes, use:
830             #
831             # declare_op('XOR', 'infix()', ... accept => [ 'mysql', 'postgresql' ]);
832             #
833             # ONLY restrict the input syntax if the input cannot be normalised in a
834             # standard way. Currently, we have no strict input mode: we only reject what
835             # cannot be rectified, regardless of %read_dialect, and that's the rule for now.
836             #
837             # Also note: you cannot freely switch type, but only if the number of
838             # parameters of the write type subsumes those of the read type:
839             #
840             # min max
841             # funcall 0 undef
842             # funcall1col 1 1 # one param which is a column name
843             # infix() 0/1 undef # min depends on whether result0 is set
844             # prefix 1 1
845             # prefixn 1 1 # never parens around param
846             # prefix1 1 1 # disallows point-wise application
847             # suffix 1 1
848             # infix2 2 2
849             # infix23 2 3
850             # infix3 3 3
851             #
852             # Note that all used symbolic operators must be known to token_scan_rec(),
853             # otherwise they are not correctly extracted from the input stream.
854              
855             #
856             # Missing:
857             #
858             # & | ~ (bit operations in MySQL)
859             #
860             # :: CAST (or TREAT?) in PostgreSQL
861             #
862              
863             # If the type is found in the following table, stringification will be
864             # handled by _prefix() and _suffix(). Otherwise, the compiled Perl
865             # code will already contain the logic of how to build the SQL command.
866             my %functor_kind= (
867             'infix()' => 'suffix',
868             'infix2' => 'suffix',
869             #'infix23' => 'suffix', # complex syntax, cannot be changed later, see funcsep
870             #'infix3' => 'suffix', # complex syntax, cannot be changed later, see funcsep
871              
872             'funcall' => 'prefix',
873             #'funcsep' => 'prefix', # complex syntax, currently not supported
874              
875             # Not built via _suffix() or _prefix():
876             #
877             # prefixn
878              
879             'suffix' => 'suffix', # applied point-wise, different from funcall
880             'prefix' => 'prefix', # applied point-wise, different from funcall
881             'funcall1' => 'prefix', # applied point-wise, different from funcall
882             'prefix()' => 'prefix', # not applied point-wise
883             );
884             my %functor_suffix= ( # for functors read in infix or suffix notation
885             # aliasses:
886             '==' => '=',
887             '!=' => '<>',
888              
889             # infix2 and infix():
890             declare_op('POWER', 'funcall', prec => 80, assoc => ASSOC_RIGHT,
891             read_value => '**', read_type => 'infix2'), # Oracle
892             #declare_op('POWER', 'funcall', prec => 80, assoc => ASSOC_RIGHT,
893             # read_value => '^', read_type => 'infix2'), # not MySQL
894             #declare_op('POWER', 'funcall', prec => 80, assoc => ASSOC_RIGHT
895             # read_value => ':', read_type => 'infix2'), # Postgres??
896              
897             # bitwise operators:
898             declare_op('^', 'infix()', result0 => 0,
899             assoc => ASSOC_LEFT,
900             dialect => {
901             oracle => make_op('BITXOR', 'funcall'),
902             }),
903              
904             declare_op('|', 'infix()', result0 => 0,
905             assoc => ASSOC_LEFT,
906             dialect => {
907             oracle => make_op('BITOR', 'funcall'),
908             }),
909              
910             declare_op('&', 'infix()', assoc => ASSOC_LEFT,
911             dialect => {
912             oracle => make_op('BITAND', 'funcall'),
913             }),
914              
915             # others:
916             declare_op('*', 'infix()', prec => 70, assoc => ASSOC_LEFT, result0 => 1),
917             declare_op('/', 'infix2', prec => 70, assoc => ASSOC_LEFT),
918              
919             declare_op('MOD', 'funcall', prec => 70, assoc => ASSOC_NON,
920             read_value => '%', read_type => 'infix2',), # MySQL, Postgres
921              
922             declare_op('+', 'infix()', prec => 60, assoc => ASSOC_LEFT, result0 => 0),
923             declare_op('-', 'infix2', prec => 60, assoc => ASSOC_LEFT),
924              
925             declare_op('=', 'infix2', prec => 50, assoc => ASSOC_NON, comparison => 1, allow_when => 1),
926             declare_op('<>', 'infix2', prec => 50, assoc => ASSOC_NON, comparison => 1, allow_when => 1),
927             declare_op('<', 'infix2', prec => 50, assoc => ASSOC_NON, comparison => 1, allow_when => 1),
928             declare_op('>', 'infix2', prec => 50, assoc => ASSOC_NON, comparison => 1, allow_when => 1),
929             declare_op('<=', 'infix2', prec => 50, assoc => ASSOC_NON, comparison => 1, allow_when => 1),
930             declare_op('>=', 'infix2', prec => 50, assoc => ASSOC_NON, comparison => 1, allow_when => 1),
931              
932             declare_op('AND', 'infix()', prec => 30, assoc => ASSOC_LEFT, result0 => 1),
933             declare_op('OR', 'infix()', prec => 30, assoc => ASSOC_LEFT, result0 => 0),
934              
935             declare_op('XOR', 'infix()', prec => 30, assoc => ASSOC_LEFT, result0 => 0,
936             accept => [ 'mysql', 'postgresql', 'oracle']),
937              
938             declare_op('||', 'infix()', assoc => ASSOC_LEFT, result0 => '',
939             dialect => {
940             mysql => make_op('CONCAT','funcall',result0 => ''),
941             }),
942              
943             declare_op('OVERLAPS', 'infix2', allow_when => 1),
944              
945             declare_op('IS DISTINCT FROM', 'infix2', allow_when => 1),
946              
947             declare_op('IS OF', 'infix2', rhs => 'type_list', allow_when => 1),
948             declare_op('IS NOT OF', 'infix2', rhs => 'type_list', allow_when => 1),
949              
950             declare_op('IN', 'infix2', rhs => 'expr_list', allow_when => 1),
951             declare_op('NOT IN', 'infix2', rhs => 'expr_list', allow_when => 1),
952              
953             # infix23
954             declare_op('NOT SIMILAR TO', 'infix23', value2 => 'ESCAPE', allow_when => 1),
955             declare_op('SIMILAR TO', 'infix23', value2 => 'ESCAPE', allow_when => 1),
956              
957             declare_op('LIKE', 'infix23', value2 => 'ESCAPE', allow_when => 1),
958             declare_op('NOT LIKE', 'infix23', value2 => 'ESCAPE', allow_when => 1),
959              
960             declare_op('CLIKE', 'infix23', value2 => 'ESCAPE', allow_when => 1),
961             declare_op('NOT CLIKE', 'infix23', value2 => 'ESCAPE', allow_when => 1),
962              
963             # infix3
964             declare_op('BETWEEN', 'infix3', value2 => 'AND', prec => 31, allow_when => 1),
965             declare_op('BETWEEN SYMMETRIC', 'infix3', value2 => 'AND', prec => 31, allow_when => 1),
966             declare_op('BETWEEN ASYMMETRIC', 'infix3', value2 => 'AND', prec => 31, allow_when => 1),
967             declare_op('NOT BETWEEN', 'infix3', value2 => 'AND', prec => 31, allow_when => 1),
968             declare_op('NOT BETWEEN SYMMETRIC', 'infix3', value2 => 'AND', prec => 31, allow_when => 1),
969             declare_op('NOT BETWEEN ASYMMETRIC', 'infix3', value2 => 'AND', prec => 31, allow_when => 1),
970              
971             # suffix
972             declare_op('IS NORMALIZED', 'suffix', prec => 45, allow_when => 1),
973             declare_op('IS NOT NORMALIZED', 'suffix', prec => 45, allow_when => 1),
974             declare_op('IS TRUE', 'suffix', prec => 45, allow_when => 1),
975             declare_op('IS NOT TRUE', 'suffix', prec => 45, allow_when => 1),
976             declare_op('IS FALSE', 'suffix', prec => 45, allow_when => 1),
977             declare_op('IS NOT FALSE', 'suffix', prec => 45, allow_when => 1),
978             declare_op('IS NULL', 'suffix', prec => 45, allow_when => 1),
979             declare_op('IS NOT NULL', 'suffix', prec => 45, allow_when => 1),
980             declare_op('IS UNKNOWN', 'suffix', prec => 45, allow_when => 1),
981             declare_op('IS NOT UNKNOWN', 'suffix', prec => 45, allow_when => 1),
982             declare_op('IS A SET', 'suffix', prec => 45, allow_when => 1),
983             declare_op('IS NOT A SET', 'suffix', prec => 45, allow_when => 1),
984             );
985              
986             my %functor_prefix= ( # functors read in prefix notation:
987             declare_op('+', 'prefix1', prec => 90, read_type => 'prefix'), # prefix1 disallows list context
988             declare_op('-', 'prefix', prec => 90),
989             declare_op('NOT', 'prefix', prec => 40),
990              
991             declare_op('~', 'prefix', dialect => { # MySQL
992             oracle => make_op('BITNOT', 'funcall'), # funcall1:
993             }),
994              
995             # Allow AND and OR as prefix operators.
996             # Because - and + are already defined, they are not translated this way.
997             declare_op('AND', 'prefix()',, read_type => 'prefix',
998             dialect => {
999             -default => make_op('AND', 'infix()', result0 => 1),
1000             }),
1001             declare_op('OR', 'prefix()', read_type => 'prefix',
1002             dialect => {
1003             -default => make_op('OR', 'infix()', result0 => 0),
1004             }),
1005              
1006             declare_op('BITXOR', 'funcall', assoc => ASSOC_LEFT,
1007             dialect => {
1008             mysql => make_op('^', 'infix()'),
1009             }),
1010             declare_op('BITOR', 'funcall', assoc => ASSOC_LEFT,
1011             dialect => {
1012             mysql => make_op('|', 'infix()'),
1013             }),
1014             declare_op('BITAND', 'funcall', assoc => ASSOC_LEFT,
1015             dialect => {
1016             mysql => make_op('&', 'infix()'),
1017             }),
1018              
1019             declare_op('POWER', 'funcall',
1020             read_value => 'POW'), # MySQL
1021              
1022             declare_op('CONCAT', 'funcall',
1023             dialect => {
1024             'mysql' => undef, # keep
1025             -default => make_op('||', 'infix()', result0 => ''),
1026             }),
1027              
1028             declare_op('CONCATENATE', 'funcall',
1029             dialect => {
1030             'mysql' => make_op('CONCAT', 'funcall'),
1031             -default => make_op('||', 'infix()', result0 => ''),
1032             }),
1033              
1034             declare_op('VALUES', 'funcall', accept => [ 'mysql' ], read_type => 'funcall1col'),
1035              
1036             # Funcalls with special separators instead of commas (who invented these??):
1037             # NOTE: These *must* start with (, otherwise they are even more special
1038             # than funcsep. Note that because of the hilarious syntax of UNNEST,
1039             # the closing paren is included in the rhs pattern.
1040             declare_op('CAST', 'funcsep',
1041             rhs => [ \q{expr}, 'AS', \q{type}, ')' ]),
1042              
1043             declare_op('TREAT', 'funcsep',
1044             rhs => [ \q{expr}, 'AS', \q{type}, ')' ]),
1045              
1046             declare_op('TRANSLATE', 'funcsep',
1047             rhs => [ \q{expr}, 'AS', \q{transliteration}, ')' ]),
1048              
1049             declare_op('POSITION','funcsep',
1050             rhs => [ \q{string_expr}, 'IN', \q{expr}, # hack for 'IN' infix op.
1051             [ 'USING', \q{char_unit} ], ')' ]),
1052              
1053             declare_op('SUBSTRING', 'funcsep',
1054             rhs => [ \q{expr}, 'FROM', \q{expr},
1055             [ 'FOR', \q{expr}], [ 'USING', \q{char_unit} ], ')' ]),
1056              
1057             declare_op('CHARACTER_LENGTH', 'funcsep',
1058             rhs => [ \q{expr}, [ 'USING', \q{char_unit} ], ')' ]),
1059              
1060             declare_op('CONVERT', 'funcsep',
1061             rhs => [ \q{expr}, 'USING', \q{transcoding}, ')' ]),
1062              
1063             declare_op('OVERLAY', 'funcsep',
1064             rhs => [ \q{expr}, 'PLACING', \q{expr}, 'FROM', \q{expr},
1065             [ 'FOR', \q{expr} ], [ 'USING', \q{char_unit} ], ')' ]),
1066              
1067             declare_op('EXTRACT', 'funcsep',
1068             rhs => [ \q{expr}, 'FROM', \q{expr}, ')']),
1069              
1070             declare_op('UNNEST', 'funcsep',
1071             rhs => [ \q{expr}, ')', [ 'WITH ORDINALITY' ] ]),
1072             );
1073              
1074             my %functor_special= ( # looked up manually, not generically.
1075             declare_op('ANY', 'prefixn'), # n=no paren. I know, it's lame.
1076             declare_op('SOME', 'prefixn'),
1077             declare_op('ALL', 'prefixn'),
1078             declare_op('DISTINCT', 'prefixn'),
1079             declare_op('DEFAULT', 'funcall', accept => [ 'mysql' ], read_type => 'funcall1col'),
1080             # Special functor because it collides with DEFAULT pseudo
1081             # constant, so it needs extra care during parsing.
1082             );
1083              
1084             # Reserved words from SQL-2003 spec:
1085             my @reserved= qw(
1086             ADD ALL ALLOCATE ALTER AND ANY ARE ARRAY AS ASENSITIVE ASYMMETRIC
1087             AT ATOMIC AUTHORIZATION BEGIN BETWEEN BIGINT BINARY BLOB BOOLEAN
1088             BOTH BY CALL CALLED CASCADED CASE CAST CHAR CHARACTER CHECK CLOB
1089             CLOSE COLLATE COLUMN COMMIT CONNECT CONSTRAINT CONTINUE
1090             CORRESPONDING CREATE CROSS CUBE CURRENT CURRENT_DATE
1091             CURRENT_DEFAULT_TRANSFORM_GROUP CURRENT_PATH CURRENT_ROLE
1092             CURRENT_TIME CURRENT_TIMESTAMP CURRENT_TRANSFORM_GROUP_FOR_TYPE
1093             CURRENT_USER CURSOR CYCLE DATE DAY DEALLOCATE DEC DECIMAL DECLARE
1094             DEFAULT DELETE DEREF DESCRIBE DETERMINISTIC DISCONNECT DISTINCT
1095             DOUBLE DROP DYNAMIC EACH ELEMENT ELSE END END-EXEC ESCAPE EXCEPT
1096             EXEC EXECUTE EXISTS EXTERNAL FALSE FETCH FILTER FLOAT FOR FOREIGN
1097             FREE FROM FULL FUNCTION GET GLOBAL GRANT GROUP GROUPING HAVING
1098             HOLD HOUR IDENTITY IMMEDIATE IN INDICATOR INNER INOUT INPUT
1099             INSENSITIVE INSERT INT INTEGER INTERSECT INTERVAL INTO IS
1100             ISOLATION JOIN LANGUAGE LARGE LATERAL LEADING LEFT LIKE LOCAL
1101             LOCALTIME LOCALTIMESTAMP MATCH MEMBER MERGE METHOD MINUTE MODIFIES
1102             MODULE MONTH MULTISET NATIONAL NATURAL NCHAR NCLOB NEW NO NONE NOT
1103             NULL NUMERIC OF OLD ON ONLY OPEN OR ORDER OUT OUTER OUTPUT OVER
1104             OVERLAPS PARAMETER PARTITION PRECISION PREPARE PRIMARY PROCEDURE
1105             RANGE READS REAL RECURSIVE REF REFERENCES REFERENCING RELEASE
1106             RETURN RETURNS REVOKE RIGHT ROLLBACK ROLLUP ROW ROWS SAVEPOINT
1107             SCROLL SEARCH SECOND SELECT SENSITIVE SESSION_USER SET SIMILAR
1108             SMALLINT SOME SPECIFIC SPECIFICTYPE SQL SQLEXCEPTION SQLSTATE
1109             SQLWARNING START STATIC SUBMULTISET SYMMETRIC SYSTEM SYSTEM_USER
1110             TABLE THEN TIME TIMESTAMP TIMEZONE_HOUR TIMEZONE_MINUTE TO
1111             TRAILING TRANSLATION TREAT TRIGGER TRUE UNION UNIQUE UNKNOWN
1112             UNNEST UPDATE USER USING VALUE VALUES VARCHAR VARYING WHEN
1113             WHENEVER WHERE WINDOW WITH WITHIN WITHOUT YEAR
1114             );
1115             my %reserved= ( map { $_ => 1 } @reserved );
1116              
1117             sub double_quote_perl($)
1118             {
1119 0     0 0 0 my ($s)= @_;
1120 0         0 $s =~ s/([\\\"\$\@])/\\$1/g;
1121 0         0 $s =~ s/\t/\\t/g;
1122 0         0 $s =~ s/\n/\\n/g;
1123 0         0 $s =~ s/\r/\\r/g;
1124 0         0 $s =~ s/([\x00-\x1f\x7f])/sprintf("\\x%02x", ord($1))/gsex;
  0         0  
1125 0         0 return "\"$s\"";
1126             }
1127              
1128             sub single_quote_perl($)
1129             {
1130 2884     2884 0 3491 my ($s)= @_;
1131 2884         3452 $s =~ s/([\\\'])/\\$1/g;
1132 2884         8824 return "'$s'";
1133             }
1134              
1135             sub quote_perl($)
1136             {
1137 3256     3256 0 3880 my ($s)= @_;
1138 3256 100       4536 return 'undef' unless defined $s;
1139 2884 50       5822 return ($s =~ /[\x00-\x1f\x7f\']/) ? double_quote_perl($s) : single_quote_perl($s);
1140             }
1141              
1142             sub skip_ws($)
1143             {
1144 3558     3558 0 4195 my ($lx)= @_;
1145 3558         3635 my $s= $lx->{text_p};
1146              
1147 3558         3484 for(;;) {
1148 6288 100       10996 if ($$s =~ /\G\n/gc) { # count lines
1149 433         533 $lx->{line}++;
1150 433         534 next;
1151             }
1152 5855 100       12629 next if $$s =~ /\G[^\n\S]+/gc; # other space but newline
1153 3577 100       5002 next if $$s =~ /\G\#[^\n]*/gc; # comments
1154 3558         4485 last;
1155             }
1156             }
1157              
1158             sub token_new($$;$%)
1159             {
1160 3223     3223 0 6771 my ($lx, $kind, $value, %opt)= @_;
1161 3223 50       4942 my_confess unless $kind;
1162             my $t= {
1163             lx => $lx,
1164             line => $lx->{line_before}, # start of token: rel. line num. in $lx->{text_p}
1165             line_after => $lx->{line},
1166             pos => $lx->{pos_before}, # start of token: string position in $lx->{text_p}
1167 3223         16169 pos_after => pos(${ $lx->{text_p} }), # end of token: string position in $lx->{text_p}
1168             kind => $kind,
1169             value => $value,
1170             str => $opt{str},
1171             type => $opt{type}, # interproc: 'variable', 'block', 'num', etc.
1172             perltype => $opt{perltype}, # interproc: 'array', 'scalar', 'hash', 'list'
1173             prec => $opt{prec},
1174             error => $opt{error},
1175 3223         4354 };
1176 3223         8692 lock_keys %$t;
1177 3223         28445 return $t;
1178             }
1179              
1180             sub token_describe($)
1181             {
1182 1     1 0 2 my ($t)= @_;
1183              
1184 1         3 my %opt= ();
1185 1         2 for my $key(qw(value str prec)) {
1186 3 100       8 if (defined $t->{$key}) {
1187 1         2 $opt{$key}= $t->{$key};
1188             }
1189             }
1190 1         2 for my $key(qw(perltype type)) {
1191 2 100       4 if ($t->{$key}) {
1192 1         2 $opt{$key}= $t->{$key};
1193             }
1194             }
1195              
1196 1 50       4 if (scalar(keys %opt)) {
1197             return "$t->{kind} (".
1198             join(", ",
1199             map {
1200 1         7 my $k= $_;
  2         2  
1201 2         4 "$k=".quote_perl($opt{$k})
1202             }
1203             sort keys %opt
1204             ).
1205             ")";
1206             }
1207             else {
1208 0         0 return quote_perl($t->{kind});
1209             }
1210             }
1211              
1212             sub error_new($$$)
1213             {
1214 1     1 0 4 my ($lx, $value, $expl)= @_;
1215 1         4 return token_new ($lx, 'error', $value, str => $expl, error => 1);
1216             }
1217              
1218             sub syn_new($$$)
1219             {
1220 2167     2167 0 4516 my ($lx, $type, $name)= @_;
1221 2167         3600 return token_new ($lx, $name, undef, perltype => '', type => $type);
1222             # perltype and type are for * and ?, which can occur as
1223             # syntactic values in expressions.
1224             }
1225              
1226             sub interpol_new($$$$$)
1227             {
1228 517     517 0 894 my ($lx, $interpol, $value, $type, $perltype)= @_;
1229 517         1093 return token_new ($lx, "interpol$interpol", $value,
1230             type => $type,
1231             perltype => $perltype
1232             );
1233             }
1234              
1235             sub token_scan_codeblock($$)
1236             {
1237 62     62 0 97 my ($lx, $interpol)= @_;
1238 62         90 my $s= $lx->{text_p};
1239              
1240             # Text::Balanced actually honours and updates pos($$s), so we can
1241             # interface directly:
1242 62         199 my ($ex)= Text::Balanced::extract_codeblock($$s, '{}()[]');
1243             return error_new($lx, 'codeblock', $@->{error})
1244 62 50       27841 if $@;
1245              
1246 62         145 $lx->{line}+= ($ex =~ tr/\n//);
1247 62         195 return interpol_new ($lx, $interpol, "do$ex", 'block', 'list');
1248             # $ex contains {}, so do$ex is sound.
1249             }
1250              
1251             sub token_scan_variable($$$)
1252             {
1253 170     170 0 333 my ($lx, $interpol, $perltype)= @_;
1254 170         212 my $s= $lx->{text_p};
1255              
1256 170         475 my ($ex)= Text::Balanced::extract_variable($$s);
1257             return error_new($lx, 'variable', $@->{error})
1258 170 50       33110 if $@;
1259              
1260 170         361 $lx->{line}+= ($ex =~ tr/\n//);
1261 170         318 return interpol_new ($lx, $interpol, $ex, 'variable', $perltype);
1262             }
1263              
1264             sub token_scan_delimited($$$)
1265             {
1266 50     50 0 125 my ($lx, $interpol, $delim)= @_;
1267 50         75 my $s= $lx->{text_p};
1268              
1269 50         163 my ($ex)= Text::Balanced::extract_delimited($$s, $delim);
1270             return error_new($lx, 'delimited', $@->{error})
1271 50 50       4477 if $@;
1272              
1273 50         101 $lx->{line}+= ($ex =~ tr/\n//);
1274 50         114 return interpol_new ($lx, $interpol, $ex, 'string', 'scalar');
1275             }
1276              
1277             sub token_num_new($$$)
1278             {
1279 235     235 0 640 my ($lx, $interpol, $value)= @_;
1280 235   50     807 return interpol_new ($lx, $interpol || 'Expr', $value, 'num', 'scalar');
1281             }
1282              
1283             sub ident_new($$)
1284             {
1285 538     538 0 1661 my ($lx, $value)= @_;
1286 538         906 return token_new ($lx, 'ident', $value, perltype => 'scalar');
1287             }
1288              
1289             sub keyword_new($$) # either syn or function
1290             {
1291 754     754 0 961 my ($lx, $name)= @_;
1292 754 100       1318 if ($reserved{$name}) {
1293 681         1030 return syn_new($lx, 'reserved', $name);
1294             }
1295             else {
1296 73         129 return syn_new($lx, 'keyword', $name);
1297             }
1298             }
1299              
1300             sub replace_synonym($)
1301             {
1302 1268     1268 0 1576 my ($name)= @_;
1303 1268         2436 while (my $syn= $synonym{$name}) {
1304 7         16 $name= $syn;
1305             }
1306 1268         1785 return $name;
1307             }
1308              
1309             sub multi_token_new($$)
1310             {
1311 926     926 0 2367 my ($lx, $name)= @_;
1312 926         1218 my $s= $lx->{text_p};
1313              
1314 926         1256 $name= replace_synonym($name);
1315 926 100       1703 if (my $tree= $multi_token{$name}) {
1316 172         270 SUB_TOKEN: for (;;) {
1317 327         519 skip_ws($lx);
1318              
1319 327         355 my $p= pos($$s);
1320 327 100       785 last SUB_TOKEN unless $$s =~ /\G ([A-Z][A-Z_0-9]*)\b /gcsx;
1321 187         267 my $sub_name= $1;
1322              
1323 187         249 $sub_name= replace_synonym($sub_name);
1324 187         311 $tree= $tree->{$sub_name};
1325 187 100       289 unless ($tree) {
1326 32         59 pos($$s)= $p; # unscan rejected keyword
1327 32         61 last SUB_TOKEN;
1328             }
1329              
1330 155         260 $name.= " $sub_name";
1331 155         204 $name= replace_synonym($name);
1332             }
1333 172         327 return syn_new ($lx, 'keyword', $name); # never a function, always a keyword
1334             }
1335             else {
1336 754         1208 return keyword_new ($lx, $name);
1337             }
1338             }
1339              
1340             sub good_interpol_type($);
1341              
1342             sub token_scan_rec($$);
1343             sub token_scan_rec($$)
1344             {
1345 3231     3231 0 3971 my ($lx, $interpol)= @_;
1346 3231         3540 my $s= $lx->{text_p};
1347              
1348 3231         5274 skip_ws($lx);
1349              
1350 3231         4212 $lx->{pos_before}= pos($$s);
1351 3231         3702 $lx->{line_before}= $lx->{line}; # strings may contain \n, so this may change.
1352              
1353             # idents: distinguished by case:
1354 3231 100       6624 return multi_token_new ($lx, $1) if $$s =~ /\G ([A-Z][A-Z_0-9]*)\b /gcsx;
1355 2305 100       4292 return ident_new ($lx, $1) if $$s =~ /\G ([a-z][a-z_0-9]*)\b /gcsx;
1356 1767 50       3580 return ident_new ($lx, $1) if $$s =~ /\G \`([^\n\\\`]+)\` /gcsx;
1357              
1358 1767 100       2762 if ($$s =~ /\G ([A-Z][a-z][A-Za-z0-9]*)\b /gcsx) {
1359             # type specifiers change the token context itself, so we recurse here.
1360 8         16 my $interpol_new= $1;
1361 8 50       24 return error_new ($lx, $interpol_new, 'unknown type cast')
1362             unless good_interpol_type($interpol_new);
1363              
1364 8 50       19 return error_new ($lx, $interpol_new, 'duplicate type case')
1365             if $interpol;
1366              
1367 8         22 my $tok= token_scan_rec ($lx, $interpol_new);
1368 8 50       22 return $tok if $tok->{error};
1369              
1370             return error_new ($lx, $tok->{kind},
1371             "Expected Perl interpolation after type cast to '$interpol_new'")
1372 8 50       33 unless $tok->{kind} =~ /^interpol/;
1373              
1374 8         15 return $tok;
1375             }
1376              
1377 1759 50       2804 return error_new ($lx, $1, 'illegal identifier: neither keyword nor name')
1378             if $$s =~ /\G ([A-Za-z_][A-Za-z_0-9]*) /gcsx;
1379              
1380             # Numbers, strings, and embedded Perl code are handled alike: they will be
1381             # extracted as is and evaluated as is. This way, much of the embedded SQL
1382             # syntax is just like in Perl, and you don't face surprises. The uniform
1383             # kind of this token is 'interpol'. The precise type is stored in the
1384             # str attribute, in case anyone wants to know later.
1385              
1386             # numbers:
1387             ## ints:
1388 1759 100       2986 return token_num_new ($lx, $interpol, hex($1)) if $$s =~ /\G 0x([0-9a-f_]+)\b /gcsix;
1389 1758 100       2417 return token_num_new ($lx, $interpol, oct($1)) if $$s =~ /\G (0b[0-1_]+)\b /gcsx;
1390 1757 100       2958 return token_num_new ($lx, $interpol, $1) if $$s =~ /\G ([1-9][0-9_]*)\b /gcsx;
1391 1541 100       2462 return token_num_new ($lx, $interpol, oct($1)) if $$s =~ /\G ([0][0-7_]*)\b /gcsx;
1392             # Note: oct() interprets 0b as binary, and there's not bin().
1393              
1394 1524 50       2332 return token_num_new ($lx, $interpol, $1) if $$s =~ /\G ([1-9][0-9_]*)(?=[KMG]\b) /gcsx;
1395             # special case for , which we split in two.
1396              
1397             ## floats:
1398 1524 50       2807 return token_num_new ($lx, $interpol, $1)
1399             if $$s =~ /\G( (?= [1-9] # not empty, but numeric
1400             | [.][0-9]
1401             )
1402             (?: [1-9] [0-9_]* )?
1403             (?: [.] [0-9_]+ )?
1404             (?: e[-+] [0-9_]+ )?\b )/gcsix;
1405              
1406 1524 50       2422 return error_new ($lx, $1, 'not a number')
1407             if $$s =~ /\G ([0-9][a-z_0-9]*) /gcsix;
1408              
1409             # embedded Perl:
1410 1524 100       2560 return token_scan_variable ($lx, $interpol, 'scalar') if $$s =~ /\G (?= \$\S ) /gcsx;
1411 1453 100       2324 return token_scan_variable ($lx, $interpol, 'array') if $$s =~ /\G (?= \@\S ) /gcsx;
1412 1382 100       2187 return token_scan_variable ($lx, $interpol, 'hash') if $$s =~ /\G (?= \%[^\s\d] ) /gcsx;
1413 1354 100       2136 return token_scan_codeblock ($lx, $interpol) if $$s =~ /\G (?= \{ ) /gcsx;
1414 1292 100       2046 return token_scan_delimited ($lx, $interpol, $1) if $$s =~ /\G (?= [\'\"] ) /gcsx;
1415              
1416             # symbols:
1417 1242 100       3353 return syn_new ($lx, 'symbol', $1)
1418             if $$s =~ /\G(
1419             == | != | <= | >=
1420             | \&\& | \|\| | \! | \^\^
1421             | \*\* | \^
1422             | [-+*\/;:,.()\[\]{}<=>?\%\&\|]
1423             )/gcsx;
1424              
1425             # specials:
1426 41 100       91 return error_new ($lx, $1, 'Unexpected character') if $$s =~ /\G(.)/gcs;
1427 40         78 return syn_new ($lx, 'special', '');
1428             }
1429              
1430             sub token_scan($)
1431             {
1432 3223     3223 0 3660 my ($lx)= @_;
1433 3223         4179 my $t= token_scan_rec($lx, '');
1434             #print STDERR "DEBUG: scanned: ".token_describe($t)."\n";
1435 3223         6530 return $t;
1436             }
1437              
1438             sub lexer_shift($)
1439             # returns the old token kind
1440             {
1441 3223     3223 0 4098 my ($lx)= @_;
1442 3223         4034 my $r= $lx->{token}{kind};
1443 3223         4649 $lx->{token}= token_scan($lx);
1444 3223         3931 return $r;
1445             }
1446              
1447             sub lexer_new($$$)
1448             {
1449 280     280 0 585 my ($s, $file, $line_start)= \(@_);
1450 280         1452 my $lx= {
1451             text_p => $s,
1452             token => undef,
1453             file => $$file,
1454             line_start => $$line_start, # relative start line of text in file
1455             line => 1, # current line (after current token)
1456             prev_line => 1, # end line of previous token (before white space)
1457             line_before => 1, # start line of current token (after white space)
1458             pos_before => 0, # pos($$s) at start of current token
1459             error => undef,
1460             };
1461 280         897 lock_keys %$lx;
1462 280         2851 lexer_shift($lx);
1463 280         347 return $lx;
1464             }
1465              
1466             sub flatten($);
1467             sub flatten($)
1468             {
1469 12156     12156 0 16155 my ($x)= @_;
1470 12156 100       20833 return $x
1471             unless ref($x);
1472              
1473 892 50       1959 return map { flatten($_) } @$x
  3716         4193  
1474             if ref($x) eq 'ARRAY';
1475              
1476 0 0       0 return flatten([ sort keys %$x ])
1477             if ref($x) eq 'HASH';
1478              
1479 0         0 my_confess "No idea how to flatten $x";
1480             }
1481              
1482             sub flatten_hash($);
1483             sub flatten_hash($)
1484             {
1485 8407     8407 0 9361 my ($x)= @_;
1486 8407         9637 return map {$_ => 1} flatten $x;
  11167         20887  
1487             }
1488              
1489             sub looking_at_raw($$)
1490             {
1491 9536     9536 0 10763 my ($lx, $kind)= @_;
1492 9536 100       12770 return unless $kind;
1493              
1494 8407         10318 my %kind= flatten_hash $kind;
1495             return $lx->{token}{kind}
1496 8407 100       17228 if $kind{$lx->{token}{kind}};
1497              
1498 6965         12254 return; # Don't return undef, but an empty list, so in array context, we get 0 results
1499             # This principle is used everywhere in this file. In scalar context, we still
1500             # get undef from am empty list.
1501             }
1502              
1503             sub looking_at($$;$)
1504             {
1505 9536     9536 0 12151 my ($lx, $kind, $do_shift)= @_;
1506 9536 100       11551 if (my $x= looking_at_raw($lx,$kind)) {
1507 1442 100       3026 lexer_shift($lx) if $do_shift;
1508 1442         3362 return $x;
1509             }
1510 8094         13339 return;
1511             }
1512              
1513             sub english_or(@)
1514             {
1515 1     1 0 1 my $map= undef;
1516 1 50       4 $map= shift
1517             if ref($_[0]) eq 'CODE';
1518              
1519 1         2 my @l= sort map flatten($_), @_;
1520              
1521 1 50       3 @l= map { $map->($_) } @l
  1         3  
1522             if $map;
1523              
1524 1 50       4 return 'nothing' if scalar(@l) == 0;
1525 1 50       7 return $l[0] if scalar(@l) == 1;
1526 0 0       0 return "$l[0] or $l[1]" if scalar(@l) == 2;
1527              
1528 0         0 return join(", ", @l[0..$#l-1], "or $l[-1]");
1529             }
1530              
1531             sub expect($$;$)
1532             {
1533 790     790 0 1125 my ($lx, $kind, $do_shift)= @_;
1534 790 100       1080 if (my $x= looking_at($lx, $kind, $do_shift)) {
    50          
1535 789         1824 return $x;
1536             }
1537             elsif (my $err= lx_token_error($lx)) {
1538 0         0 $lx->{error}= $err;
1539             }
1540             else {
1541             $lx->{error}= 'Expected '.(english_or \"e_perl, $kind).
1542 1         4 ', but found '.token_describe($lx->{token});
1543             }
1544 1         2 return;
1545             }
1546              
1547             # Parse Functions
1548             # ---------------
1549             # These functions return either:
1550             #
1551             # undef - in case of a syntax error
1552             # $lx->{error} will contain more information
1553             #
1554             # [...] - In case of a sequence of things (parse_list)
1555             #
1556             # {...} - In case of a successfully parsed item.
1557             # The hash contains a 'type' plus additional
1558             # slots depending on what was parsed.
1559             #
1560             # These things can be created with create().
1561             #
1562             # Note that tokens may be used here, too.
1563             #
1564             # Note: you cannot *try* to parse something and in case of a
1565             # failure, do something else, because pos() and the $lx->{token}
1566             # will have changed. E.g. when calling parse_list, you *must*
1567             # pass all things that might end a list instead of reading up
1568             # to an error. That's what the look-ahead token is for!
1569              
1570             sub create($$@)
1571             {
1572 2622     2622 0 4449 my ($lx, $kind, @more)= @_;
1573             my $r= {
1574             (ref($kind) ?
1575             (
1576             kind => $kind->[0],
1577             type => $kind->[1]
1578             )
1579             : (
1580             kind => $kind,
1581             type => ''
1582             )
1583             ),
1584             line => $lx->{token}{line},
1585 2622 100       4989 map { $_ => undef } @more,
  11669         18495  
1586             };
1587 2622         6428 lock_keys %$r;
1588 2622         19922 return $r;
1589             }
1590              
1591             # special creates that occur frequently:
1592             sub create_Expr($)
1593             {
1594 1034     1034 0 1189 my ($lx)= @_;
1595 1034         1446 return create ($lx, 'Expr', qw(maybe_check token functor arg switchval otherwise));
1596             }
1597              
1598             sub parse_list($$$$;$)
1599             # We allow multiple separators and also lists beginning with
1600             # separators, but we do not allow them to end with the same separator.
1601             # If a separator is encountered, we assume that the list continues.
1602              
1603             # There is one exception: if you specify an $end, then before the
1604             # $end, there may be too many separators. This is handy for
1605             # statements that often end in ; just before the closing }.
1606              
1607             # is implicit treated as an $end in all invocations.
1608              
1609             # A token matching $end is not shifted.
1610             #
1611             # If $end is given, lists may be empty. Otherwise, they may
1612             # not be.
1613              
1614             # The result is either a list reference or undef in case
1615             # of an error. $lx->{error} will then be set accordingly.
1616             {
1617 1233     1233 0 2035 my ($result, $lx, $parse_elem, $list_sep, $end)= @_;
1618              
1619 1233         1365 my %pos= ();
1620 1233         1279 ELEMENT: {do {
  1233         1245  
1621 1502         1497 do {
1622             # check that we have no infinite loop:
1623 1511         1419 my $p= pos(${ $lx->{text_p} });
  1511         2197  
1624 1511 50       3685 die "BUG: pos() not shifted in list" if $pos{$p}++;
1625              
1626             # check for end:
1627 1511 100       2172 last ELEMENT if looking_at($lx, $end);
1628 1502 100       1980 last ELEMENT if looking_at($lx, '');
1629              
1630             # allow too many separators:
1631             } while (looking_at($lx, $list_sep, SHIFT));
1632              
1633             # parse one element:
1634             return unless
1635 1492 100       2432 my @result1= $parse_elem->($lx);
1636              
1637             # append that element to result:
1638 1490         5799 push @$result, @result1;
1639              
1640             # check whether the list continues:
1641             } while (looking_at($lx, $list_sep, SHIFT))};
1642              
1643 1231         3293 return $result;
1644             }
1645              
1646             sub parse_try_list($$$)
1647             # List without delimiter, but sequenced prefix-marked elements.
1648             # For example: a list of JOIN clauses.
1649             #
1650             # The parsers for such elements must handle try-parsing, i.e.,
1651             # returning undef while not setting $lx->{error} to indicate
1652             # that they are not looking at a prefix-marked element.
1653             {
1654 159     159 0 282 my ($result, $lx, $parse_elem)= @_;
1655              
1656 159         319 while (my @result1= $parse_elem->($lx)) {
1657 55         145 push @$result, @result1;
1658             }
1659              
1660 159 50       274 return if $lx->{error};
1661 159         485 return $result;
1662             }
1663              
1664             sub find_ref(\%$)
1665             # Finds a ref-valued value in a hash table, allowing redirections.
1666             # If nothing is found, '' is returned (which would never be returned
1667             # otherwise, because it is neither a ref(), nor undef).
1668             {
1669 9673     9673 0 11779 my ($hash, $key)= @_;
1670 9673         10113 my $result= undef;
1671 9673         24527 local $SIG{__DIE__}= \&my_confess;
1672 9673 100       17718 if (exists $hash->{$key}) {
    100          
1673 7601         8931 $result= $hash->{$key}
1674             }
1675             elsif (exists $hash->{-default}) {
1676             $result= $hash->{-default}
1677 1099         1298 }
1678             else {
1679 973         3772 return '';
1680             }
1681              
1682 8700   100     15955 until (ref($result) || !defined $result) { # No infinite loop protection!
1683             die "'$result' key not in hash table"
1684 405 50       662 unless exists $hash->{$result};
1685 405         785 $result= $hash->{$result};
1686             }
1687              
1688 8700         22711 return $result;
1689             }
1690              
1691             sub switch($%) # waiting for Perl 5.10: given/when/default
1692             {
1693 8369     8369 0 48243 my ($value, %case)= @_;
1694 8369 50       13114 if (my $code= find_ref(%case, $value)) {
1695 8369         11032 return $code->();
1696             }
1697              
1698 0         0 my_confess "Expected ".(english_or \"e_perl, \%case).", but found '$value'";
1699             }
1700              
1701             sub lx_token_error($)
1702             {
1703 2     2 0 5 my ($lx)= @_;
1704 2 100       6 if ($lx->{token}{error}) {
1705             return 'Found '.
1706             quote_perl($lx->{token}{value}).': '.
1707 1         3 $lx->{token}{str};
1708             }
1709 1         3 return;
1710             }
1711              
1712             sub parse_choice($%)
1713             {
1714 2304     2304 0 14845 my ($lx, %opt)= @_;
1715             return switch ($lx->{token}{kind},
1716             -default => sub {
1717 1 50   0   2 if (my $err= lx_token_error($lx)) { # already have an error message.
    0          
1718 1         8 $lx->{error}= 'In '.(caller(3))[3].": $err";
1719             }
1720             elsif (scalar(keys %opt) > 10) {
1721             $lx->{error}= 'In '.(caller(3))[3].': '.
1722 0         0 ' Unexpected '.token_describe($lx->{token});
1723             }
1724             else {
1725             $lx->{error}= 'In '.(caller(3))[3].': Expected '.
1726             (english_or \"e_perl, \%opt).
1727             ', but found '.
1728 0         0 token_describe($lx->{token});
1729             }
1730 1         14 return;
1731             },
1732 2304         12740 %opt, # may override -default
1733             );
1734             }
1735              
1736             sub parse_plain_ident($)
1737             {
1738 553     553 0 695 my ($lx)= @_;
1739             return parse_choice($lx,
1740             'interpol' => sub {
1741 49     49   81 my $r= $lx->{token};
1742              
1743             # If it is unambiguous, even "..." interpolation is intepreted as
1744             # a column name.
1745             #if (FORCE_STRING && $r->{type} eq 'string') {
1746             # $lx->{error}=
1747             # 'Expected identifier, but found string: '.token_describe($r).
1748             # "\n\t".
1749             # "If you want to construct an identifier name, use {$r->{value}}.";
1750             # return;
1751             #}
1752             #els
1753 49 50       110 if ($r->{type} eq 'num') {
1754             $lx->{error}=
1755 0         0 'Expected identifier, but found number: '.token_describe($r).
1756             "\n\t".
1757             "If you want to construct an identifier name, use {$r->{value}}.";
1758 0         0 return;
1759             }
1760              
1761 49         98 lexer_shift($lx);
1762 49         475 return $r;
1763             },
1764              
1765             'interpolColumn' => 'ident',
1766             'interpolTable' => 'ident',
1767             'interpolCharSet' => 'ident',
1768             'interpolEngine' => 'ident',
1769             'interpolCollate' => 'ident',
1770             'interpolConstraint' => 'ident',
1771             'interpolIndex' => 'ident',
1772             'interpolTranscoding' => 'ident',
1773             'interpolTransliteration' => 'ident',
1774             '*' => 'ident',
1775             'ident' => sub {
1776 504     504   568 my $r= $lx->{token};
1777 504         922 lexer_shift($lx);
1778 504         4647 return $r;
1779             },
1780 553         2451 );
1781             }
1782              
1783             sub parse_ident_chain($$)
1784             {
1785 520     520 0 710 my ($lx, $arr)= @_;
1786 520         938 return parse_list($arr, $lx, \&parse_plain_ident, '.');
1787             }
1788              
1789             sub check_column(@)
1790             {
1791 330     330 0 573 while (scalar(@_) < 4) { unshift @_, undef; }
  960         1558  
1792 330         595 my ($cat,$sch,$tab,$col)= @_;
1793              
1794             #return unless !defined $cat || my $cat= $cat->{
1795             #check_ident ('Column', $cat, $sch, $tab, $col);
1796             }
1797              
1798             sub parse_column($;$)
1799             # The interpretation of the identifier chain is left to the column{1,2,3,4}
1800             # family of functions. It is as follows:
1801             #
1802             # Depending on the number of elements in the chain, the following types
1803             # are allowed:
1804             #
1805             # 1 Element:
1806             # - Column: a fully qualified column object, maybe including
1807             # a table specification
1808             # - ColumnName: a single identifier object with a column name
1809             # - string: a single identifier, too, will be quoted accordingly.
1810             #
1811             # 2 Elements:
1812             # - First element: Table or string
1813             # Last element: ColumnName or string
1814             #
1815             # more Elements:
1816             # - All but last element: string only
1817             # - Last element: ColumnName or string
1818             {
1819 330     330 0 445 my ($lx, $arr)= @_;
1820 330         485 my $r= create ($lx, 'Column', qw(ident_chain));
1821 330   100     1472 $arr||= [];
1822             return
1823 330 50       546 unless parse_ident_chain($lx, $arr);
1824              
1825 330 50       591 my_confess if scalar(@$arr) < 1;
1826 330 50       566 if (scalar(@$arr) > 4) {
1827 0         0 $lx->{error}= 'Too many parts of column identifier chain. '.
1828             'Maximum is 4, found '.scalar(@$arr);
1829 0         0 return;
1830             }
1831              
1832 330         628 check_column(@$arr);
1833              
1834 330         411 $r->{ident_chain}= $arr;
1835              
1836 330         691 lock_keys %$r;
1837 330         2811 return $r;
1838             }
1839              
1840             sub parse_schema_qualified($$)
1841             {
1842 190     190 0 271 my ($lx, $kind)= @_;
1843              
1844 190         274 my $r= create ($lx, $kind, qw(ident_chain));
1845 190         245 my $arr= [];
1846             return
1847 190 50       256 unless parse_ident_chain($lx, $arr);
1848              
1849 190 50       347 my_confess if scalar(@$arr) < 1;
1850 190 50       312 if (scalar(@$arr) > 3) {
1851 0         0 $lx->{error}= 'Too many identifiers in $kind. '.
1852             'Maximum is 3, found '.scalar(@$arr);
1853 0         0 return;
1854             }
1855              
1856 190         223 $r->{ident_chain}= $arr;
1857              
1858 190         436 lock_keys %$r;
1859 190         1725 return $r;
1860             }
1861              
1862             sub parse_table($)
1863             # The interpretation of the identifier chain is left to the table{1,2,3}
1864             # family of functions. It is as follows:
1865             #
1866             # Depending on the number of elements in the chain, the following types
1867             # are allowed:
1868             #
1869             # 1 Element:
1870             # - Table: a fully qualified table object
1871             # - string: a single identifier, too, will be quoted accordingly.
1872             #
1873             # more Elements:
1874             # - all elements: string
1875             {
1876 172     172 0 230 my ($lx)= @_;
1877 172         291 return parse_schema_qualified($lx, 'Table');
1878             }
1879              
1880             sub parse_charset($)
1881             {
1882 7     7 0 12 my ($lx)= @_;
1883 7         13 return parse_schema_qualified($lx, 'CharSet');
1884             }
1885              
1886             sub parse_constraint($)
1887             {
1888 7     7 0 12 my ($lx)= @_;
1889 7         11 return parse_schema_qualified($lx, 'Constraint');
1890             }
1891              
1892             sub parse_index($)
1893             {
1894 1     1 0 3 my ($lx)= @_;
1895 1         2 return parse_schema_qualified($lx, 'Index');
1896             }
1897              
1898             sub parse_collate($)
1899             {
1900 1     1 0 3 my ($lx)= @_;
1901 1         2 return parse_schema_qualified($lx, 'Collate');
1902             }
1903              
1904             sub parse_transliteration($)
1905             {
1906 0     0 0 0 my ($lx)= @_;
1907 0         0 return parse_schema_qualified($lx, 'Transliteration');
1908             }
1909              
1910             sub parse_transcoding($)
1911             {
1912 0     0 0 0 my ($lx)= @_;
1913 0         0 return parse_schema_qualified($lx, 'Transcoding');
1914             }
1915              
1916             sub parse_engine($)
1917             {
1918 2     2 0 6 my ($lx)= @_;
1919 2         4 return parse_schema_qualified($lx, 'Engine');
1920             }
1921              
1922              
1923             sub parse_column_name($)
1924             {
1925 49     49 0 81 my ($lx)= @_;
1926 49         73 my $r= create ($lx, 'ColumnName', qw(token));
1927              
1928             parse_choice($lx,
1929             'ident' => sub {
1930 44     44   61 $r->{type}= 'ident';
1931 44         63 $r->{token}= $lx->{token};
1932 44         62 lexer_shift($lx);
1933             },
1934              
1935             'interpolColumn' => 'interpol',
1936             'interpol' => sub {
1937 5     5   17 $r->{type}= 'interpol';
1938 5         9 $r->{token}= $lx->{token};
1939 5         11 lexer_shift($lx);
1940             },
1941 49         231 );
1942 49 50       369 return if $lx->{error};
1943              
1944 49         121 lock_keys %$r;
1945 49         445 return $r;
1946             }
1947              
1948             sub parse_column_index($)
1949             {
1950 2     2 0 5 my ($lx)= @_;
1951 2         6 my $r= create ($lx, 'ColumnIndex', qw(name length desc));
1952              
1953             return unless
1954 2 50       4 $r->{name}= parse_column_name($lx);
1955              
1956 2 100       6 if (looking_at($lx, '(', SHIFT)) {
1957             return unless
1958 1 50 33     3 $r->{length}= parse_limit_expr($lx)
1959             and expect ($lx, ')', SHIFT);
1960             }
1961              
1962 2 100       5 if (looking_at($lx, 'DESC', SHIFT)) {
    50          
1963 1         2 $r->{desc}= 1;
1964             }
1965             elsif (looking_at($lx, 'ASC', SHIFT)) {
1966             #ignore
1967             }
1968              
1969 2         6 lock_hash %$r;
1970 2         36 return $r;
1971             }
1972              
1973             sub parse_table_name($)
1974             {
1975 5     5 0 11 my ($lx)= @_;
1976 5         11 my $r= create ($lx, 'TableName', qw(token));
1977              
1978             parse_choice($lx,
1979             'ident' => sub {
1980 5     5   12 $r->{type}= 'ident';
1981 5         8 $r->{token}= $lx->{token};
1982 5         9 lexer_shift($lx);
1983             },
1984              
1985             'interpolTable' => 'interpol',
1986             'interpol' => sub {
1987 0     0   0 $r->{type}= 'interpol';
1988 0         0 $r->{token}= $lx->{token};
1989 0         0 lexer_shift($lx);
1990             },
1991 5         47 );
1992 5 50       44 return if $lx->{error};
1993              
1994 5         16 lock_keys %$r;
1995 5         42 return $r;
1996             }
1997              
1998             sub parse_table_as($)
1999             {
2000 128     128 0 178 my ($lx)= @_;
2001 128         190 my $r= create ($lx, 'TableAs', qw(table as));
2002              
2003 128 100       269 if (looking_at($lx, '(', SHIFT)) {
2004 2         4 my $stmt;
2005             return unless
2006 2 50 33     7 $stmt= parse_select_stmt($lx)
2007             and expect ($lx, ')', SHIFT);
2008              
2009 2         10 my $s= create_Expr ($lx);
2010 2         4 $s->{type}= 'subquery';
2011 2         3 $s->{arg}= $stmt;
2012              
2013 2         3 $r->{table} = $s;
2014             }
2015             else {
2016             return unless
2017 126 50       227 $r->{table}= parse_table($lx);
2018             }
2019              
2020 128 100       223 if (looking_at($lx, 'AS', SHIFT)) {
2021             return unless
2022 5 50       17 $r->{as}= parse_table_name($lx);
2023             }
2024              
2025 128         313 lock_hash %$r;
2026 128         2423 return $r;
2027             }
2028              
2029             sub parse_value_or_column_into($$$)
2030             {
2031 173     173 0 260 my ($lx, $r, $type)= @_;
2032              
2033 173         271 my $token= $lx->{token};
2034 173         299 lexer_shift($lx);
2035              
2036 173 100       257 if (looking_at($lx, '.')) {
2037 9         16 $r->{type}= 'column';
2038 9         23 $r->{arg}= parse_column($lx, [ $token ]);
2039             }
2040             else {
2041 164         240 $r->{type}= $type;
2042 164         349 $r->{token}= $token;
2043             }
2044             }
2045              
2046             sub parse_expr($;$$);
2047             sub parse_select_stmt($);
2048             sub parse_funcsep($$$);
2049             sub parse_expr_post($$$$);
2050              
2051 5     5   50 use constant ACTION_AMBIGUOUS => undef;
  5         12  
  5         247  
2052 5     5   27 use constant ACTION_REDUCE => -1;
  5         5  
  5         257  
2053 5     5   26 use constant ACTION_SHIFT => +1;
  5         7  
  5         97406  
2054              
2055             sub plural($;$$)
2056             {
2057 0     0 0 0 my ($cnt, $sg, $pl)= @_;
2058 0 0       0 return $cnt == 1 ? (defined $sg ? $sg : '') : (defined $pl ? $pl : 's');
    0          
    0          
2059             }
2060              
2061             sub parse_limit_expr($)
2062             {
2063 24     24 0 41 my ($lx)= @_;
2064             return unless
2065 24 50       62 my $limit= parse_limit_num($lx);
2066 24         76 my $r= create_Expr ($lx);
2067 24         39 $r->{type}= 'limit';
2068 24         32 $r->{arg}= $limit;
2069 24         63 lock_hash %$r;
2070 24         522 return $r;
2071             }
2072              
2073             sub parse_char_unit($)
2074             {
2075 1     1 0 2 my ($lx)= @_;
2076 1         2 my $r= create($lx, 'CharUnit', qw(name));
2077 1         3 $r->{name}= expect($lx, ['CHARACTERS', 'CODE_UNITS', 'OCTETS'], SHIFT);
2078 1         3 lock_hash %$r;
2079 1         17 return $r;
2080             }
2081              
2082             sub parse_list_delim($$)
2083             {
2084 59     59 0 100 my ($lx, $func)= @_;
2085             return unless
2086 59 50 33     106 expect($lx, '(', SHIFT)
      33        
2087             and my $list= parse_list([], $lx, $func, ',', ')')
2088             and expect($lx, ')', SHIFT);
2089 59         226 return $list;
2090             }
2091              
2092             sub parse_type_post_inner($)
2093             {
2094 106     106 0 148 my ($lx)= @_;
2095              
2096 106         116 my $functor= undef;
2097 106         131 my @arg= ();
2098             parse_choice ($lx,
2099             -default => sub {
2100 73 100   73   137 if (my $spec= find_ref(%type_spec, $lx->{token}{kind})) {
2101 27 100       55 if ($spec->{value_list}) {
2102 1         3 $functor= 'basewlist',
2103             push @arg, lexer_shift($lx);
2104             return unless
2105 1 50       2 my $value_list= parse_list_delim($lx, \&parse_expr);
2106 1         3 push @arg, @$value_list;
2107             }
2108             else {
2109 26         37 $functor= 'base';
2110 26         49 push @arg, lexer_shift($lx);
2111             }
2112             }
2113             },
2114              
2115             'UNSIGNED' => 'SIGNED',
2116             'SIGNED' => sub {
2117 1     1   2 $functor= 'property';
2118 1         2 push @arg, 'sign', lexer_shift($lx);
2119             },
2120             'DROP SIGN' => sub {
2121 0     0   0 $functor= 'property';
2122 0         0 push @arg, 'sign', '';
2123 0         0 lexer_shift($lx);
2124             },
2125              
2126             'ZEROFILL' => sub {
2127 1     1   3 $functor= 'property';
2128 1         3 push @arg, 'zerofill', lexer_shift($lx);
2129             },
2130              
2131             'DROP ZEROFILL' => sub {
2132 0     0   0 $functor= 'property';
2133 0         0 push @arg, 'zerofill', '';
2134 0         0 lexer_shift($lx);
2135             },
2136              
2137             'ASCII' => sub {
2138 0     0   0 my $cs= create($lx, 'CharSet', qw(token));
2139 0         0 $cs->{token}= ident_new($lx, 'latin1');
2140 0         0 $functor= 'property';
2141 0         0 push @arg, 'charset', $cs;
2142 0         0 lexer_shift($lx);
2143             },
2144             'UNICODE' => sub {
2145 0     0   0 my $cs= create($lx, 'CharSet', qw(token));
2146 0         0 $cs->{token}= ident_new($lx, 'ucs2');
2147 0         0 $functor= 'property';
2148 0         0 push @arg, 'charset', $cs;
2149 0         0 lexer_shift($lx);
2150             },
2151             'CHARACTER SET' => sub {
2152 4     4   10 lexer_shift($lx);
2153             return unless
2154 4 50       12 my $arg= parse_charset($lx);
2155 4         7 $functor= 'property';
2156 4         10 push @arg, 'charset', $arg;
2157             },
2158             'DROP CHARACTER SET' => sub {
2159 3     3   6 $functor= 'property';
2160 3         5 push @arg, 'charset', '';
2161 3         4 lexer_shift($lx);
2162             },
2163              
2164             'COLLATE' => sub {
2165 1     1   3 lexer_shift($lx);
2166             return unless
2167 1 50       3 my $arg= parse_collate($lx);
2168 1         2 $functor= 'property';
2169 1         3 push @arg, 'collate', $arg;
2170             },
2171             'DROP COLLATE' => sub {
2172 0     0   0 $functor= 'property';
2173 0         0 push @arg, 'collate', '';
2174 0         0 lexer_shift($lx);
2175             },
2176              
2177             'WITH LOCAL TIME ZONE' => 'WITH TIME ZONE',
2178             'WITHOUT TIME ZONE' => 'WITH TIME ZONE',
2179             'WITH TIME ZONE' => sub {
2180 0     0   0 $functor= 'property';
2181 0         0 push @arg, 'timezone', lexer_shift($lx);
2182             },
2183              
2184             'DROP TIME ZONE' => sub {
2185 0     0   0 $functor= 'property';
2186 0         0 push @arg, 'timezone', '';
2187 0         0 lexer_shift($lx);
2188             },
2189              
2190             '(' => sub {
2191 23     23   52 lexer_shift($lx);
2192             return unless
2193 23 50       58 my $list= parse_list ([], $lx, \&parse_limit_expr, ',', ')');
2194              
2195             parse_choice($lx,
2196             'K' => 'G',
2197             'M' => 'G',
2198             'G' => sub {
2199 1 50       4 if (scalar(@$list) > 1) {
2200 0         0 $lx->{error}= "At most one value in () expected, but found ".scalar($list);
2201 0         0 return;
2202             }
2203              
2204 1         1 $functor= 'largelength';
2205 1         2 push @arg, $list->[0];
2206              
2207 1         2 push @arg, lexer_shift($lx);
2208              
2209 1 50       3 if (looking_at($lx, ')')) {
2210 0         0 push @arg, '';
2211             }
2212             else {
2213             return unless
2214 1 50       2 my $unit= parse_char_unit($lx);
2215              
2216 1         2 push @arg, $unit;
2217             }
2218             },
2219              
2220             'ident' => sub {
2221 0 0       0 if (scalar(@$list) > 1) {
2222 0         0 $lx->{error}= "At most one value in () expected, but found ".scalar($list);
2223 0         0 return;
2224             }
2225              
2226 0         0 $functor= 'largelength';
2227 0         0 push @arg, '';
2228              
2229             },
2230              
2231             -default => sub {
2232 22 50       59 if (scalar(@$list) > 2) {
2233 0         0 $lx->{error}= "At most two values in () expected, but found ".scalar($list);
2234 0         0 return;
2235             }
2236              
2237 22         35 $functor= 'length';
2238 22         50 push @arg, @$list;
2239             }
2240 23         161 );
2241 23 50       235 return if $lx->{error};
2242 23 50       44 return unless expect($lx, ')', SHIFT);
2243             },
2244 106         1514 );
2245              
2246 106         2143 return ($functor, \@arg);
2247             }
2248              
2249             sub parse_type_post($$);
2250             sub parse_type_post($$)
2251             {
2252 86     86 0 151 my ($lx, $base)= @_;
2253 86         135 my $r= create($lx, 'TypePost', qw(base functor arg));
2254 86         119 $r->{base}= $base;
2255              
2256 86         131 ($r->{functor}, $r->{arg})= parse_type_post_inner($lx);
2257             return
2258 86 50       177 if $lx->{error};
2259              
2260             return $base
2261 86 100       242 unless defined $r->{functor};
2262              
2263 56         116 return parse_type_post ($lx, $r);
2264             }
2265              
2266             sub parse_type($)
2267             {
2268 30     30 0 48 my ($lx)= @_;
2269 30         61 my $r= create($lx, 'Type', qw(base token));
2270              
2271 30 100       78 if (looking_at($lx, ['interpol', 'interpolType'])) {
2272 8         12 $r->{type}= 'interpol';
2273 8         9 $r->{token}= $lx->{token};
2274 8         17 lexer_shift($lx);
2275             }
2276             else {
2277 22 50       80 unless ($type_spec{$lx->{token}{kind}}) {
2278 0         0 $lx->{error}= 'Expected type name, but found '.token_describe($lx->{token});
2279 0         0 return;
2280             }
2281 22         39 $r->{type}= 'base';
2282 22         41 $r->{base}= $lx->{token}{kind};
2283             }
2284              
2285 30         90 lock_hash %$r;
2286 30         553 return parse_type_post ($lx, $r);
2287             }
2288              
2289             sub parse_type_list($) # without enclosing (...)
2290             {
2291 1     1 0 3 my ($lx)= @_;
2292             return unless
2293 1 50       4 my $arg= parse_list ([], $lx, \&parse_type, ',', ')');
2294              
2295 1         3 my $r= create ($lx, ['TypeList','explicit'], qw(arg));
2296 1         3 $r->{arg}= $arg;
2297 1         3 lock_hash %$r;
2298 1         19 return $r;
2299             }
2300              
2301             sub parse_type_list_delim($) # with enclosing (...)
2302             {
2303 1     1 0 2 my ($lx)= @_;
2304              
2305             return parse_choice($lx,
2306             '(' => sub {
2307 1     1   3 lexer_shift($lx);
2308             return unless
2309 1 50 33     3 my $r= parse_type_list ($lx)
2310             and expect ($lx, ')', SHIFT);
2311 1         13 return $r;
2312             },
2313              
2314             'interpol' => sub { # Perl array reference:
2315 0     0   0 my $r= create ($lx, ['TypeList','interpol'], qw(token));
2316 0         0 $r->{token}= $lx->{token};
2317 0         0 lexer_shift($lx);
2318 0         0 lock_hash %$r;
2319 0         0 return $r;
2320             },
2321 1         5 );
2322             }
2323              
2324             sub parse_on_action($)
2325             {
2326 1     1 0 3 my ($lx)= @_;
2327 1         4 return looking_at($lx, ['RESTRICT', 'CASCADE', 'SET NULL', 'SET DEFAULT', 'NO ACTION'], SHIFT);
2328             }
2329              
2330             sub parse_references($)
2331             {
2332 3     3 0 6 my ($lx)= @_;
2333 3         7 my $r= create($lx, 'References', qw(table column match on_delete on_update));
2334              
2335 3         7 lexer_shift($lx);
2336              
2337             return unless
2338             $r->{table}= parse_table($lx)
2339 3 50 33     6 and $r->{column}= parse_list_delim($lx, \&parse_column_name);
2340              
2341 3 100       6 if (looking_at($lx, 'MATCH', SHIFT)) {
2342 1         4 $r->{match}= expect ($lx, ['FULL','PARTIAL','SINGLE'], SHIFT);
2343             }
2344              
2345             parse_try_list([], $lx, sub {
2346             parse_choice($lx,
2347             'ON DELETE' => sub {
2348 1         2 lexer_shift($lx);
2349             return unless
2350 1 50       4 $r->{on_delete}= parse_on_action($lx);
2351             },
2352             'ON UPDATE' => sub {
2353 0         0 lexer_shift($lx);
2354             return unless
2355 0 0       0 $r->{on_update}= parse_on_action($lx);
2356             },
2357             -default => sub {}
2358 4     4   21 );
2359 3         16 });
2360 3 50       51 return if $lx->{error};
2361              
2362 3         12 lock_hash %$r;
2363 3         68 return $r;
2364             }
2365              
2366             sub parse_column_spec_post_inner($)
2367             {
2368 38     38 0 54 my ($lx)= @_;
2369 38         48 my $functor= undef;
2370 38         73 my @arg= ();
2371              
2372 38         45 my $constraint= undef;
2373 38 100       58 if (looking_at($lx, 'CONSTRAINT', SHIFT)) {
2374             return unless
2375 2 50       6 $constraint= parse_constraint($lx);
2376             }
2377              
2378             parse_choice($lx,
2379             -default => sub {
2380 20 50   20   34 if ($constraint) {
2381 0         0 $lx->{error}= 'Constraint expected';
2382             }
2383             else {
2384 20         33 my ($func, $arg)= parse_type_post_inner($lx); # inherit column type post
2385 20 100       53 if ($func) {
2386 4         7 $functor= "type_$func";
2387 4         13 @arg= @$arg;
2388             }
2389             }
2390             },
2391             'NOT NULL' => sub {
2392 10     10   15 $functor= 'property';
2393 10         24 push @arg, $constraint, 'notnull', lexer_shift($lx);
2394             },
2395             'NULL' => sub {
2396 0     0   0 $functor= 'property';
2397 0         0 push @arg, $constraint, 'notnull', '';
2398 0         0 lexer_shift($lx);
2399             },
2400              
2401             'AUTO_INCREMENT' => sub {
2402 0     0   0 $functor= 'property';
2403 0         0 push @arg, $constraint, 'autoinc', lexer_shift($lx);
2404             },
2405             'DROP AUTO_INCREMENT' => sub {
2406 0     0   0 $functor= 'property';
2407 0         0 push @arg, $constraint, 'autoinc', '';
2408 0         0 lexer_shift($lx);
2409             },
2410              
2411             'UNIQUE' => sub {
2412 1     1   2 $functor= 'property';
2413 1         2 push @arg, $constraint, 'unique', lexer_shift($lx);
2414             },
2415             'DROP UNIQUE' => sub {
2416 0     0   0 $functor= 'property';
2417 0         0 push @arg, $constraint, 'unique', '';
2418 0         0 lexer_shift($lx);
2419             },
2420              
2421             'PRIMARY KEY' => sub {
2422 0     0   0 $functor= 'property';
2423 0         0 push @arg, $constraint, 'primary', lexer_shift($lx);
2424             },
2425             'DROP PRIMARY KEY' => sub {
2426 0     0   0 $functor= 'property';
2427 0         0 push @arg, $constraint, 'primary', '';
2428 0         0 lexer_shift($lx);
2429             },
2430              
2431             'KEY' => sub {
2432 0     0   0 $functor= 'property';
2433 0         0 push @arg, $constraint, 'key', lexer_shift($lx);
2434             },
2435             'DROP KEY' => sub {
2436 0     0   0 $functor= 'property';
2437 0         0 push @arg, $constraint, 'key', '';
2438 0         0 lexer_shift($lx);
2439             },
2440              
2441             'DEFAULT' => sub {
2442 6     6   13 lexer_shift($lx);
2443             return unless
2444 6 50       13 my $val= parse_expr($lx);
2445 6         11 $functor= 'property';
2446 6         14 push @arg, $constraint, 'default', $val;
2447             },
2448             'DROP DEFAULT' => sub {
2449 0     0   0 lexer_shift($lx);
2450 0         0 $functor= 'property';
2451 0         0 push @arg, $constraint, 'default', '';
2452             },
2453              
2454             'CHECK' => sub {
2455 0     0   0 lexer_shift($lx);
2456             return unless
2457 0 0       0 my $val= parse_expr($lx);
2458 0         0 $functor= 'property';
2459 0         0 push @arg, $constraint, 'check', $val;
2460             },
2461             'DROP CHECK' => sub {
2462 0     0   0 lexer_shift($lx);
2463 0         0 $functor= 'property';
2464 0         0 push @arg, $constraint, 'check', '';
2465             },
2466              
2467             ($read_dialect{mysql} ?
2468             (
2469             'COMMENT' => sub {
2470 0     0   0 lexer_shift($lx);
2471             return unless
2472 0 0       0 my $val= parse_expr($lx);
2473 0         0 $functor= 'property';
2474 0         0 push @arg, $constraint, 'comment', $val;
2475             },
2476             'DROP COMMENT' => sub {
2477 0     0   0 lexer_shift($lx);
2478 0         0 $functor= 'property';
2479 0         0 push @arg, $constraint, 'comment', '';
2480             },
2481             'COLUMN_FORMAT' => sub {
2482 0     0   0 lexer_shift($lx);
2483 0         0 $functor= 'property';
2484 0         0 push @arg, $constraint, 'column_format',
2485             expect($lx, ['FIXED','DYNAMIC','DEFAULT'], SHIFT);
2486             },
2487             'STORAGE' => sub {
2488 0     0   0 lexer_shift($lx);
2489 0         0 $functor= 'property';
2490 0         0 push @arg, $constraint, 'storage',
2491             expect($lx, ['DISK','MEMORY','DEFAULT'], SHIFT);
2492             }
2493             )
2494             : ()
2495             ),
2496              
2497             'REFERENCES' => sub {
2498             return unless
2499 1 50   1   4 my $ref= parse_references($lx);
2500 1         2 $functor= 'property';
2501 1         4 push @arg, $constraint, 'references', $ref;
2502             },
2503             'DROP REFERENCES' => sub {
2504 0     0   0 lexer_shift($lx);
2505 0         0 $functor= 'property';
2506 0         0 push @arg, $constraint, 'references', '';
2507             },
2508 38 50       772 );
2509              
2510 38         888 return ($functor, \@arg);
2511             }
2512              
2513             sub parse_column_spec_post($$);
2514             sub parse_column_spec_post($$)
2515             {
2516 38     38 0 62 my ($lx, $base)= @_;
2517              
2518 38         66 my $r= create($lx, 'ColumnSpecPost', qw(base functor arg));
2519 38         76 $r->{base}= $base;
2520 38         56 $r->{arg}= [];
2521              
2522 38         70 ($r->{functor}, $r->{arg})= parse_column_spec_post_inner($lx);
2523             return
2524 38 50       86 if $lx->{error};
2525              
2526             return $base
2527 38 100       120 unless defined $r->{functor};
2528              
2529 22         50 return parse_column_spec_post ($lx, $r);
2530             }
2531              
2532             sub parse_column_spec($)
2533             {
2534 16     16 0 31 my ($lx)= @_;
2535              
2536 16         35 my $r= create($lx, 'ColumnSpec', qw(datatype name token));
2537              
2538             parse_choice($lx,
2539             'interpolColumnSpec' => 'interpol',
2540             'interpol' => sub {
2541 7     7   11 $r->{type}= 'interpol';
2542 7         10 $r->{token}= $lx->{token};
2543 7         13 lexer_shift($lx);
2544             },
2545              
2546             -default => sub {
2547 9     9   17 $r->{type}= 'base';
2548             return unless
2549 9 50       27 $r->{datatype}= parse_type($lx);
2550             }
2551 16         87 );
2552 16 50       118 return if $lx->{error};
2553              
2554 16         49 lock_hash %$r;
2555 16         325 return parse_column_spec_post($lx, $r);
2556              
2557             }
2558              
2559             sub parse_expr_list($) # without enclosing (...)
2560             {
2561 9     9 0 16 my ($lx)= @_;
2562 9 100       28 if (looking_at($lx, [@SELECT_INITIAL,'interpolStmt'])) {
2563             return unless
2564 3 50       7 my $q= parse_select_stmt($lx);
2565              
2566 3         10 my $r= create_Expr ($lx);
2567 3         4 $r->{type}= 'subquery';
2568 3         5 $r->{arg}= $q;
2569 3         13 return $r;
2570             }
2571             else {
2572 6         19 my $r= create ($lx, ['ExprList','explicit'], qw(arg));
2573              
2574             return unless
2575 6 50       24 my $arg= parse_list ([], $lx, \&parse_expr, ',', ')');
2576              
2577 6         13 $r->{arg}= $arg;
2578 6         21 lock_hash %$r;
2579 6         129 return $r;
2580             }
2581             }
2582              
2583             sub parse_expr_list_delim($) # with enclosing (...)
2584             {
2585 15     15 0 29 my ($lx)= @_;
2586              
2587             return parse_choice($lx,
2588             '(' => sub {
2589 9     9   20 lexer_shift($lx);
2590             return unless
2591 9 50 33     25 my $r= parse_expr_list ($lx)
2592             and expect ($lx, ')', SHIFT);
2593 9         98 return $r;
2594             },
2595              
2596             'interpol' => sub { # Perl array reference:
2597 5     5   16 my $r= create ($lx, ['ExprList','interpol'], qw(token));
2598 5         12 $r->{token}= $lx->{token};
2599 5         10 lexer_shift($lx);
2600 5         12 lock_hash %$r;
2601 5         129 return $r;
2602             },
2603 15         72 );
2604             }
2605              
2606             sub get_rhs($$)
2607             {
2608 190     190 0 293 my ($left, $arg_i)= @_;
2609 190   33     715 return $left->{rhs_map}{$arg_i} || $left->{rhs};
2610             }
2611              
2612             sub parse_thing($$;$$)
2613             {
2614 203     203 0 357 my ($lx, $thing_name, $left, $right_mark)= @_;
2615             return switch ($thing_name,
2616             'expr' => sub {
2617 191     191   356 return parse_expr ($lx, $left, $right_mark)
2618             },
2619             'type' => sub {
2620 1     1   2 return parse_type ($lx);
2621             },
2622             'string_expr' => sub {
2623 1     1   2 return parse_expr ($lx, $left, 'string')
2624             },
2625             'expr_list' => sub {
2626 9     9   18 return parse_expr_list_delim($lx);
2627             },
2628             'type_list' => sub {
2629 1     1   4 return parse_type_list_delim($lx);
2630             },
2631 203         1122 );
2632             }
2633              
2634             sub parse_funcsep($$$)
2635             {
2636 8     8 0 13 my ($lx, $r, $pattern)= @_;
2637 8         14 for my $e (@$pattern) {
2638 34 100       77 if (!ref($e)) {
    100          
    50          
2639             return unless
2640 14 50       22 expect($lx, $e, SHIFT);
2641 14         20 push @{ $r->{arg} }, $e; # no ref()
  14         28  
2642             }
2643             elsif (ref($e) eq 'SCALAR') {
2644             return unless
2645 13 50       24 my $arg= parse_thing($lx, $$e); # will return a ref()
2646 13         50 push @{ $r->{arg} }, $arg;
  13         31  
2647             }
2648             elsif (ref($e) eq 'ARRAY') {
2649 7 100       13 if (looking_at($lx, $e->[0])) {
2650             return unless
2651 2 50       7 parse_funcsep($lx, $r, $e);
2652             }
2653             }
2654             else {
2655 0         0 die "Unrecognised pattern piece, ref()=".ref($e);
2656             }
2657             }
2658 8         28 return $r;
2659             }
2660              
2661             sub parse_check($)
2662             {
2663 9     9 0 11 my ($lx)= @_;
2664 9         14 my $r= create ($lx, 'Check', qw(expr));
2665              
2666 9         17 my $cond= parse_expr_post($lx, undef, undef, create($lx, 'ExprEmpty'));
2667 9 50       17 return unless $cond;
2668              
2669 9         14 $r->{expr}= $cond;
2670 9         19 return $r;
2671             }
2672              
2673             sub parse_when_post($)
2674             {
2675 42     42 0 58 my ($lx)= @_;
2676              
2677             return unless
2678 42 100       65 looking_at($lx, 'WHEN', SHIFT); # no error if false (-> parse_try_list)
2679              
2680 20         28 my $cond;
2681              
2682 20         40 my $functor= find_functor(\%functor_suffix, $lx->{token}{kind});
2683 20 100 66     58 if ($functor && $functor->{allow_when}) {
2684 9         20 $cond= parse_expr_post($lx, undef, undef, create($lx, 'ExprEmpty'));
2685             }
2686             else {
2687 11         22 $cond= parse_expr($lx);
2688             }
2689              
2690             return unless
2691 20 50 33     59 $cond
      33        
2692             and expect($lx, 'THEN', SHIFT)
2693             and my $expr= parse_expr($lx);
2694              
2695 20         30 $cond->{maybe_check}= 1; # allow Check interpolation if this is an Expr
2696              
2697 20         64 return [ $cond, $expr ];
2698             }
2699              
2700             sub parse_when($)
2701             {
2702 22     22 0 29 my ($lx)= @_;
2703              
2704             return unless
2705 22 50 66     31 looking_at($lx, 'WHEN', SHIFT) # no error if false (-> parse_try_list)
      66        
      33        
2706             and my $cond= parse_expr($lx)
2707             and expect($lx, 'THEN', SHIFT)
2708             and my $expr= parse_expr($lx);
2709              
2710 10         30 return [ $cond, $expr ];
2711             }
2712              
2713             sub shift_or_reduce_pure($$$)
2714             # $right_mark is either 0/undef, 1, or 'string', see parse_expr().
2715             {
2716 183     183 0 261 my ($left, $right, $right_mark)= @_;
2717              
2718             # hack for 'IN':
2719             return ACTION_REDUCE
2720             if ($right_mark || '') eq 'string' &&
2721 183 100 100     625 $right->{value} eq 'IN';
      66        
2722              
2723             # currently, this is very simple, because we don't use precedences:
2724 182 100       365 return ACTION_SHIFT
2725             unless $left;
2726              
2727             # special rule to allow sequencing even for operators without precedence:
2728             return ACTION_REDUCE
2729             if $left->{value} eq $right->{value} &&
2730 4 100 66     16 $left->{read_type} eq 'infix()';
2731              
2732             # parse with precedences?
2733 3 50       9 if ($do_prec) {
2734             # if both have a precedence:
2735 0 0 0     0 if ($left->{prec} && $right->{prec}) {
2736             return ACTION_REDUCE
2737 0 0       0 if $left->{prec} > $right->{prec};
2738              
2739             return ACTION_SHIFT
2740 0 0       0 if $left->{prec} < $right->{prec};
2741              
2742             # if both have an associativity and the associativity is the same:
2743 0 0 0     0 if ($left->{assoc} && $right->{assoc} &&
      0        
2744             $left->{assoc} == $right->{assoc})
2745             {
2746 0 0 0     0 if ($left->{assoc} == ASSOC_LEFT && $right_mark) {
2747 0         0 return ACTION_REDUCE;
2748             }
2749             else {
2750 0         0 return ACTION_SHIFT;
2751             }
2752             }
2753             }
2754             }
2755             else {
2756             # no precedences at all:
2757             # For infix23 and infix3, we need to reduce, instead of failing:
2758 3 50       6 if (defined $left->{value2}) {
2759 3         6 return ACTION_REDUCE;
2760             }
2761             }
2762              
2763             # otherwise: ambiguous
2764 0         0 return ACTION_AMBIGUOUS;
2765             }
2766              
2767             sub shift_or_reduce($$$$)
2768             {
2769 183     183 0 281 my ($lx, $left, $right, $right_mark)= @_;
2770 183         310 my $result= shift_or_reduce_pure ($left, $right, $right_mark);
2771 183 50       320 unless ($result) {
2772 0         0 $lx->{error}= "Use of operators '$left->{value}' vs. '$right->{value}' ".
2773             "requires parentheses.";
2774             }
2775 183         326 return $result;
2776             }
2777              
2778             sub find_functor($$)
2779             {
2780 1123     1123 0 1484 my ($map, $kind)= @_;
2781              
2782             return unless
2783 1123 100       1551 my $functor= find_ref(%$map, $kind);
2784              
2785 263 100       566 if (my $accept= $functor->{accept}) {
2786 3         7 for my $a (@$accept) {
2787 3 50       12 if ($read_dialect{$a}) {
2788 3         9 return $functor;
2789             }
2790             }
2791 0         0 return;
2792             }
2793              
2794 260         432 return $functor;
2795             }
2796              
2797             sub set_expr_functor($$@)
2798             {
2799 260     260 0 376 my ($r, $functor, @arg)= @_;
2800 260 50       436 my_confess if $r->{arg};
2801              
2802 260         382 $r->{type}= $functor->{type};
2803 260         345 $r->{functor}= $functor;
2804 260         417 $r->{arg}= [ @arg ];
2805             }
2806              
2807             sub parse_expr_post($$$$)
2808             # $right_mark is either 0/undef, 1, or 'string', see parse_expr().
2809             {
2810 1021     1021 0 1673 my ($lx, $left, $right_mark, $arg1)= @_;
2811              
2812             # infix:
2813 1021         1437 my $kind= $lx->{token}{kind};
2814              
2815 1021 100       1481 if (my $right= find_functor(\%functor_suffix, $kind)) {
2816             return unless
2817 183 50       310 my $action= shift_or_reduce($lx, $left, $right, $right_mark);
2818              
2819 183 100       359 if ($action == ACTION_SHIFT) {
2820 178         311 lexer_shift ($lx);
2821              
2822 178         261 my $r= create_Expr ($lx);
2823 178         363 set_expr_functor ($r, $right, $arg1);
2824              
2825             switch ($right->{read_type},
2826             'infix2' => sub {
2827             # parse second arg:
2828             return unless
2829 91 100   91   161 my $arg2= parse_thing ($lx, get_rhs($right,0), $right, 1);
2830 90         395 push @{ $r->{arg} }, $arg2;
  90         221  
2831             },
2832             'infix()' => sub {
2833             # parse sequence:
2834 63     63   78 my $i=0;
2835 63         68 do {
2836             return unless
2837 64 50       123 my $argi= parse_thing ($lx, get_rhs($right,$i++), $right, 1);
2838 64         308 push @{ $r->{arg} }, $argi;
  64         189  
2839             } while (looking_at($lx, $kind, SHIFT)); # same operator?
2840             },
2841             'infix23' => sub {
2842             # parse second arg:
2843             return unless
2844 2 50   2   6 my $arg2= parse_thing ($lx, get_rhs($right,0), $right, 1);
2845 2         8 push @{ $r->{arg} }, $arg2;
  2         5  
2846              
2847             # maybe parse third arg:
2848 2 50       5 if (looking_at ($lx, $right->{value2}, SHIFT)) {
2849             return unless
2850 0 0       0 my $arg3= parse_thing ($lx, get_rhs($right,1), $right, 1);
2851 0         0 push @{ $r->{arg} }, $arg3;
  0         0  
2852             }
2853             },
2854             'infix3' => sub {
2855             # parse second arg:
2856             return unless
2857             my $arg2= parse_thing ($lx, get_rhs($right,0), $right, 1)
2858 2 50 33 2   5 and expect ($lx, $right->{value2}, SHIFT)
      33        
2859             and my $arg3= parse_thing ($lx, get_rhs($right,1), $right, 1); # descend
2860              
2861 2         13 push @{ $r->{arg} }, $arg2, $arg3;
  2         5  
2862             },
2863       20     'suffix' => sub {
2864             # nothing more to do
2865             }
2866 178         1305 );
2867 178 100       1629 return if $lx->{error};
2868              
2869 177         424 lock_keys %$r; # {maybe_check} may be modified if we parse WHEN clauses.
2870              
2871 177         1351 return parse_expr_post ($lx, $left, $right_mark, $r); # descend
2872             }
2873             }
2874              
2875 843         2613 return $arg1;
2876             }
2877              
2878             sub parse_expr($;$$)
2879             # $right_mark is either 0/undef, 1, or 'string'.
2880             # 'string' is a hack for POSITION(a IN b) and keeps parse_expr
2881             # from shifting IN. It's one of these typical design complications
2882             # in SQL grammar that prevents you from writing a straight-forward
2883             # recursive parser. If $right_mark eq 'string', then $functor
2884             # is undef. Otherwise $functor is defined if $right_mark is true.
2885             {
2886 826     826 0 1494 my ($lx, $functor, $right_mark)= @_;
2887 826         1067 my $r= create_Expr ($lx);
2888              
2889             parse_choice($lx,
2890             '.' => sub {
2891 23     23   52 lexer_shift($lx);
2892 23         35 $r->{type}= 'column';
2893 23         63 $r->{arg}= parse_column ($lx);
2894             },
2895              
2896             'interpolColumn' => 'ident',
2897             'interpolTable' => 'ident',
2898             '*' => 'ident',
2899             'ident' => sub {
2900 289     289   412 $r->{type}= 'column';
2901 289         442 $r->{arg}= parse_column ($lx);
2902             },
2903              
2904             'interpolExpr' => sub {
2905 206     206   289 $r->{type}= 'interpol';
2906 206         235 $r->{token}= $lx->{token};
2907 206         273 lexer_shift($lx);
2908             },
2909              
2910             'interpol' => sub {
2911 173     173   357 parse_value_or_column_into ($lx, $r, 'interpol');
2912             },
2913              
2914             'TRUE' => '?',
2915             'FALSE' => '?',
2916             'NULL' => '?',
2917             'UNKNOWN' => '?',
2918             'DEFAULT' => '?',
2919             '?' => sub {
2920 7     7   12 $r->{type}= 'interpol';
2921 7         11 $r->{token}= $lx->{token};
2922 7         14 lexer_shift($lx);
2923              
2924             # special care for functors like MySQL's DEFAULT(...). Since
2925             # there's both DEFAULT and DEFAULT(...), we need to check. We
2926             # use find_functor() in order to support read_dialect properly.
2927 7 100 66     15 if (looking_at($lx, '(', SHIFT) and
2928             my $functor= find_functor(\%functor_special, $r->{token}{kind}))
2929             {
2930             switch ($functor->{read_type},
2931             'funcall1col' => sub {
2932             return unless
2933 1 50 33     4 my $arg= parse_column_name($lx)
2934             and expect ($lx, ')', SHIFT);
2935 1         3 set_expr_functor ($r, $functor, $arg);
2936             }
2937 1         5 );
2938             }
2939             },
2940              
2941             'CASE' => sub {
2942 34     34   92 lexer_shift($lx);
2943 34         52 $r->{type}= 'case';
2944 34 100       74 if (looking_at($lx, ['WHEN','ELSE','END'])) { # without 'switchval'
2945             return unless
2946 12 50       25 $r->{arg}= parse_try_list([], $lx, \&parse_when);
2947             }
2948             else { # with switchval
2949             return unless
2950             $r->{switchval}= parse_expr($lx)
2951 22 50 33     45 and $r->{arg}= parse_try_list([], $lx, \&parse_when_post);
2952             }
2953              
2954 34 100       69 if (looking_at($lx, 'ELSE', SHIFT)) {
2955             return unless
2956 24 50       39 $r->{otherwise}= parse_expr($lx);
2957             }
2958              
2959             return unless
2960 34 50       63 expect($lx, 'END', SHIFT);
2961             },
2962              
2963             'DISTINCT' => 'SOME',
2964             'ALL' => 'SOME',
2965             'ANY' => 'SOME',
2966             'SOME' => sub {
2967             #if (!$functor || !$functor->{comparison} || !$right_mark) {
2968             # $lx->{error}= "$lx->{token}{kind} can only be used directly after a comparison.";
2969             # return;
2970             #}
2971 2     2   5 my $functor2= find_functor(\%functor_special, $lx->{token}{kind});
2972 2 50       5 unless ($functor2) {
2973 0         0 $lx->{error}= "Unexpected $lx->{token}{kind} in expression.";
2974 0         0 return;
2975             }
2976 2         5 lexer_shift($lx);
2977              
2978 2         2 my $r2;
2979 2 100       5 if (looking_at($lx, '(')) {
2980             return unless
2981 1 50 33     2 expect($lx, '(', SHIFT)
      33        
2982             and my $q= parse_select_stmt ($lx)
2983             and expect($lx, ')', SHIFT);
2984              
2985 1         4 $r2= create_Expr($lx);
2986 1         2 $r2->{type}= 'subquery';
2987 1         1 $r2->{arg}= $q;
2988              
2989             }
2990             else {
2991 1         4 $r2 = parse_expr($lx, $functor, $right_mark);
2992             }
2993              
2994 2         4 set_expr_functor ($r, $functor2, $r2);
2995             },
2996              
2997             '(' => sub {
2998 13     13   25 lexer_shift($lx);
2999 13 100       32 if (looking_at($lx, [@SELECT_INITIAL,'interpolStmt'])) {
3000             return unless
3001 2 50       6 my $q= parse_select_stmt ($lx);
3002 2         8 $r->{type}= 'subquery';
3003 2         3 $r->{arg}= $q;
3004             }
3005             else {
3006             return unless
3007 11 50       26 my $arg= parse_expr($lx);
3008 11         17 $r->{type}= '()';
3009 11         15 $r->{arg}= $arg;
3010             }
3011             return unless
3012 13 50       30 expect($lx, ')', SHIFT);
3013             },
3014              
3015             -default => sub {
3016 79     79   152 my $functor2= find_functor(\%functor_prefix, $lx->{token}{kind});
3017 79 100 66     206 if (!$functor2 && $lx->{token}{type} eq 'keyword') { # generic funcall
3018 11         31 $functor2= make_op($lx->{token}{kind}, 'funcall');
3019             }
3020              
3021             # prefix / funcall:
3022 79 50       121 if ($functor2) {
    0          
3023 79         145 set_expr_functor ($r, $functor2);
3024 79         139 lexer_shift($lx);
3025              
3026             switch ($functor2->{read_type},
3027             'prefix' => sub {
3028 45         53 my $arg;
3029 45 100       77 if (looking_at($lx, '(', NO_SHIFT)) {
3030             return unless
3031 16 50       40 $r->{arg}= parse_list_delim ($lx, \&parse_expr);
3032             }
3033             else {
3034             return unless
3035 29 50       58 my $arg= parse_thing ($lx, get_rhs($functor2,0), $functor2, 0);
3036 29         162 $r->{arg}= [ $arg ];
3037             }
3038             },
3039             'funcall' => sub {
3040             return unless
3041 27 50       63 $r->{arg}= parse_list_delim ($lx, \&parse_expr);
3042             },
3043             'funcall1col' => sub {
3044             return unless
3045 1 50 33     4 expect ($lx, '(', SHIFT)
      33        
3046             and my $arg1= parse_column_name($lx)
3047             and expect ($lx, ')', SHIFT);
3048 1         3 $r->{arg}= [ $arg1 ];
3049             },
3050             'funcsep' => sub {
3051             return unless
3052             expect ($lx, '(', SHIFT)
3053 6 50 33     11 and parse_funcsep ($lx, $r, $functor2->{rhs});
3054             },
3055 79         522 );
3056 79 50       610 return if $lx->{error};
3057             }
3058             # error:
3059             elsif (! $lx->{error}) {
3060 0         0 $lx->{error}= "Unexpected ".token_describe($lx->{token})." in expression";
3061             }
3062              
3063 79         145 return;
3064             },
3065 826         9050 );
3066 826 50       16852 return if $lx->{error};
3067              
3068 826 50       1747 die unless $r;
3069 826         1821 lock_keys %$r; # {arg} may be modified when parsing sequenced infix operators
3070             # and {maybe_check} may be modified when parsing WHEN clauses
3071              
3072             # And now parse the suffix:
3073 826         6344 return parse_expr_post ($lx, $functor, $right_mark, $r);
3074             }
3075              
3076             sub parse_limit_num($) # Simply returns the single token if it is appropriate.
3077             {
3078 32     32 0 55 my ($lx)= @_;
3079             return parse_choice($lx,
3080             'interpolExpr' => 'interpol',
3081             '?' => 'interpol',
3082             'interpol' => sub {
3083 32     32   50 my $r= $lx->{token};
3084 32         59 lexer_shift($lx);
3085 32         239 return $r;
3086             },
3087 32         132 );
3088             }
3089              
3090             sub parse_expr_as($)
3091             {
3092 231     231 0 306 my ($lx)= @_;
3093 231         325 my $r= create ($lx, 'ExprAs', qw(expr as));
3094              
3095             return unless
3096 231 100       437 $r->{expr}= parse_expr($lx);
3097              
3098 230 100       365 if (looking_at($lx, 'AS', SHIFT)) {
3099             return unless
3100 4 50       16 $r->{as}= parse_column_name($lx);
3101             }
3102              
3103 230         504 lock_hash %$r;
3104 230         4205 return $r;
3105             }
3106              
3107             sub parse_order($)
3108             {
3109 35     35 0 54 my ($lx)= @_;
3110 35         57 my $r= create ($lx, 'Order', qw(type expr token desc));
3111 35         44 $r->{desc}= 0;
3112              
3113             parse_choice($lx,
3114             -default => sub {
3115 14     14   22 $r->{type}= 'expr';
3116             return unless
3117 14 50       29 $r->{expr}= parse_expr($lx);
3118             },
3119              
3120             'interpolOrder' => 'interpol',
3121             'interpol' => sub {
3122 21 100   21   54 if ($lx->{token}{type} eq 'string') {
3123             # Strings are still expressions, not column names. There is no
3124             # other way of forcing Perl interpolation to String type, so
3125             # we assume a string here.
3126 3         6 $r->{type}= 'expr';
3127             return unless
3128 3 50       7 $r->{expr}= parse_expr($lx);
3129             }
3130             else {
3131 18         23 $r->{type}= 'interpol';
3132 18         21 $r->{token}= $lx->{token};
3133 18         32 lexer_shift($lx);
3134             }
3135             },
3136 35         165 );
3137 35 50       264 return if $lx->{error};
3138              
3139             parse_choice($lx,
3140       23     -default => sub {}, # no error
3141 2     2   7 'ASC' => sub { lexer_shift($lx); $r->{desc}= 0; },
  2         5  
3142 10     10   16 'DESC' => sub { lexer_shift($lx); $r->{desc}= 1; },
  10         19  
3143 35         170 );
3144              
3145 35         266 lock_hash %$r;
3146 35         667 return $r;
3147             }
3148              
3149             sub parse_join($)
3150             {
3151 133     133 0 165 my ($lx)= @_;
3152 133         230 my $r= create ($lx, 'Join', qw(token table qual on using natural));
3153              
3154             #print STDERR "parse join: ".token_describe($lx->{token})."\n";
3155             parse_choice($lx,
3156             'interpolJoin' => 'interpol',
3157             'interpol' => sub {
3158             $r->{type}= 'interpol',
3159 5     5   11 $r->{token}= $lx->{token};
3160 5         11 lexer_shift($lx);
3161             },
3162              
3163             -default => sub {
3164 128     128   176 my $shifted= 0;
3165              
3166 128         134 my $want_condition= 1;
3167 128 100       197 if (looking_at($lx, 'NATURAL', SHIFT)) {
3168 3         4 $r->{natural}= 1;
3169 3         5 $shifted= 1;
3170 3         4 $want_condition= 0;
3171             }
3172              
3173             parse_choice($lx,
3174             -default => sub {
3175 118         218 $r->{type}= 'INNER';
3176             },
3177              
3178             'INNER' => sub{
3179 4         6 $r->{type}= 'INNER';
3180 4         9 lexer_shift($lx);
3181 4         7 $shifted= 1;
3182             },
3183              
3184             'UNION' => 'CROSS',
3185             'CROSS' => sub {
3186 3 50       16 if ($r->{natural}) {
3187 0         0 $lx->{error}= "NATURAL cannot be used with CROSS or UNION JOIN";
3188 0         0 return;
3189             }
3190              
3191 3         7 $r->{type}= lexer_shift($lx);
3192 3         5 $want_condition= 0;
3193 3         5 $shifted= 1;
3194             },
3195              
3196             'LEFT' => 'FULL',
3197             'RIGHT' => 'FULL',
3198             'FULL' => sub {
3199 3         7 $r->{type}= lexer_shift($lx);
3200 3         9 looking_at($lx, 'OUTER', SHIFT);
3201 3         4 $shifted= 1;
3202             },
3203 128         809 );
3204 128 50       1167 return if $lx->{error};
3205              
3206 128 100       217 unless (looking_at ($lx, 'JOIN', SHIFT)) {
3207 117 50       186 if ($shifted) {
3208 0         0 $lx->{error}= "Expected JOIN, but found ".token_describe($lx->{token});
3209             }
3210 117         245 $r= undef;
3211 117         199 return;
3212             }
3213              
3214             return unless
3215 11 50       28 $r->{table}= parse_list([], $lx, \&parse_table_as, ',');
3216              
3217 11 100       27 if ($want_condition) {
3218             parse_choice($lx,
3219             'ON' => sub {
3220 3         9 lexer_shift($lx);
3221 3         8 $r->{on}= parse_expr($lx);
3222             },
3223             'USING' => sub {
3224 2         4 lexer_shift($lx);
3225             return unless
3226 2 50       7 $r->{using}= parse_list_delim ($lx, \&parse_column_name);
3227             },
3228 5         21 );
3229             }
3230             }
3231 133         727 );
3232 133 50       1242 return if $lx->{error};
3233 133 100       429 return unless $r;
3234              
3235 16         42 lock_hash %$r;
3236 16         361 return $r;
3237             }
3238              
3239             sub push_option($$$)
3240             {
3241 503     503 0 647 my ($lx, $list, $words)= @_;
3242 503 100       673 if (my $x= looking_at($lx, $words, SHIFT)) {
3243 5         13 push @$list, $x;
3244 5         15 return $x;
3245             }
3246 498         856 return 0;
3247             }
3248              
3249             sub push_option_list($$$)
3250             {
3251 310     310 0 462 my ($lx, $list, $words)= @_;
3252 310         446 while (push_option($lx, $list, $words)) {}
3253             }
3254              
3255             sub parse_where($) # WHERE is supposed to haveing been parsed already here
3256             {
3257 56     56 0 92 my ($lx)= @_;
3258             # FIXME: MISSING:
3259             # - WHERE CURRENT OF (i.e., cursor support)
3260 56         121 return parse_expr($lx);
3261             }
3262              
3263             sub parse_select($)
3264             {
3265 185     185 0 270 my ($lx)= @_;
3266 185         793 my $r= create ($lx, ['Stmt','Select'],
3267             qw(
3268             opt_front
3269             opt_back
3270             expr_list
3271             from
3272             join
3273             where
3274             group_by
3275             group_by_with_rollup
3276             having
3277             order_by
3278             limit_cnt
3279             limit_offset
3280             )
3281             );
3282              
3283 185 50       384 return unless expect($lx, 'SELECT', SHIFT);
3284              
3285             # Missing:
3286             # PostgresQL:
3287             # - DISTINCT **ON**
3288             # - WITH
3289             # - WINDOW
3290             # - FETCH
3291             # - FOR UPDATE|SHARE **OF** ( , ... )
3292             #
3293             # All:
3294             # - UNION
3295             # - INTERSECT
3296             # - EXCEPT
3297             # - FETCH [ FIRST | NEXT ] count [ ROW | ROWS ] ONLY (same as LIMIT in SQL:2008)
3298              
3299 185         305 $r->{opt_front}= [];
3300             push_option ($lx, $r->{opt_front}, [
3301             'DISTINCT', 'ALL',
3302             ($read_dialect{mysql} ?
3303 185 50       671 ('DISTINCTROW')
3304             : ()
3305             )
3306             ]);
3307              
3308             push_option_list ($lx, $r->{opt_front}, [
3309             ($read_dialect{mysql} ?
3310             (
3311 185 50       710 'HIGH_PRIORITY', 'STRAIGHT_JOIN',
3312             'SQL_SMALL_RESULT', 'SQL_BIG_RESULT', 'SQL_BUFFER_RESULT',
3313             'SQL_CACHE', 'SQL_NO_CACHE', 'SQL_CALC_FOUND_ROWS'
3314             )
3315             : ()
3316             )
3317             ]);
3318              
3319             return unless
3320 185 100       452 $r->{expr_list}= parse_list([], $lx, \&parse_expr_as, ',');
3321              
3322 184 100       296 if (looking_at($lx, 'FROM', SHIFT)) {
3323             return unless
3324             $r->{from}= parse_list([], $lx, \&parse_table_as, ',')
3325 99 50 33     243 and $r->{join}= parse_try_list([], $lx, \&parse_join);
3326              
3327 99 100       185 if (looking_at($lx, 'WHERE', SHIFT)) {
3328             return unless
3329 44 50       99 $r->{where}= parse_where ($lx);
3330             }
3331 99 100       177 if (looking_at($lx, 'GROUP BY', SHIFT)) {
3332             return unless
3333 6 50       21 $r->{group_by}= parse_list([], $lx, \&parse_order, ',');
3334              
3335 6         13 $r->{group_by_with_rollup}= looking_at($lx, 'WITH ROLLUP', SHIFT);
3336             }
3337 99 100       192 if (looking_at($lx, 'HAVING', SHIFT)) {
3338             return unless
3339 1 50       2 $r->{having}= parse_expr ($lx);
3340             }
3341 99 100       162 if (looking_at($lx, 'ORDER BY', SHIFT)) {
3342             return unless
3343 8 50       25 $r->{order_by}= parse_list([], $lx, \&parse_order, ',');
3344             }
3345              
3346 99 100       164 if (looking_at($lx, 'LIMIT', SHIFT)) {
3347 4 50       13 unless (looking_at($lx, 'ALL', SHIFT)) {
3348 4         14 my $first_num= parse_limit_num ($lx);
3349 4 100       15 if (looking_at($lx, ',', SHIFT)) {
3350 2         5 $r->{limit_offset}= $first_num;
3351 2         6 $r->{limit_cnt}= parse_limit_num($lx);
3352             }
3353             else {
3354 2         4 $r->{limit_cnt}= $first_num;
3355             }
3356             }
3357             }
3358 99 100 100     293 if (!$r->{limit_offset} &&
3359             looking_at ($lx, 'OFFSET', SHIFT))
3360             {
3361 1         3 $r->{limit_offset}= parse_limit_num ($lx);
3362             }
3363              
3364 99         160 $r->{opt_back}= [];
3365             push_option_list ($lx, $r->{opt_back}, [
3366             ($read_dialect{mysql} || $read_dialect{postgresql} ?
3367             ('FOR UPDATE')
3368             : ()
3369             ),
3370             ($read_dialect{mysql} ?
3371             (
3372             'LOCK IN SHARE MODE' # FIXME: normalise: PostgreSQL: FOR SHARE
3373             )
3374             : ()
3375             ),
3376             ($read_dialect{postgresql} ?
3377             (
3378 99 50 33     532 'FOR SHARE', # FIXME: normalise: MySQL: LOCK IN SHARE MODE
    50          
    50          
3379             'NOWAIT'
3380             )
3381             : ()
3382             ),
3383             ]);
3384             }
3385              
3386 184         507 lock_hash %$r;
3387 184         6895 return $r;
3388             }
3389              
3390             sub parse_insert($)
3391             {
3392 13     13 0 24 my ($lx)= @_;
3393 13         44 my $r= create ($lx, ['Stmt','Insert'],
3394             qw(
3395             opt_front
3396             into
3397             column
3398             default_values
3399             value
3400             value_interpol
3401             set
3402             select
3403             duplicate_update
3404             )
3405             );
3406              
3407 13 50       31 return unless expect($lx, 'INSERT', SHIFT);
3408              
3409             # PostgreSQL:
3410             # - RETURNING ...
3411              
3412 13         29 $r->{opt_front}= [];
3413             push_option_list ($lx, $r->{opt_front}, [
3414             ($read_dialect{mysql} ?
3415             (
3416 13 50       65 'IGNORE',
3417             'LOW_PRIORITY',
3418             'HIGH_PRIORITY',
3419             'DELAYED',
3420             )
3421             : ()
3422             )
3423             ]);
3424              
3425 13         37 looking_at($lx, 'INTO', SHIFT); # optional in MySQL
3426              
3427             return unless
3428 13 50       29 $r->{into}= parse_table($lx);
3429              
3430 13 100       26 if (looking_at($lx, '(')) {
3431             return unless
3432 5 50       23 $r->{column}= parse_list_delim($lx, \&parse_column_name);
3433             }
3434              
3435             parse_choice($lx,
3436             'DEFAULT VALUES' => sub {
3437 0     0   0 lexer_shift($lx);
3438 0         0 $r->{default_values}= 1;
3439             },
3440              
3441             'VALUE' => 'VALUES',
3442             'VALUES' => sub {
3443 5     5   17 lexer_shift($lx);
3444 5         20 $r->{value}= parse_list([], $lx, \&parse_expr_list_delim, ',');
3445             },
3446              
3447             'SET' => sub {
3448             # MySQL extension, but will be normalised to VALUES clause, so we
3449             # always accept this even with !$read_dialect{mysql}.
3450 8 50   8   19 if ($r->{column}) {
3451 0         0 $lx->{error}= "Either column list or 'SET' expected, but found both.";
3452 0         0 return;
3453             }
3454 8         18 lexer_shift($lx);
3455 8         25 $r->{set}= parse_list([], $lx, \&parse_expr, ',');
3456             },
3457              
3458 13         59 (map { $_ => 'interpolStmt' } @SELECT_INITIAL),
3459             'interpol' => 'interpolStmt',
3460             'interpolStmt' => sub {
3461 0     0   0 $r->{select}= parse_select_stmt($lx);
3462             },
3463 13         80 );
3464 13 50       164 return if $lx->{error};
3465              
3466 13 100 66     54 if ($read_dialect{mysql} &&
3467             looking_at ($lx, 'ON DUPLICATE KEY UPDATE', SHIFT))
3468             {
3469             return unless
3470 1 50       4 $r->{duplicate_update}= parse_list([], $lx, \&parse_expr, ',');
3471             }
3472              
3473 13         43 lock_hash %$r;
3474 13         490 return $r;
3475             }
3476              
3477             sub parse_update($)
3478             {
3479 9     9 0 17 my ($lx)= @_;
3480 9         32 my $r= create ($lx, ['Stmt','Update'],
3481             qw(
3482             opt_front
3483             table
3484             set
3485             from
3486             join
3487             where
3488             order_by
3489             limit_cnt
3490             limit_offset
3491             )
3492             );
3493              
3494 9 50       25 return unless expect($lx, 'UPDATE', SHIFT);
3495              
3496             # PostgreSQL:
3497             # - RETURNING ...
3498              
3499 9         31 $r->{opt_front}= [];
3500             push_option_list ($lx, $r->{opt_front}, [
3501             ($read_dialect{mysql} ?
3502             (
3503             'IGNORE',
3504             'LOW_PRIORITY',
3505             )
3506             : ()
3507             ),
3508             ($read_dialect{postgresql} ?
3509             (
3510 9 50       44 'ONLY',
    50          
3511             )
3512             : ()
3513             )
3514             ]);
3515              
3516             return unless
3517             $r->{table}= parse_list([], $lx, \&parse_table_as, ',')
3518             and expect($lx, 'SET', SHIFT)
3519 9 50 33     27 and $r->{set}= parse_list([], $lx, \&parse_expr, ',');
      33        
3520              
3521 9 100       20 if (looking_at($lx, 'FROM', SHIFT)) {
3522             return unless
3523 1 50       4 $r->{from}= parse_list([], $lx, \&parse_table_as, ',');
3524             }
3525             return unless
3526 9 50       23 $r->{join}= parse_try_list([], $lx, \&parse_join);
3527              
3528 9 50       20 if (looking_at($lx, 'WHERE', SHIFT)) {
3529             return unless
3530 9 50       16 $r->{where}= parse_where ($lx);
3531             }
3532 9 100       21 if (looking_at($lx, 'ORDER BY', SHIFT)) {
3533             return unless
3534 1 50       5 $r->{order_by}= parse_list([], $lx, \&parse_order, ',');
3535             }
3536 9 100       21 if (looking_at($lx, 'LIMIT', SHIFT)) {
3537 1         4 $r->{limit_cnt}= parse_limit_num($lx);
3538             }
3539              
3540 9         30 lock_hash %$r;
3541 9         294 return $r;
3542             }
3543              
3544             sub parse_delete($)
3545             {
3546 4     4 0 10 my ($lx)= @_;
3547 4         18 my $r= create ($lx, ['Stmt','Delete'],
3548             qw(
3549             opt_front
3550             from
3551             from_opt_front
3552             join
3553             using
3554             where
3555             order_by
3556             limit_cnt
3557             limit_offset
3558             )
3559             );
3560              
3561 4 50       12 return unless expect($lx, 'DELETE', SHIFT);
3562              
3563             # PostgreSQL:
3564             # - RETURNING ...
3565              
3566 4         10 $r->{opt_front}= [];
3567             push_option_list ($lx, $r->{opt_front}, [
3568             ($read_dialect{mysql} ?
3569             (
3570 4 50       23 'IGNORE',
3571             'LOW_PRIORITY',
3572             'QUICK'
3573             )
3574             : ()
3575             )
3576             ]);
3577              
3578 4 50       15 return unless expect($lx, 'FROM', SHIFT);
3579              
3580 4         10 $r->{from_opt_front}= [];
3581             push_option ($lx, $r->{from_opt_front}, [
3582             ($read_dialect{postgresql} ?
3583 4 50       21 ('ONLY')
3584             : ()
3585             )
3586             ]);
3587              
3588             return unless
3589 4 50       13 $r->{from}= parse_list([], $lx, \&parse_table_as, ',');
3590              
3591 4 100       11 if (looking_at($lx, 'USING', SHIFT)) {
3592             return unless
3593 2 50       8 $r->{using}= parse_list([], $lx, \&parse_table_as, ',');
3594             }
3595              
3596             return unless
3597 4 50       14 $r->{join}= parse_try_list([], $lx, \&parse_join);
3598              
3599 4 100       9 if (looking_at($lx, 'WHERE', SHIFT)) {
3600             return unless
3601 3 50       12 $r->{where}= parse_where ($lx);
3602             }
3603 4 50       12 if (looking_at($lx, 'ORDER BY', SHIFT)) {
3604             return unless
3605 0 0       0 $r->{order_by}= parse_list([], $lx, \&parse_order, ',');
3606             }
3607 4 50       11 if (looking_at($lx, 'LIMIT', SHIFT)) {
3608 0         0 $r->{limit_cnt}= parse_limit_num($lx);
3609             }
3610              
3611 4         15 lock_hash %$r;
3612 4         136 return $r;
3613             }
3614              
3615             sub keyword($$)
3616             {
3617 1     1 0 2 my ($lx, $keyword)= @_;
3618             return
3619 1 50       3 unless $keyword;
3620              
3621 1 50       3 return $keyword
3622             if ref($keyword);
3623            
3624 1         2 my $r= create($lx, 'Keyword', qw(keyword));
3625 1         2 $r->{keyword}= $keyword;
3626 1         4 lock_hash %$r;
3627 1         17 return $r;
3628             }
3629              
3630             sub parse_index_option($)
3631             {
3632 1     1 0 3 my ($lx)= @_;
3633 1         3 my $r= create($lx, 'IndexOption', qw(arg));
3634              
3635             parse_choice($lx,
3636             -default => sub {
3637 1     1   2 $r= undef;
3638             },
3639              
3640             # MySQL does not like it here, but only accepts it in front of the
3641             # column list, which is against the manual's description.
3642             #'USING' => sub {
3643             # lexer_shift($lx);
3644             # return unless
3645             # my $t= expect($lx, ['BTREE','HASH','RTREE'], SHIFT);
3646             # $r->{type}= 'using';
3647             # $r->{arg}= $t;
3648             #},
3649 1         5 );
3650 1 50       7 return unless $r;
3651 0 0       0 return if $lx->{error};
3652              
3653 0         0 lock_hash %$r;
3654 0         0 return $r;
3655             }
3656              
3657             sub parse_index_type ($)
3658             {
3659 3     3 0 6 my ($lx)= @_;
3660 3 100       15 if (looking_at($lx, 'USING', SHIFT)) {
3661 1         3 return expect($lx, ['BTREE','HASH','RTREE'], SHIFT);
3662             }
3663 2         4 return;
3664             }
3665              
3666             sub parse_table_constraint($)
3667             {
3668 3     3 0 4 my ($lx)= @_;
3669 3         8 my $r= create($lx, "TableConstraint", qw(constraint index_type column index_option reference));
3670 3         5 $r->{index_option}= [];
3671              
3672 3 50       6 if (looking_at($lx, 'CONSTRAINT', SHIFT)) {
3673             return unless
3674 3 50       6 $r->{constraint}= parse_constraint($lx);
3675             }
3676              
3677             parse_choice($lx,
3678             'PRIMARY KEY' => sub {
3679 0     0   0 lexer_shift($lx);
3680 0         0 $r->{type}= 'primary_key';
3681 0         0 $r->{index_type}= parse_index_type($lx);
3682             return unless
3683             $r->{column}= parse_list_delim($lx, \&parse_column_index)
3684 0 0 0     0 and $r->{index_option}= parse_try_list([], $lx, \&parse_index_option);
3685             },
3686             'UNIQUE' => sub {
3687 1     1   3 lexer_shift($lx);
3688 1         2 $r->{type}= 'unique';
3689 1         4 $r->{index_type}= parse_index_type($lx);
3690             return unless
3691             $r->{column}= parse_list_delim($lx, \&parse_column_index)
3692 1 50 33     4 and $r->{index_option}= parse_try_list([], $lx, \&parse_index_option);
3693             },
3694             'FULLTEXT' => sub {
3695 0     0   0 lexer_shift($lx);
3696 0         0 $r->{type}= 'fulltext';
3697 0         0 $r->{index_type}= parse_index_type($lx);
3698             return unless
3699             $r->{column}= parse_list_delim($lx, \&parse_column_index)
3700 0 0 0     0 and $r->{index_option}= parse_try_list([], $lx, \&parse_index_option);
3701             },
3702             'SPATIAL' => sub {
3703 0     0   0 lexer_shift($lx);
3704 0         0 $r->{type}= 'spatial';
3705 0         0 $r->{index_type}= parse_index_type($lx);
3706             return unless
3707             $r->{column}= parse_list_delim($lx, \&parse_column_index)
3708 0 0 0     0 and $r->{index_option}= parse_try_list([], $lx, \&parse_index_option);
3709             },
3710             'FOREIGN KEY' => sub {
3711 2     2   5 lexer_shift($lx);
3712 2         3 $r->{type}= 'foreign_key';
3713 2         3 $r->{index_type}= parse_index_type($lx);
3714             return unless
3715             $r->{column}= parse_list_delim($lx, \&parse_column_name)
3716 2 50 33     5 and $r->{reference}= parse_references($lx);
3717             },
3718             # 'CHECK' => sub {
3719             # },
3720             ($read_dialect{mysql} ?
3721             (
3722             'INDEX' => sub {
3723 0     0   0 lexer_shift($lx);
3724 0         0 $r->{type}= 'index';
3725             # FIXME: mysql allows an index name here
3726             return unless
3727 0 0       0 $r->{column}= parse_list_delim($lx, \&parse_column_index);
3728 0         0 $r->{index_option}= parse_try_list([], $lx, \&parse_index_option);
3729             }
3730             )
3731 3 50       34 : ()
3732             ),
3733             );
3734 3 50       48 return if $lx->{error};
3735              
3736 3         9 lock_hash %$r;
3737 3         68 return $r;
3738             }
3739              
3740             sub parse_table_option1($$$$)
3741             {
3742 6     6 0 14 my ($lx, $r, $name, $parse)= @_;
3743 6         10 $r->{type}= 'literal';
3744 6         11 $r->{name}= $name;
3745 6         12 lexer_shift($lx);
3746 6         13 looking_at($lx, '=', SHIFT); # optional =
3747             return unless
3748 6 50       15 $r->{value}= $parse->($lx);
3749 6         15 while (looking_at($lx, ',', SHIFT)) {} # optional ,
3750 6         12 return $r;
3751             }
3752              
3753             sub parse_on_commit_action($)
3754             {
3755 1     1 0 3 my ($lx)= @_;
3756             return keyword ($lx,
3757             expect($lx,
3758             [
3759             'PRESERVE ROWS',
3760             'DELETE ROWS',
3761             ($read_dialect{postgresql} ?
3762             (
3763 1 50       6 'DROP'
3764             )
3765             : ()
3766             )
3767             ],
3768             SHIFT
3769             )
3770             );
3771             }
3772              
3773             sub parse_table_option($)
3774             {
3775 12     12 0 19 my ($lx)= @_;
3776 12         22 my $r= create($lx, 'TableOption', qw(name value token));
3777              
3778             parse_choice($lx,
3779             -default => sub {
3780 4     4   9 $r= undef;
3781             },
3782              
3783             ($read_dialect{mysql} ?
3784             (
3785             'ENGINE' => sub {
3786 2     2   10 return parse_table_option1($lx, $r, 'ENGINE', \&parse_engine);
3787             },
3788            
3789             'CHARACTER SET' => 'DEFAULT CHARACTER SET',
3790             'DEFAULT CHARACTER SET' => sub {
3791 2     2   4 return parse_table_option1($lx, $r, 'DEFAULT CHARACTER SET', \&parse_charset);
3792             },
3793              
3794             'COLLATE' => 'DEFAULT COLLATE',
3795             'DEFAULT COLLATE' => sub {
3796 0     0   0 return parse_table_option1($lx, $r, 'DEFAULT COLLATE', \&parse_collate);
3797             },
3798              
3799             'AUTO_INCREMENT' => sub {
3800 0     0   0 return parse_table_option1($lx, $r, 'AUTO_INCREMENT', \&parse_expr);
3801             },
3802              
3803             'COMMENT' => sub {
3804 1     1   3 return parse_table_option1($lx, $r, 'COMMENT', \&parse_expr);
3805             },
3806             )
3807             : ()
3808             ),
3809              
3810             'ON COMMIT' => sub {
3811 1     1   3 return parse_table_option1($lx, $r, 'ON COMMIT', \&parse_on_commit_action);
3812             },
3813              
3814             'interpolTableOption' => 'interpol',
3815             'interpol' => sub {
3816 2     2   5 $r->{type}= 'interpol';
3817 2         3 $r->{token}= $lx->{token};
3818 2         6 lexer_shift($lx);
3819 2         5 while (looking_at($lx, ',', SHIFT)) {} # optional ,
3820 2         5 return $r;
3821             },
3822 12 50       122 );
3823 12 100       157 return unless $r;
3824 8 50       17 return if $lx->{error};
3825 8         22 lock_hash %$r;
3826 8         152 return $r;
3827             }
3828              
3829             sub parse_column_def($)
3830             {
3831 6     6 0 10 my ($lx)= @_;
3832 6         13 my $r= create($lx, 'ColumnDef', qw(name column_spec));
3833             return unless
3834             $r->{name}= parse_column_name($lx)
3835 6 50 33     24 and $r->{column_spec}= parse_column_spec($lx);
3836 6         22 lock_hash %$r;
3837 6         143 return $r;
3838             }
3839              
3840             sub parse_column_def_or_option($)
3841             {
3842 6     6 0 9 my ($lx)= @_;
3843             return parse_choice($lx,
3844             'interpol' => 'ident',
3845             'ident' => sub {
3846 4     4   9 return parse_column_def($lx);
3847             },
3848             -default => sub {
3849 2     2   5 return parse_table_constraint($lx);
3850             },
3851 6         29 );
3852             }
3853              
3854             sub parse_create_table($)
3855             {
3856 2     2 0 5 my ($lx)= @_;
3857             return unless
3858 2 50       7 expect($lx, \@CREATE_TABLE_INITIAL);
3859              
3860 2         10 my $r= create($lx, ['Stmt','CreateTable'],
3861             qw(subtype if_not_exists table column_def tabconstr tableopt select));
3862 2         5 $r->{subtype}= lexer_shift($lx);
3863              
3864 2 100 66     12 if ($read_dialect{mysql} &&
3865             looking_at($lx, 'IF NOT EXISTS', SHIFT))
3866             {
3867 1         2 $r->{if_not_exists}= 1;
3868             }
3869              
3870             return unless
3871 2 50       7 $r->{table}= parse_table($lx);
3872              
3873 2         5 $r->{column_def}= [];
3874 2         4 $r->{tabconstr}= [];
3875 2 50       6 if (looking_at($lx, '(')) {
3876             return unless
3877 2 50       9 my $spec= parse_list_delim($lx, \&parse_column_def_or_option);
3878              
3879 2         6 $r->{column_def}= [ grep { $_->{kind} eq 'ColumnDef' } @$spec ];
  6         15  
3880 2         5 $r->{tabconstr}= [ grep { $_->{kind} ne 'ColumnDef' } @$spec ];
  6         12  
3881             }
3882              
3883             return unless
3884 2 50       6 $r->{tableopt}= parse_try_list([], $lx, \&parse_table_option);
3885              
3886 2 100 66     6 if (looking_at($lx, 'AS', SHIFT) ||
3887             looking_at($lx, \@SELECT_INITIAL))
3888             {
3889             return unless
3890 1 50       5 $r->{select}= parse_select($lx);
3891             }
3892              
3893 2 0 33     4 unless (scalar(@{ $r->{column_def} }) || $r->{select}) {
  2         8  
3894 0         0 $lx->{error}= 'Either query or at least one column expected';
3895 0         0 return;
3896             }
3897              
3898 2         7 lock_hash %$r;
3899 2         90 return $r;
3900             }
3901              
3902             sub parse_drop_table($)
3903             {
3904 1     1 0 2 my ($lx)= @_;
3905             return unless
3906 1 50       4 expect($lx, \@DROP_TABLE_INITIAL);
3907              
3908 1         5 my $r= create($lx, ['Stmt','DropTable'],
3909             qw(subtype if_exists table cascade));
3910 1         3 $r->{subtype}= lexer_shift($lx);
3911              
3912 1 50 33     7 if ($read_dialect{mysql} &&
3913             looking_at($lx, 'IF EXISTS', SHIFT))
3914             {
3915 1         2 $r->{if_exists}= 1;
3916             }
3917              
3918             return unless
3919 1 50       5 $r->{table}= parse_list([], $lx, \&parse_table, ',');
3920              
3921 1         3 $r->{cascade}= looking_at($lx, ['RESTRICT','CASCADE'], SHIFT);
3922              
3923 1         5 lock_hash %$r;
3924 1         29 return $r;
3925             }
3926              
3927             sub parse_column_pos_perhaps($)
3928             {
3929 3     3 0 3 my ($lx)= @_;
3930             return parse_choice($lx,
3931             -default => sub {
3932 1     1   8 return;
3933             },
3934             'FIRST' => sub {
3935 1     1   3 return lexer_shift($lx);
3936             },
3937             'AFTER' => sub {
3938 1     1   4 lexer_shift($lx);
3939 1         2 return ('AFTER', parse_column_name($lx));
3940             },
3941 3         16 );
3942             }
3943              
3944             sub parse_alter_table($)
3945             {
3946 20     20 0 22 my ($lx)= @_;
3947             return unless
3948 20 50       38 expect($lx, \@ALTER_TABLE_INITIAL);
3949              
3950 20         43 my $r= create($lx, ['Stmt','AlterTable'],
3951             qw(subtype functor subfunctor arg online ignore table only));
3952 20         40 $r->{subtype}= lexer_shift($lx);
3953 20         34 $r->{arg}= [];
3954              
3955             return unless
3956 20 50       33 $r->{table}= parse_table($lx);
3957              
3958 20         29 $r->{only}= looking_at($lx, 'ONLY', SHIFT);
3959              
3960             parse_choice($lx,
3961             'DROP CONSTRAINT' => sub {
3962 1     1   3 $r->{functor}= lexer_shift($lx);
3963             return unless
3964 1 50       3 my $constraint= parse_constraint($lx);
3965 1         1 push @{ $r->{arg} }, $constraint, looking_at($lx, ['RESTRICT','CASCADE'], SHIFT);
  1         4  
3966             },
3967              
3968             'DROP COLUMN' => sub {
3969 3     3   7 $r->{functor}= lexer_shift($lx);
3970             return unless
3971 3 50       7 my $column= parse_column_name($lx);
3972 3         4 push @{ $r->{arg} }, $column, looking_at($lx, ['RESTRICT','CASCADE'], SHIFT);
  3         8  
3973             },
3974              
3975             'RENAME COLUMN' => sub {
3976 1     1   2 $r->{functor}= lexer_shift($lx);
3977              
3978             return unless
3979 1 50 33     4 my $column= parse_column_name($lx)
      33        
3980             and expect($lx, 'TO', SHIFT)
3981             and my $column2= parse_column_name($lx);
3982              
3983 1         3 push @{ $r->{arg} }, $column, 'TO', $column2;
  1         4  
3984             },
3985              
3986             'DROP PRIMARY KEY' => sub {
3987 1     1   3 $r->{functor}= lexer_shift($lx);
3988             },
3989              
3990             'ALTER COLUMN' => sub {
3991 6     6   9 $r->{functor}= lexer_shift($lx);
3992 6         8 push @{ $r->{arg} }, parse_column_name($lx);
  6         11  
3993 6 50       13 return if $lx->{error};
3994              
3995             parse_choice($lx,
3996             'DROP DEFAULT' => 'SET NOT NULL',
3997             'DROP NOT NULL' => 'SET NOT NULL',
3998             'SET NOT NULL' => sub {
3999 3         4 push @{ $r->{arg} }, lexer_shift($lx);
  3         6  
4000             },
4001              
4002             'SET DEFAULT' => sub {
4003 1         2 push @{ $r->{arg} }, lexer_shift($lx);
  1         4  
4004 1         1 push @{ $r->{arg} }, parse_expr($lx);
  1         5  
4005             },
4006              
4007             ($read_dialect{postgresql} ?
4008             (
4009             'TYPE' => sub {
4010 2         3 push @{ $r->{arg} }, lexer_shift($lx);
  2         6  
4011 2         4 push @{ $r->{arg} }, parse_type($lx);
  2         5  
4012 2 50       5 return if $lx->{error};
4013 2 100       4 if (my $x= looking_at($lx, 'USING', SHIFT)) {
4014 1         2 push @{ $r->{arg} }, $x, parse_expr($lx);
  1         3  
4015             }
4016             }
4017             )
4018 6 50       49 : ()
4019             ),
4020             );
4021             },
4022              
4023             'RENAME TO' => sub {
4024 1     1   4 $r->{functor}= lexer_shift($lx);
4025 1         2 push @{ $r->{arg} }, parse_table($lx);
  1         3  
4026             },
4027              
4028             'ADD COLUMN' => sub {
4029 2     2   3 $r->{functor}= lexer_shift($lx);
4030 2 100       4 if (looking_at($lx, '(', SHIFT)) {
4031 1         2 push @{ $r->{arg} }, parse_list([], $lx, \&parse_column_def, ',');
  1         18  
4032 1 50       3 return if $lx->{error};
4033 1         2 expect($lx, ')', SHIFT);
4034             }
4035             else {
4036             return unless
4037 1 50 33     2 my $col1= parse_column_name($lx)
4038             and my $spec= parse_column_spec($lx);
4039 1         2 push @{ $r->{arg} }, $col1, $spec, parse_column_pos_perhaps($lx);
  1         4  
4040             }
4041             },
4042              
4043             'ADD' => sub {
4044 1     1   2 $r->{functor}= lexer_shift($lx);
4045 1         2 push @{ $r->{arg} }, parse_table_constraint($lx);
  1         4  
4046             },
4047              
4048             ($read_dialect{mysql} ?
4049             (
4050             'MODIFY COLUMN' => sub {
4051 1     1   3 $r->{functor}= lexer_shift($lx);
4052             return unless
4053 1 50 33     3 my $col1= parse_column_name($lx)
4054             and my $spec= parse_column_spec($lx);
4055 1         2 push @{ $r->{arg} }, $col1, $spec, parse_column_pos_perhaps($lx);
  1         3  
4056             },
4057             'CHANGE COLUMN' => sub {
4058 1     1   3 $r->{functor}= lexer_shift($lx);
4059             return unless
4060 1 50 33     3 my $col1= parse_column_name($lx)
      33        
4061             and my $col2= parse_column_name($lx)
4062             and my $spec= parse_column_spec($lx);
4063 1         2 push @{ $r->{arg} }, $col1, $col2, $spec, parse_column_pos_perhaps($lx);
  1         3  
4064             },
4065             'DROP FOREIGN KEY' => sub { # standard SQL: DROP CONSTRAINT
4066 1     1   2 $r->{functor}= lexer_shift($lx);
4067             return unless
4068 1 50       3 my $constraint= parse_constraint($lx);
4069 1         2 push @{ $r->{arg} }, $constraint;
  1         3  
4070             },
4071             'DROP INDEX' => sub {
4072 1     1   3 $r->{functor}= lexer_shift($lx);
4073             return unless
4074 1 50       5 my $index= parse_index($lx);
4075 1         2 push @{ $r->{arg} }, $index;
  1         12  
4076             },
4077             )
4078 20 50       272 : ()
4079             ),
4080             );
4081 20 50       488 return if $lx->{error};
4082              
4083 20         55 lock_hash %$r;
4084 20         703 return $r;
4085             }
4086              
4087             sub parse_stmt_interpol($)
4088             {
4089 3     3 0 6 my ($lx)= @_;
4090              
4091             # Some interpols will never be good statements, so issue an error as early
4092             # as possible (i.e., at compile time instead of at runtime):
4093 3 50 33     20 if ($lx->{token}{type} eq 'num' ||
4094             $lx->{token}{type} eq 'string')
4095             {
4096 0         0 $lx->{error}= "Expected 'Stmt', but found $lx->{token}{type}";
4097 0         0 return;
4098             }
4099              
4100 3 50       8 if (! $lx->{token}{type}) {
4101 0         0 $lx->{error}= "Expected 'Stmt', but found $lx->{token}{kind}";
4102 0         0 return;
4103             }
4104              
4105 3 50       8 if ($lx->{token}{perltype} eq 'hash') {
4106 0         0 $lx->{error}= "Expected scalar or array, but found $lx->{token}{perltype}.";
4107 0         0 return;
4108             }
4109              
4110             # But some may be:
4111 3         9 my $r= create ($lx, ['Stmt','Interpol'], qw(token));
4112 3         8 $r->{token}= $lx->{token};
4113 3         10 lexer_shift($lx);
4114              
4115 3         10 lock_hash %$r;
4116 3         88 return $r;
4117             }
4118              
4119             sub parse_select_stmt($)
4120             {
4121 8     8 0 14 my ($lx)= @_;
4122             return parse_choice($lx,
4123 8     8   23 'SELECT' => sub { parse_select ($lx) },
4124              
4125             'interpolStmt' => 'interpol',
4126 0     0   0 'interpol' => sub { parse_stmt_interpol ($lx) },
4127 8         38 );
4128             }
4129              
4130             sub parse_stmt($)
4131             {
4132 228     228 0 341 my ($lx)= @_;
4133             return parse_choice($lx,
4134 176     176   293 'SELECT' => sub { parse_select ($lx) },
4135 13     13   33 'INSERT' => sub { parse_insert ($lx) },
4136 9     9   23 'UPDATE' => sub { parse_update ($lx) },
4137 4     4   11 'DELETE' => sub { parse_delete ($lx) },
4138              
4139 1368         1872 (map { $_ => 'CREATE TABLE' } @CREATE_TABLE_INITIAL),
4140 2     2   6 'CREATE TABLE' => sub { parse_create_table($lx) },
4141              
4142 456         839 (map { $_ => 'DROP TABLE' } @DROP_TABLE_INITIAL),
4143 1     1   4 'DROP TABLE' => sub { parse_drop_table($lx) },
4144              
4145 1368         2069 (map { $_ => 'ALTER TABLE' } @ALTER_TABLE_INITIAL),
4146 20     20   36 'ALTER TABLE' => sub { parse_alter_table($lx) },
4147              
4148             'interpolStmt' => 'interpol',
4149 3     3   9 'interpol' => sub { parse_stmt_interpol ($lx) },
4150 228         1191 );
4151             }
4152              
4153             ######################################################################
4154             # Perl generation:
4155              
4156              
4157             ## First: creating a list of strings.
4158             #
4159             # The str_ family implements a simple concatenator for strings. The goal
4160             # is to generate a list of literal strings and Perl code generating strings,
4161             # separated by commas. For appending such things to the list, there is
4162             # str_append_str() and str_append_perl(), resp. E.g.:
4163             #
4164             # my $s= str_new();
4165             # str_append_str ($s, "a");
4166             # str_append_perl ($s, "b");
4167             #
4168             # This would result in the following string:
4169             #
4170             # 'a',b
4171             #
4172             # Appending the comma separator is done automatically.
4173             #
4174             # Further, we need to keep track of the line number. So there is a function
4175             # str_target_line() for setting the target line number for the next string
4176             # or raw perl code that is appended. Appending the necessary newline
4177             # characters is done automatically by the str_ functions.
4178             #
4179             # Finally, we need to generate substrings by joining them. This is done
4180             # with the str_append_join() and str_append_end() functions. E.g.
4181             #
4182             # my $s= str_new();
4183             # str_append_str ($s, 'a');
4184             # str_append_join ($s, sep => ':');
4185             # str_append_perl ($s, 'b');
4186             # str_target_line ($s, 2);
4187             # str_append_str ($s, 'c');
4188             # str_append_end ($s);
4189             # str_append_perl ($s, 'd');
4190             #
4191             # This results in the following string in $s:
4192             #
4193             # 'a',join(':',b,
4194             # 'c'),d
4195             #
4196             # Another possible sub-list structure is a map, which can be added with
4197             # str_append_map() ... str_append_en() functions. E.g.:
4198             #
4199             # str_append_str ($s, 'a');
4200             # str_append_map ($s, '$_." DESC"');
4201             # str_append_perl ($s, 'b');
4202             # str_append_str ($s, 'c');
4203             # str_append_end ($s);
4204             # str_append_perl ($s, 'd');
4205             #
4206             # This results in:
4207             #
4208             # 'a',(map{$_." DESC"} b,'c'),d
4209             #
4210             # A str_append_min1() ... str_append_end() block checks that there
4211             # is at least one result in the enclosed list. This, together with
4212             # _max1_if_scalar, are slightly inefficient and should later be eliminated
4213             # if possible.
4214             #
4215             # str_get_string() returns the current string as composed so far. If the
4216             # string is empty, an empty list () is returned instead, because the
4217             # empty string is not a valid syntactic empty list in Perl, so it causes
4218             # problems, e.g. after map:
4219             #
4220             # (map {...} -->HERE<--)
4221             #
4222             # If we insert an empty string -->HERE<--, then we get a syntax error.
4223             #
4224             # The implementation of the str_ family is very straightforward: we have a
4225             # current state that is updated and a string that is appended to accordingly.
4226             sub str_new($)
4227             {
4228 278     278 0 364 my ($line_start)= @_;
4229 278         336 my $text= [];
4230 278         842 my $s= {
4231             buff => '',
4232             need_comma => 0,
4233             line_is => 1,
4234             line_target => 1,
4235             line_start => $line_start,
4236             end_str => [], # final str to push, if defined
4237             };
4238 278         582 lock_keys %$s; # poor-man's bless()
4239 278         4116 return $s;
4240             }
4241              
4242             sub str_append_raw($$)
4243             {
4244 11884     11884 0 13996 my ($s, $text)= @_;
4245 11884         13702 $s->{buff}.= $text;
4246 11884         15379 $s->{line_is}+= ($text =~ tr/\n//);
4247             }
4248              
4249             sub str_sync_line($)
4250             {
4251 5929     5929 0 6652 my ($s)= @_;
4252 5929         9202 while ($s->{line_is} < $s->{line_target}) {
4253 315         432 str_append_raw ($s, "\n");
4254             }
4255             }
4256             sub str_target_line($$)
4257             {
4258 2885     2885 0 3749 my ($s, $n)= @_;
4259 2885 50       3950 my_confess "undefined line number" unless defined $n;
4260 2885         3466 $s->{line_target}= $n;
4261             }
4262              
4263             sub str_append_comma($)
4264             {
4265 7553     7553 0 8085 my ($s)= @_;
4266 7553 100       10715 if ($s->{need_comma}) {
4267 3877         5975 str_append_raw ($s, COMMA_STR);
4268 3877         4582 $s->{need_comma}= 0;
4269             }
4270             }
4271              
4272             sub str_append_perl($$)
4273             {
4274 4435     4435 0 5687 my ($s, $perl)= @_;
4275 4435 50       6441 if ($perl ne '') {
4276 4435         6503 str_append_comma($s);
4277 4435         6351 str_sync_line ($s);
4278 4435         6678 str_append_raw ($s, $perl);
4279 4435         36657 $s->{need_comma}= 1;
4280             }
4281             }
4282              
4283             sub str_append_str($$)
4284             {
4285 2690     2690 0 3213 my ($s, $contents)= @_;
4286 2690         3404 str_append_perl ($s, quote_perl($contents));
4287             }
4288              
4289             sub str_append_join($%)
4290             {
4291 783     783 0 1963 my ($s, %opt)= @_;
4292 783   100     1993 $opt{prefix}||= '';
4293 783   100     2187 $opt{suffix}||= '';
4294 783   100     1985 $opt{sep}||= '';
4295              
4296 783         1276 str_append_comma($s);
4297 783         1162 str_sync_line ($s);
4298 783 100 66     2904 if ($opt{joinfunc}) {
    100 66        
    100 66        
      66        
      66        
4299             # special case: ignore all other settings
4300 94         244 str_append_raw ($s, "$opt{joinfunc}(");
4301 94         128 $s->{need_comma}= 0;
4302 94         119 push @{ $s->{end_str} }, undef;
  94         222  
4303             }
4304             elsif ($opt{prefix} eq '' &&
4305             $opt{suffix} eq '' &&
4306             (
4307             $opt{never_empty} ||
4308             (defined $opt{result0} && $opt{result0} eq '')
4309             ))
4310             {
4311             # simple case 1
4312 262         443 str_append_raw ($s, 'join(');
4313 262         541 str_append_str ($s, $opt{sep});
4314 262         355 $s->{need_comma}= 1;
4315 262         307 push @{ $s->{end_str} }, undef;
  262         518  
4316             }
4317             elsif ($opt{sep} eq '' &&
4318             (
4319             $opt{never_empty} ||
4320             (defined $opt{result0} && $opt{result0} eq $opt{prefix}.$opt{suffix})
4321             ))
4322             {
4323             # simple case 2
4324 21         34 str_append_raw ($s, 'join(');
4325 21         36 str_append_str ($s, '');
4326              
4327 21 50       40 if($opt{prefix} ne '') {
4328 21         41 str_append_str ($s, $opt{prefix});
4329             }
4330              
4331 21   100     26 push @{ $s->{end_str} }, $opt{suffix} || undef;
  21         71  
4332             }
4333             else {
4334             # complex case:
4335 406         835 str_append_raw ($s, __PACKAGE__.'::joinlist(');
4336 406         897 str_append_perl ($s, $s->{line_target} + $s->{line_start});
4337             # Unfortunately, Perl's caller() is often imprecise for the
4338             # generated code, and I couldn't find a cause for that to avoid
4339             # that. So the original line number is passed long for
4340             # nicer error messages if necessary.
4341 406         579 str_append_comma($s);
4342 406         741 str_append_str ($s, $opt{result0});
4343 406         668 str_append_comma($s);
4344 406         707 str_append_str ($s, $opt{prefix});
4345 406         724 str_append_comma($s);
4346 406         714 str_append_str ($s, $opt{sep});
4347 406         699 str_append_comma($s);
4348 406         705 str_append_str ($s, $opt{suffix});
4349 406         468 $s->{need_comma}= 1;
4350 406         431 push @{ $s->{end_str} }, undef;
  406         816  
4351             }
4352             }
4353              
4354             sub str_append_map($$)
4355             {
4356 178     178 0 338 my ($s,$code)= @_;
4357 178         399 str_append_comma($s);
4358 178         314 str_sync_line ($s);
4359 178         431 str_append_raw ($s, "(map{ $code } ");
4360 178         269 $s->{need_comma}= 0;
4361 178         194 push @{ $s->{end_str} }, undef;
  178         303  
4362             }
4363              
4364             sub str_append_funcall_begin($$$)
4365             {
4366 532     532 0 686 my ($s, $func, $in_list)= @_;
4367 532         908 str_append_comma($s);
4368 532         878 str_sync_line ($s);
4369 532 100       724 if ($in_list) {
4370 280         564 str_append_raw ($s, "(map { $func(");
4371             }
4372             else {
4373 252         470 str_append_raw ($s, "$func(");
4374             }
4375 532         707 $s->{need_comma}= 0;
4376 532         552 push @{ $s->{end_str} }, undef;
  532         1020  
4377             }
4378              
4379             sub str_append_funcall_end($$)
4380             {
4381 532     532 0 708 my ($s, $in_list)= @_;
4382 532 100       812 if ($in_list) {
4383 280         458 str_append_perl ($s, '$_');
4384 280         459 str_append_raw ($s, ') }');
4385 280         415 $s->{need_comma}= 0;
4386             }
4387             }
4388              
4389             sub str_append_funcall($$$)
4390             {
4391 454     454 0 706 my ($s, $code, $in_list)= @_;
4392 454         840 str_append_funcall_begin ($s, $code, $in_list);
4393 454         641 str_append_funcall_end ($s, $in_list);
4394             }
4395              
4396             sub str_append_end($)
4397             # Terminator for:
4398             # str_append_map
4399             # str_append_funcall
4400             # str_append_join
4401             {
4402 1483     1483 0 1942 my ($s)= @_;
4403 1483         1436 my $end_str= pop @{ $s->{end_str} };
  1483         2231  
4404 1483 100       2370 if (defined $end_str) {
4405 18         33 str_append_str($s, $end_str);
4406             }
4407 1483         2365 str_append_raw ($s, ')');
4408 1483         35465 $s->{need_comma}= 1;
4409             }
4410              
4411             sub str_get_string($)
4412             {
4413 275     275 0 397 my ($s)= @_;
4414 275 50       554 return '()' if $s->{buff} eq '';
4415 275         580 return $s->{buff};
4416             }
4417              
4418             # Now start appending more complex things:
4419              
4420             sub str_append_thing($$$$);
4421              
4422             sub str_append_list($$$;%)
4423             # If you know the list is non-empty, please specify never_empty => 1
4424             # so str_append_join() can optimise.
4425             {
4426 365     365 0 784 my ($str, $list, $parens, %opt)= @_;
4427 365         969 local $SIG{__DIE__}= \&my_confess;
4428              
4429             # set line to first element (if any):
4430 365 50       688 if (scalar(@$list)) {
4431 365         668 str_target_line ($str, $list->[0]{line});
4432             }
4433              
4434             # joining, delimiters, result if empty:
4435             str_append_join($str,
4436             sep => defined $opt{sep} ? $opt{sep} : COMMA_STR, # waiting for Perl 5.10: //
4437             prefix => $opt{prefix},
4438             suffix => $opt{suffix},
4439             result0 => $opt{result0},
4440 365 100       1338 );
4441              
4442             # map?
4443 365 100       851 if (my $x= $opt{map}) {
4444 1         3 str_append_comma ($str);
4445 1         2 str_sync_line ($str);
4446 1         3 str_append_raw ($str, "map{$x} ");
4447 1         1 $str->{need_comma}= 0;
4448             };
4449              
4450             # the list:
4451 365         524 for my $l (@$list) {
4452 439         749 str_append_thing ($str, $l, IN_LIST, $parens);
4453             }
4454              
4455             # end:
4456 363         805 str_append_end($str);
4457             }
4458              
4459             sub interpol_set_context ($$);
4460              
4461             sub perl_val($$$)
4462             {
4463 1068     1068 0 1497 my ($token, $ctxt, $allow)= @_;
4464              
4465             my_confess "Expected ".(english_or \"e_perl, $allow).", but found '$token->{kind}'"
4466             if $allow &&
4467 1068 50 66     2164 scalar(grep { $token->{kind} eq $_ } flatten($allow)) == 0;
  96         211  
4468              
4469             return switch($token->{kind},
4470 535     535   867 'ident' => sub { quote_perl($token->{value}) },
4471 12     12   49 '*' => sub { __PACKAGE__.'::ASTERISK' },
4472 2     2   8 '?' => sub { __PACKAGE__.'::QUESTION' },
4473 2     2   7 'NULL' => sub { __PACKAGE__.'::NULL' },
4474 1     1   4 'TRUE' => sub { __PACKAGE__.'::TRUE' },
4475 0     0   0 'FALSE' => sub { __PACKAGE__.'::FALSE' },
4476 0     0   0 'UNKNOWN' => sub { __PACKAGE__.'::UNKNOWN' },
4477 1     1   4 'DEFAULT' => sub { __PACKAGE__.'::DEFAULT' },
4478             -default => sub {
4479 515 50   515   1581 if ($token->{kind} =~ /^interpol/) {
4480 515         975 return interpol_set_context ($token->{value}, $ctxt);
4481             }
4482             else {
4483 0         0 my_confess "No idea how to print thing in Perl: ".token_describe($token);
4484             }
4485             }
4486 1068         8125 );
4487             }
4488              
4489             sub perl_val_list($$$)
4490             {
4491 46     46 0 87 my ($token, $ctxt, $allow)= @_;
4492 46         119 my $s= perl_val($token, $ctxt, $allow);
4493              
4494 46 100       303 if ($token->{perltype} eq 'hash') {
4495 9         35 return "sort keys $s";
4496             }
4497             else {
4498 37         115 return $s;
4499             }
4500             }
4501              
4502             sub token_pos($)
4503             {
4504 3     3 0 8 my ($token)= @_;
4505 3         32 return "$token->{lx}{file}:".($token->{line} + $token->{lx}{line_start});
4506             }
4507              
4508             sub lx_pos($)
4509             {
4510 2     2 0 4 my ($lx)= @_;
4511 2         21 return "$lx->{file}:".($lx->{line} + $lx->{line_start});
4512             }
4513              
4514             sub croak_unless_scalar($)
4515             {
4516 870     870 0 1293 my ($token)= @_;
4517             die token_pos($token).": ".
4518             "Error: Scalar context, embedded Perl must not be syntactic array or hash.\n"
4519 870 100 66     2687 if $token->{perltype} eq 'array' || $token->{perltype} eq 'hash';
4520             }
4521              
4522             sub str_append_typed($$$$$%)
4523             {
4524 476     476 0 963 my ($str, $callback, $ctxt, $thing, $in_list, %opt)= @_;
4525 476         849 my $q_val= perl_val ($thing->{token}, $ctxt, undef);
4526              
4527 476 100 100     3291 if (!$in_list ||
    100          
4528             $thing->{token}{perltype} eq 'scalar')
4529             {
4530 360         706 croak_unless_scalar ($thing->{token});
4531 358         902 str_append_perl ($str, __PACKAGE__."::${callback}($q_val)");
4532             }
4533             elsif ($thing->{token}{perltype} eq 'hash') {
4534 19 100       49 if ($opt{hash}) {
    50          
4535 16         61 str_append_perl ($str, __PACKAGE__."::${callback}_hash($q_val)");
4536             }
4537             elsif ($opt{hashkeys}) {
4538 3         14 str_append_map ($str, __PACKAGE__."::${callback}(\$_)");
4539 3         10 str_append_perl ($str, "sort keys $q_val");
4540 3         7 str_append_end ($str);
4541             }
4542             else {
4543 0         0 die token_pos($thing->{token}).": Error: Hashes are not allowed here.\n";
4544             }
4545             }
4546             else {
4547 97         352 str_append_map ($str, __PACKAGE__."::${callback}(\$_)");
4548 97         193 str_append_perl ($str, $q_val);
4549 97         185 str_append_end ($str);
4550             }
4551             }
4552              
4553             sub is_multicol($);
4554             sub is_multicol($)
4555             {
4556 526     526 0 686 my ($thing) = @_;
4557             return switch ($thing->{kind},
4558             'ExprAs' => sub{
4559 170     170   283 return is_multicol($thing->{expr});
4560             },
4561             'Expr' => sub {
4562 170 100   170   355 if ($thing->{type} eq 'column') {
4563 93         161 return is_multicol($thing->{arg});
4564             }
4565 77         567 return 0;
4566             },
4567             'Column' => sub {
4568 93     93   173 return is_multicol($thing->{ident_chain}[-1]);
4569             },
4570             '*' => sub {
4571 7     7   115 return 1;
4572             },
4573             'interpol' => sub {
4574 21     21   268 return $thing->{perltype} ne 'scalar';
4575             },
4576             -default => sub {
4577 65     65   861 return 0;
4578             },
4579 526         3023 );
4580             }
4581              
4582             # Contexts for the different sql{...} interpolation blocks:
4583             my %ident_context= (
4584             'Column' => {
4585             1 => [ 'Column' ],
4586             2 => [ 'Table', 'none' ],
4587             },
4588             );
4589              
4590             sub str_append_ident_chain($$$@)
4591             {
4592 519     519 0 854 my ($str, $in_list, $family, @token)= @_;
4593 519         811 my $func= lc($family);
4594              
4595 519   66     1669 my $ctxt= $ident_context{$family}{scalar @token} ||
4596             (scalar(@token) == 1 ?
4597             [ $family ]
4598             : [ map 'none', 1..scalar(@token) ]
4599             );
4600              
4601 519         764 my $n= scalar(@token);
4602 519         991 my @non_scalar_i= grep { $token[$_]{perltype} ne 'scalar' } 0..$n-1;
  561         1393  
4603              
4604 519 100 100     1413 if (!$in_list ||
    100          
4605             scalar(@non_scalar_i) == 0)
4606             {
4607 477         717 for my $a (@token) { croak_unless_scalar ($a); }
  510         756  
4608             my $q_vals= join(",",
4609             map
4610 476         745 { perl_val($token[$_], $ctxt->[$_], undef) }
  509         884  
4611             0..$n-1
4612             );
4613 476         1433 str_append_perl ($str, __PACKAGE__."::${func}${n}($q_vals)");
4614             }
4615             elsif (scalar(@non_scalar_i) == 1) {
4616             str_append_map ($str,
4617             __PACKAGE__."::${func}${n}(".
4618             join(",",
4619             map {
4620 38 100       131 ($token[$_]{perltype} eq 'scalar' ?
  43         202  
4621             perl_val($token[$_], $ctxt->[$_], undef)
4622             : '$_'
4623             )
4624             }
4625             0..$n-1
4626             ).
4627             ")"
4628             );
4629 38         114 my ($i)= @non_scalar_i;
4630 38         91 str_append_perl ($str, perl_val_list($token[$i], $ctxt->[$i], undef));
4631 38         69 str_append_end ($str);
4632             }
4633             else {
4634 4 50       13 my $f_ident= "${func}${n}_".join('', map{ $_->{perltype} eq 'scalar' ? 1 : 'n' } @token);
  8         22  
4635             str_append_perl ($str,
4636             __PACKAGE__."::$f_ident(".
4637             join(",",
4638             map {
4639 4 50       15 ($token[$_]{perltype} eq 'scalar' ?
  8         23  
4640             perl_val($token[$_], $ctxt->[$_], undef)
4641             : '['.perl_val_list($token[$_], $ctxt->[$_], undef).']'
4642             )
4643             }
4644             0..$n-1
4645             ).
4646             ")"
4647             );
4648             }
4649             }
4650              
4651             sub str_append_limit ($$$)
4652             {
4653 111     111 0 189 my ($str, $limit_cnt, $limit_offset)= @_;
4654              
4655 111 100 66     401 if (defined $limit_cnt || defined $limit_offset) {
4656 5         10 my $limit_cnt_str= 'undef';
4657 5 50       13 if ($limit_cnt) {
4658 5         17 $limit_cnt_str= perl_val($limit_cnt, 'Expr', ['interpol', 'interpolExpr', '?']);
4659             }
4660              
4661 5         40 my $limit_offset_str= 'undef';
4662 5 100       41 if ($limit_offset) {
4663 3         11 $limit_offset_str= perl_val($limit_offset, 'Expr', ['interpol', 'interpolExpr', '?']);
4664             }
4665              
4666 5         37 str_append_perl ($str, __PACKAGE__."::limit($limit_cnt_str, $limit_offset_str)");
4667             }
4668             }
4669              
4670             sub str_append_parens($$$)
4671             {
4672 18     18 0 31 my ($str, $thing, $in_list)= @_;
4673 18 50       51 if ($in_list) {
4674 0         0 str_append_map ($str, "\"(\$_)\"");
4675 0         0 str_append_thing ($str, $thing, $in_list, NO_PARENS);
4676 0         0 str_append_end ($str);
4677             }
4678             else {
4679 18         50 str_append_join ($str, prefix => '(', suffix => ')', never_empty => 1);
4680 18         43 str_append_thing ($str, $thing, $in_list, NO_PARENS);
4681 18         723 str_append_end ($str);
4682             }
4683             }
4684              
4685             sub str_append_table_key($$$)
4686             {
4687 3     3 0 6 my ($str, $thing, $type)= @_;
4688 3         7 str_append_join ($str, sep => ' ');
4689 3 50       8 if (my $x= $thing->{constraint}) {
4690 3         8 str_append_str ($str, 'CONSTRAINT');
4691 3         5 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4692             }
4693 3         74 str_append_str ($str, $type);
4694 3 100       8 if (my $x= $thing->{index_type}) {
4695 1         4 str_append_str ($str, "USING $x");
4696             }
4697 3         9 str_append_list ($str, $thing->{column}, NO_PARENS, prefix=>'(', suffix=>')');
4698 3         5 for my $o (@{ $thing->{index_option} }) {
  3         6  
4699 0         0 str_append_thing ($str, $o, IN_LIST, NO_PARENS);
4700             }
4701 3 100       7 if (my $x= $thing->{reference}) {
4702 2         3 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4703             }
4704 3         58 str_append_end ($str);
4705             }
4706              
4707             # str_append_thing() converts a recursive representation of the parsed SQL
4708             # structure into a Perl string that generates a list of either string
4709             # representations of the SQL structure (in good SQL syntax), or blessed
4710             # objects of the correct type.
4711             #
4712             # The result of this function is then used to wrap and bless the string
4713             # or objects appropriately according to which kind of SQL structure the
4714             # string contains (statement, expressions, column, etc.).
4715             #
4716             # In detail, str_append_thing() appends pieces of Perl code to $str, that
4717             # each represent a small piece of the SQL command.
4718             #
4719             # Each invocation of str_append_thing appends code to $str that generates
4720             # exactly the amount of objects that are represented. This might seem
4721             # obvious, but since $str is actually a comma separated list, this
4722             # requirement means that if multiple pieces are pushed for a single
4723             # thing, then a join(...) must enclose and group these. E.g.
4724             # the code that generates a SELECT statement from scratch appends
4725             # several pieces of code to $str, and to make only one string, a
4726             # join() is generated.
4727             #
4728             sub str_append_thing($$$$)
4729             {
4730 2864     2864 0 4086 my ($str, $thing, $in_list, $parens)= @_;
4731 2864         6941 local $SIG{__DIE__}= \&my_confess;
4732              
4733             # simple things to append:
4734 2864 100       4978 unless (defined $thing) {
4735 16         32 str_append_perl ($str, 'undef');
4736 16         35 return;
4737             }
4738 2848 100       4377 unless (ref $thing) {
4739 384         627 str_append_str ($str, $thing);
4740 384         791 return;
4741             }
4742 2464 100       3926 if (ref($thing) eq 'ARRAY') {
4743 1         3 str_append_list ($str, $thing, NO_PARENS, prefix => '(', suffix => ')');
4744 1         3 return;
4745             }
4746              
4747             # normal structure:
4748 2463         4749 str_target_line ($str, $thing->{line});
4749              
4750             switch($thing->{kind},
4751             'Stmt' => sub {
4752             switch($thing->{type},
4753             'Select' => sub {
4754             # find out type name depending on number of columns:
4755 183         214 my $type_name = 'SelectStmt';
4756 183 100       200 if (scalar(@{ $thing->{expr_list} }) == 1) {
  183         443  
4757 170 100       339 unless (is_multicol($thing->{expr_list}[0])) {
4758 151         223 $type_name = 'SelectStmtSingle';
4759             }
4760             }
4761              
4762             # generate:
4763 183         1032 str_append_funcall ($str, __PACKAGE__.'::'.$type_name.'->obj', $in_list);
4764 183         336 str_append_join ($str, never_empty => 1);
4765              
4766             str_append_list ($str, $thing->{expr_list}, NO_PARENS,
4767             prefix => join(' ', 'SELECT',
4768 183         282 @{ $thing->{opt_front} }
  183         660  
4769             ).' '
4770             );
4771              
4772 181 100       427 if (my $x= $thing->{from}) {
4773 99         212 str_append_list ($str, $x, NO_PARENS, prefix => ' FROM ');
4774              
4775 99 50       224 if (my $x= $thing->{join}) {
4776 99 100       184 if (@$x) {
4777 5         15 str_append_map ($str, '" $_" ');
4778 5         10 for my $xi (@$x) {
4779 6         16 str_append_thing ($str, $xi, IN_LIST, NO_PARENS);
4780             }
4781 5         15 str_append_end ($str);
4782             }
4783             }
4784 99 100       205 if (my $x= $thing->{where}) {
4785 44         100 str_target_line ($str, $x->{line});
4786 44         87 str_append_str ($str, ' WHERE ');
4787 44         87 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4788             }
4789 98 100       2093 if (my $x= $thing->{group_by}) {
4790 6         11 my $suffix= '';
4791 6 100       12 if ($thing->{group_by_with_rollup}) {
4792 1         2 $suffix= ' WITH ROLLUP';
4793             }
4794 6         15 str_append_list ($str, $x, NO_PARENS,
4795             prefix => ' GROUP BY ',
4796             suffix => $suffix,
4797             result0 => '',
4798             );
4799             }
4800 98 100       206 if (my $x= $thing->{having}) {
4801 1         4 str_target_line ($str, $x->{line});
4802 1         2 str_append_str ($str, ' HAVING ');
4803 1         3 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4804             }
4805 98 100       270 if (my $x= $thing->{order_by}) {
4806 8         17 str_append_list ($str, $x, NO_PARENS,
4807             prefix => ' ORDER BY ',
4808             result0 => ''
4809             );
4810             }
4811 98         239 str_append_limit ($str, $thing->{limit_cnt}, $thing->{limit_offset});
4812              
4813 98         128 str_append_str ($str, join('', map " $_", @{ $thing->{opt_back} }));
  98         267  
4814             }
4815              
4816 180         347 str_append_end ($str);
4817 180         236 str_append_end ($str);
4818             },
4819             'Delete' => sub {
4820 4         11 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
4821 4         10 str_append_join ($str, never_empty => 1);
4822              
4823             str_append_list ($str, $thing->{from}, NO_PARENS,
4824             prefix =>
4825             join(' ',
4826             'DELETE',
4827 4         8 @{ $thing->{opt_front} },
4828             'FROM',
4829 4         8 @{ $thing->{from_opt_front} },
  4         17  
4830             ).' '
4831             );
4832              
4833 4 100       14 if (my $x= $thing->{using}) {
4834 2         5 str_append_list ($str, $x, NO_PARENS,
4835             prefix => ' USING ',
4836             result0 => ''
4837             );
4838             }
4839              
4840 4 50       21 if (my $x= $thing->{join}) {
4841 4 100       12 if (@$x) {
4842 2         7 str_append_map ($str, '" $_" ');
4843 2         4 for my $xi (@$x) {
4844 3         8 str_append_thing ($str, $xi, IN_LIST, NO_PARENS);
4845             }
4846 2         6 str_append_end ($str);
4847             }
4848             }
4849 4 100       10 if (my $x= $thing->{where}) {
4850 3         9 str_target_line ($str, $x->{line});
4851 3         6 str_append_str ($str, ' WHERE ');
4852 3         8 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4853             }
4854 4 50       126 if (my $x= $thing->{order_by}) {
4855 0         0 str_append_list ($str, $x, NO_PARENS,
4856             prefix => ' ORDER BY ',
4857             result0 => ''
4858             );
4859             }
4860 4         21 str_append_limit ($str, $thing->{limit_cnt}, $thing->{limit_offset});
4861              
4862 4         19 str_append_end ($str);
4863 4         8 str_append_end ($str);
4864             },
4865             'Insert' => sub {
4866 13         34 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
4867 13         31 str_append_join ($str, never_empty => 1);
4868              
4869             str_append_str ($str,
4870             join(' ',
4871             'INSERT',
4872 13         18 @{ $thing->{opt_front} },
  13         51  
4873             'INTO',
4874             ).' '
4875             );
4876              
4877 13         33 str_append_thing ($str, $thing->{into}, NOT_IN_LIST, NO_PARENS);
4878              
4879 13 100       429 if (my $col= $thing->{column}) {
4880 5         16 str_append_list ($str, $col, NO_PARENS, prefix => ' (', suffix => ')');
4881             }
4882              
4883 13 100       49 if (my $val= $thing->{value}) {
    50          
    0          
    0          
4884 5         14 str_append_str ($str, ' VALUES ');
4885 5         16 str_append_list ($str, $val, NO_PARENS);
4886             }
4887             elsif (my $set= $thing->{set}) {
4888 8         24 str_append_funcall ($str, __PACKAGE__."::set2values", NOT_IN_LIST);
4889 8         14 for my $l (@$set) {
4890 16         33 str_append_thing ($str, $l, IN_LIST, NO_PARENS);
4891             }
4892 8         208 str_append_end ($str);
4893             }
4894             elsif (my $sel= $thing->{select}) {
4895 0         0 str_append_str ($str, ' ');
4896 0         0 str_append_thing ($str, $sel, NOT_IN_LIST, NO_PARENS);
4897             }
4898             elsif ($thing->{default_values}) {
4899 0         0 str_append_str ($str, ' DEFAULT VALUES');
4900             }
4901             else {
4902 0         0 die;
4903             }
4904              
4905 13 100       40 if (my $x= $thing->{duplicate_update}) {
4906 1         2 str_append_str ($str, ' ON DUPLICATE KEY UPDATE ');
4907 1         130 str_append_list ($str, $x, NO_PARENS, map => __PACKAGE__.'::assign($_)');
4908             }
4909              
4910 13         23 str_append_end ($str);
4911 13         23 str_append_end ($str);
4912             },
4913             'Update' => sub {
4914 9         20 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
4915 9         18 str_append_join ($str, never_empty => 1);
4916              
4917             str_append_list ($str, $thing->{table}, NO_PARENS,
4918             prefix => join(' ', 'UPDATE',
4919 9         15 @{ $thing->{opt_front} }
  9         31  
4920             ).' '
4921             );
4922              
4923 9 100       28 if (my $x= $thing->{from}) {
4924 1         2 str_append_list ($str, $x, NO_PARENS,
4925             prefix => ' FROM ',
4926             result0 => ''
4927             );
4928             }
4929 9 50       23 if (my $x= $thing->{join}) {
4930 9 50       17 if (@$x) {
4931 0         0 str_append_map ($str, '" $_" ');
4932 0         0 for my $xi (@$x) {
4933 0         0 str_append_thing ($str, $xi, IN_LIST, NO_PARENS);
4934             }
4935 0         0 str_append_end ($str);
4936             }
4937             }
4938 9 50       18 if (my $x= $thing->{set}) {
4939 9         15 str_append_list ($str, $x, NO_PARENS,
4940             prefix => ' SET ',
4941             result0 => '' # this is an error.
4942             );
4943             }
4944 9 50       23 if (my $x= $thing->{where}) {
4945 9         153 str_target_line ($str, $x->{line});
4946 9         17 str_append_str ($str, ' WHERE ');
4947 9         19 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4948             }
4949 9 100       363 if (my $x= $thing->{order_by}) {
4950 1         3 str_append_list ($str, $x, NO_PARENS,
4951             prefix => ' ORDER BY ',
4952             result0 => ''
4953             );
4954             }
4955 9         27 str_append_limit ($str, $thing->{limit_cnt}, $thing->{limit_offset});
4956              
4957 9         19 str_append_end ($str);
4958 9         16 str_append_end ($str);
4959             },
4960             'CreateTable' => sub {
4961 2         6 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
4962 2         5 str_append_join ($str, never_empty => 1);
4963              
4964 2         8 str_append_str ($str, "$thing->{subtype} ");
4965 2 100       5 if ($thing->{if_not_exists}) {
4966 1         3 str_append_str ($str, 'IF NOT EXISTS ');
4967             }
4968 2         7 str_append_thing ($str, $thing->{table}, NOT_IN_LIST, NO_PARENS);
4969              
4970             my @tabspec= (
4971 2         5 @{ $thing->{column_def} },
4972 2         56 @{ $thing->{tabconstr} }
  2         6  
4973             );
4974 2         7 str_append_list ($str, \@tabspec, NO_PARENS,
4975             result0 => '',
4976             prefix => ' (',
4977             suffix => ')'
4978             );
4979              
4980 2         20 str_append_list ($str, $thing->{tableopt}, NO_PARENS,
4981             result0 => '',
4982             prefix => ' ',
4983             sep => ' ',
4984             );
4985              
4986 2 100       7 if (my $x= $thing->{select}) {
4987 1         2 str_append_str ($str, ' AS ');
4988 1         2 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4989             }
4990              
4991 2         69 str_append_end ($str);
4992 2         5 str_append_end ($str);
4993             },
4994             'DropTable' => sub {
4995 1         4 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
4996 1         3 str_append_join ($str, never_empty => 1);
4997              
4998 1         3 str_append_str ($str, "$thing->{subtype} ");
4999 1 50       3 if ($thing->{if_exists}) {
5000 1         4 str_append_str ($str, 'IF EXISTS ');
5001             }
5002 1         3 str_append_list ($str, $thing->{table}, NO_PARENS);
5003              
5004 1 50       5 if (my $x= $thing->{cascade}) {
5005 1         3 str_append_str ($str, " $x");
5006             }
5007 1         2 str_append_end ($str);
5008 1         2 str_append_end ($str);
5009             },
5010             'AlterTable' => sub {
5011 20         43 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
5012 20         34 str_append_join ($str, never_empty => 1);
5013              
5014 20         69 str_append_str ($str, "$thing->{subtype} ");
5015 20 50       39 if ($thing->{only}) {
5016 0         0 str_append_str ($str, 'ONLY ');
5017             }
5018 20         40 str_append_thing ($str, $thing->{table}, NOT_IN_LIST, NO_PARENS);
5019              
5020 20         553 str_append_join ($str, sep => ' ', prefix => ' ');
5021 20         28 for my $l ($thing->{functor}, @{ $thing->{arg} }) {
  20         37  
5022 61         85 str_append_thing ($str, $l, NOT_IN_LIST, NO_PARENS);
5023             }
5024 20         40 str_append_end ($str);
5025              
5026 20         31 str_append_end ($str);
5027 20         28 str_append_end ($str);
5028             },
5029             'Interpol' => sub {
5030 3         9 str_append_typed ($str, 'stmt', 'Stmt', $thing, $in_list);
5031             },
5032 235     141   2572 );
5033             },
5034              
5035             'TableOption' => sub {
5036             switch ($thing->{type},
5037             'interpol' => sub {
5038 2         6 str_append_typed ($str, 'tableopt', 'TableOption', $thing, $in_list);
5039             },
5040             'literal' => sub {
5041 6         13 str_append_join ($str, sep => ' ');
5042 6         15 str_append_str ($str, $thing->{name});
5043 6         22 str_append_thing ($str, $thing->{value}, NOT_IN_LIST, NO_PARENS);
5044 6         175 str_append_end ($str);
5045             }
5046 8     3   30 );
5047             },
5048              
5049             'Keyword' => sub {
5050 1     0   4 str_append_str ($str, $thing->{keyword});
5051             },
5052              
5053             'Join' => sub {
5054 16 100   9   35 if ($thing->{type} eq 'interpol') {
5055 5         11 str_append_typed ($str, 'joinclause', 'Join', $thing, $in_list);
5056             }
5057             else {
5058 11         25 str_append_join ($str, result0 => '');
5059              
5060 11 100       22 if ($thing->{natural}) {
5061 3 50       14 if ($thing->{type} eq 'INNER') {
5062 3         6 str_append_str ($str, "NATURAL JOIN ");
5063             }
5064             else {
5065 0         0 str_append_str ($str, "NATURAL $thing->{type} JOIN ");
5066             }
5067             }
5068             else {
5069 8         19 str_append_str ($str, "$thing->{type} JOIN ");
5070             }
5071              
5072 11         31 str_append_list ($str, $thing->{table}, NO_PARENS);
5073              
5074 11 100       36 if (my $on= $thing->{on}) {
    100          
5075 3         7 str_append_str ($str, ' ON ');
5076 3         8 str_append_thing ($str, $on, NOT_IN_LIST, NO_PARENS);
5077             }
5078             elsif (my $using= $thing->{using}) {
5079 2         6 str_append_str ($str, ' USING (');
5080 2         5 str_append_list ($str, $using, NO_PARENS);
5081 2         3 str_append_str ($str, ')');
5082             };
5083              
5084 11         150 str_append_end ($str);
5085             }
5086             },
5087              
5088             'Table' => 'Column',
5089             'CharSet' => 'Column',
5090             'Collate' => 'Column',
5091             'Index' => 'Column',
5092             'Constraint' => 'Column',
5093             'Transliteration' => 'Column',
5094             'Transcoding' => 'Column',
5095             'Engine' => 'Column',
5096             'Column' => sub {
5097 519     355   701 str_append_ident_chain ($str, $in_list, $thing->{kind}, @{ $thing->{ident_chain} });
  519         1072  
5098             },
5099              
5100             'TableAs' => sub {
5101 128 100   102   241 if (my $x= $thing->{as}) {
5102             # Oracle does not allow AS in table aliases. But this module
5103             # does not allow leaving it out. To avoid generating what
5104             # this module cannot read back in the default case, check for
5105             # the write dialect.
5106 5 50       10 if ($write_dialect eq 'oracle') {
5107 0         0 str_append_join ($str, sep => ' ', never_empty => 1);
5108             }
5109             else {
5110 5         9 str_append_join ($str, sep => ' AS ', never_empty => 1);
5111             }
5112 5         14 str_append_thing ($str, $thing->{table}, NOT_IN_LIST, NO_PARENS);
5113 5         161 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
5114 5         124 str_append_end ($str);
5115             }
5116             else {
5117 123         210 str_append_thing ($str, $thing->{table}, $in_list, NO_PARENS);
5118             }
5119             },
5120              
5121             'ExprAs' => sub {
5122 229 100   123   384 if (my $x= $thing->{as}) {
5123 4         11 str_append_join ($str, sep => ' AS ', never_empty => 1);
5124 4         13 str_append_thing ($str, $thing->{expr}, NOT_IN_LIST, NO_PARENS);
5125 3         214 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
5126 3         79 str_append_end ($str);
5127             }
5128             else {
5129 225         417 str_append_thing ($str, $thing->{expr}, $in_list, $parens);
5130             }
5131             },
5132             'Order' => sub {
5133             switch($thing->{type},
5134             'interpol' => sub {
5135 18 100       39 if ($thing->{desc}) {
5136 5         10 str_append_typed ($str, 'desc', 'Order', $thing, $in_list, hashkeys => 1);
5137             }
5138             else {
5139 13         25 str_append_typed ($str, 'asc', 'Order', $thing, $in_list, hashkeys => 1);
5140             }
5141             },
5142             'expr' => sub {
5143 17 100       32 if ($thing->{desc}) {
5144 5         15 str_append_map ($str, __PACKAGE__.'::desc($_)');
5145 5         14 str_append_thing ($str, $thing->{expr}, $in_list, NO_PARENS);
5146 5         346 str_append_end ($str);
5147             }
5148             else {
5149 12         27 str_append_thing ($str, $thing->{expr}, $in_list, NO_PARENS);
5150             }
5151             },
5152 35     17   122 );
5153             },
5154             'TypeList' => sub {
5155             switch($thing->{type},
5156             'interpol' => sub {
5157 0         0 str_append_typed ($str, 'typelist', 'Type', $thing, $in_list);
5158             },
5159              
5160             'explicit' => sub {
5161 1         3 str_append_list ($str, $thing->{arg}, NO_PARENS, prefix => '(', suffix => ')');
5162             # may not be empty!
5163             },
5164 1     0   5 );
5165             },
5166             'Type' => sub {
5167             switch ($thing->{type},
5168             'interpol' => sub {
5169 8         13 str_append_typed ($str, 'type', 'Type', $thing, $in_list);
5170             },
5171             'base' => sub {
5172 22         43 str_append_perl ($str, __PACKAGE__.'::Type->new()');
5173             },
5174 30     15   108 );
5175             },
5176             'TypePost' => sub {
5177 56 50   23   92 return str_append_parens ($str, $thing, NOT_IN_LIST)
5178             if $parens;
5179              
5180 56         173 str_append_funcall_begin ($str, __PACKAGE__.'::type_'.$thing->{functor}, $in_list);
5181 56         65 for my $arg (@{ $thing->{arg} }) {
  56         96  
5182 68         131 str_append_thing ($str, $arg, NOT_IN_LIST, NO_PARENS);
5183             }
5184 56         123 str_append_funcall_end ($str, $in_list);
5185 56         106 str_append_thing ($str, $thing->{base}, $in_list, NO_PARENS);
5186 56         1345 str_append_end ($str);
5187             },
5188             'ColumnDef' => sub {
5189 6     1   11 str_append_join ($str, sep => ' ');
5190 6         16 str_append_thing ($str, $thing->{name}, NOT_IN_LIST, NO_PARENS);
5191 6         169 str_append_thing ($str, $thing->{column_spec}, NOT_IN_LIST, NO_PARENS);
5192 6         142 str_append_end ($str);
5193             },
5194              
5195             'ColumnSpec' => sub {
5196             switch ($thing->{type},
5197             'interpol' => sub {
5198 7         16 str_append_typed ($str, 'colspec', 'ColumnSpec', $thing, $in_list);
5199             },
5200             'base' => sub {
5201 9         25 str_append_funcall ($str, __PACKAGE__.'::ColumnSpec->new', $in_list);
5202 9         37 str_append_thing ($str, $thing->{datatype}, $in_list, NO_PARENS);
5203 9         378 str_append_end ($str);
5204             }
5205 16     4   69 );
5206             },
5207             'ColumnSpecPost' => sub {
5208 22 50   4   40 return str_append_parens ($str, $thing, NOT_IN_LIST)
5209             if $parens;
5210              
5211 22         69 str_append_funcall_begin ($str, __PACKAGE__.'::colspec_'.$thing->{functor}, $in_list);
5212 22         32 for my $arg (@{ $thing->{arg} }) {
  22         38  
5213 61         94 str_append_thing ($str, $arg, NOT_IN_LIST, NO_PARENS);
5214             }
5215 22         52 str_append_funcall_end ($str, $in_list);
5216 22         47 str_append_thing ($str, $thing->{base}, $in_list, NO_PARENS);
5217 22         520 str_append_end ($str);
5218             },
5219              
5220             'TableConstraint' => sub {
5221             switch($thing->{type},
5222             'primary_key' => sub {
5223 0         0 str_append_table_key ($str, $thing, 'PRIMARY KEY');
5224             },
5225             'unique' => sub {
5226 1         4 str_append_table_key ($str, $thing, 'UNIQUE');
5227             },
5228             'fulltext' => sub {
5229 0         0 str_append_table_key ($str, $thing, 'FULLTEXT');
5230             },
5231             'spatial' => sub {
5232 0         0 str_append_table_key ($str, $thing, 'SPATIAL');
5233             },
5234             'index' => sub {
5235 0         0 str_append_table_key ($str, $thing, 'INDEX');
5236             },
5237             'foreign_key' => sub {
5238 2         4 str_append_table_key ($str, $thing, 'FOREIGN KEY');
5239             },
5240 3     0   17 );
5241             },
5242              
5243             'IndexOption' => sub {
5244             switch($thing->{type},
5245             'using' => sub {
5246 0         0 str_append_str ($str, "USING $thing->{arg}");
5247             }
5248 0     0   0 );
5249             },
5250              
5251             'References' => sub {
5252             # table column match on_delete on_update));
5253             str_append_join ($str, sep => ' ',
5254             prefix => 'REFERENCES ',
5255             suffix =>
5256 4         10 join('', map { " $_" }
5257             ($thing->{match} ?
5258             ('MATCH', $thing->{match})
5259             : ()
5260             ),
5261             ($thing->{on_delete} ?
5262             ('ON DELETE', $thing->{on_delete})
5263             : ()
5264             ),
5265             ($thing->{on_update} ?
5266             ('ON UPDATE', $thing->{on_update})
5267 3 100   0   14 : ()
    100          
    50          
5268             ),
5269             )
5270             );
5271 3         8 str_append_thing ($str, $thing->{table}, NOT_IN_LIST, NO_PARENS);
5272 3         74 str_append_list ($str, $thing->{column}, NO_PARENS,
5273             prefix => '(', suffix => ')', result0 => '');
5274 3         5 str_append_end ($str);
5275             },
5276              
5277             'CharUnit' => sub {
5278 1     0   2 str_append_str ($str, $thing->{name});
5279             },
5280              
5281             'ExprList' => sub {
5282             switch($thing->{type},
5283             'interpol' => sub {
5284 5         10 str_append_typed ($str, 'exprlist', 'Expr', $thing, $in_list);
5285             },
5286              
5287             'explicit' => sub {
5288 6         17 str_append_list ($str, $thing->{arg}, NO_PARENS, prefix => '(', suffix => ')');
5289             # may not be empty!
5290             },
5291 11     8   40 );
5292             },
5293             'ExprEmpty' => sub {
5294             # Append an empty string. Must have an operand here, otherwise
5295             # parameters might get mixed up.
5296 18     10   30 str_append_str($str, '');
5297             },
5298             'Check' => sub {
5299 9     9   21 str_append_join ($str, joinfunc => __PACKAGE__.'::Check->obj');
5300 9         23 str_append_thing ($str, $thing->{expr}, NOT_IN_LIST, NO_PARENS);
5301 9         379 str_append_end ($str);
5302             },
5303             'Expr' => sub {
5304             switch($thing->{type},
5305             'limit' => sub {
5306 24         68 my $limit_cnt_str= perl_val($thing->{arg}, 'Expr',
5307             ['interpol', 'interpolExpr', '?']);
5308 24         219 str_append_perl ($str, __PACKAGE__."::limit_number($limit_cnt_str)");
5309             },
5310             'interpol' => sub {
5311             my $func= $thing->{maybe_check} ?
5312             'expr_or_check'
5313             : ($thing->{token}{type} eq 'num' ||
5314 375 100 100     1570 $thing->{token}{type} eq 'string' ||
    100          
5315             !$parens) ?
5316             'expr'
5317             : 'exprparen';
5318 375         654 str_append_typed ($str, $func, 'Expr', $thing, $in_list, hash => 1);
5319             },
5320             'column' => sub {
5321 320         606 str_append_thing ($str, $thing->{arg}, $in_list, NO_PARENS);
5322             },
5323             '()' => sub {
5324 11         25 str_append_thing ($str, $thing->{arg}, $in_list, PARENS);
5325             },
5326             'subquery' => sub {
5327 8         19 str_append_funcall ($str, __PACKAGE__.'::subquery', $in_list);
5328 8         20 str_append_thing ($str, $thing->{arg}, $in_list, NO_PARENS);
5329 8         446 str_append_end ($str);
5330             },
5331             'prefix1' => sub {
5332 2         4 $in_list= NOT_IN_LIST; # just to be sure
5333 2 50       5 return str_append_parens ($str, $thing, NOT_IN_LIST)
5334             if $parens;
5335 2 50       3 die 'Expected exactly 1 argument' unless scalar(@{ $thing->{arg} }) == 1;
  2         7  
5336              
5337 2         9 str_append_join ($str,
5338             prefix => "$thing->{functor}{value} ",
5339             never_empty => 1
5340             );
5341 2         7 str_append_thing ($str, $thing->{arg}[0], NOT_IN_LIST, PARENS);
5342 1         40 str_append_end ($str);
5343             },
5344             'prefixn' => sub {
5345 2         3 $parens= NO_PARENS; # just to be sure
5346 2 50       2 die 'Expected exactly 1 argument' unless scalar(@{ $thing->{arg} }) == 1;
  2         7  
5347              
5348 2 100       5 if ($in_list) {
5349 1         5 str_append_map ($str, "'$thing->{functor}{value} '.(\$_)");
5350             }
5351             else {
5352 1         4 str_append_join ($str,
5353             prefix => "$thing->{functor}{value} ",
5354             never_empty => 1
5355             );
5356             }
5357 2         7 str_append_thing ($str, $thing->{arg}[0], $in_list, PARENS);
5358 2         103 str_append_end ($str);
5359             },
5360              
5361             'infix2' => sub {
5362 89         100 $in_list= NOT_IN_LIST; # just to be sure
5363 89 100       155 return str_append_parens ($str, $thing, NOT_IN_LIST)
5364             if $parens;
5365              
5366 85         128 my $f= $thing->{functor};
5367 85         176 str_append_join ($str, joinfunc => __PACKAGE__.'::Infix->obj');
5368 85         198 str_append_str ($str, $thing->{functor}{value});
5369 85         236 str_append_thing ($str, $thing->{arg}[0], NOT_IN_LIST, PARENS);
5370 85         5140 str_append_thing ($str, $thing->{arg}[1], NOT_IN_LIST, PARENS);
5371 85         3651 str_append_end ($str);
5372             },
5373              
5374             'infix23' => 'infix3',
5375             'infix3' => sub {
5376 5         8 $in_list= NOT_IN_LIST; # just to be sure
5377 5 100       14 return str_append_parens ($str, $thing, NOT_IN_LIST)
5378             if $parens;
5379              
5380 4         6 my $f= $thing->{functor};
5381 4         9 str_append_join ($str, never_empty => 1);
5382 4         11 str_append_thing ($str, $thing->{arg}[0], NOT_IN_LIST, PARENS);
5383 4         272 str_append_str ($str, " $thing->{functor}{value} ");
5384 4         11 str_append_thing ($str, $thing->{arg}[1], NOT_IN_LIST, PARENS);
5385 4 100       207 if (scalar(@{ $thing->{arg} }) == 3) {
  4         18  
5386 2         7 str_append_str ($str, " $thing->{functor}{value2} ");
5387 2         6 str_append_thing ($str, $thing->{arg}[2], NOT_IN_LIST, PARENS);
5388             }
5389 4         121 str_append_end ($str);
5390             },
5391              
5392             # prefix and suffix allow bitwise application:
5393             # Currently not supported via _prefix() and _suffix() helper
5394             # functions, but may be later. (Needs only a little rewrite
5395             # here. The helper functions don't need to be changed.)
5396             'prefix()' => 'prefix',
5397             'suffix' => 'prefix',
5398             'prefix' => sub {
5399 63 100       114 if ($thing->{type} eq 'prefix()') { # for AND() and OR() as functors
5400 20         24 $in_list = NOT_IN_LIST;
5401             }
5402 63         81 my $f= $thing->{functor};
5403 63 50 50     197 my $fk= $functor_kind{$f->{type} || ''}
5404             or die "Expected $thing->{type} to be mapped by \%functor_kind";
5405 63 100       102 if ($in_list) {
5406 27         52 my $qt= quote_perl($f->{value});
5407 27 100       119 str_append_map ($str, __PACKAGE__."::_".$fk."($qt,".($parens?1:0).",\$_)");
5408 27         29 for my $l (@{ $thing->{arg} }) {
  27         48  
5409 35         75 str_append_thing ($str, $l, IN_LIST, PARENS);
5410             }
5411 27         60 str_append_end ($str);
5412             }
5413             else {
5414 36         100 str_append_funcall($str, __PACKAGE__."::_".$fk, NOT_IN_LIST);
5415 36         107 str_append_thing ($str, $f->{value}, IN_LIST, NO_PARENS);
5416 36 100       79 str_append_thing ($str, $parens?1:0, IN_LIST, NO_PARENS);
5417 36         41 for my $l (@{ $thing->{arg} }) {
  36         63  
5418 46         80 str_append_thing ($str, $l, IN_LIST, PARENS);
5419             }
5420 36         78 str_append_end ($str);
5421             }
5422             },
5423              
5424             # funcall and infix use args inline if they are in list context.
5425             # They are handled by _prefix() and _suffix() helper functions in order
5426             # to allow dialect conversion:
5427             'funcall' => 'infix()',
5428             'infix()' => sub {
5429 97         109 $in_list= NOT_IN_LIST; # just to be sure
5430 97         140 my $f= $thing->{functor};
5431 97 50 50     298 my $fk= $functor_kind{$f->{type} || ''}
5432             or die 'Expected $thing->{type} to be mapped by %functor_kind';
5433              
5434 97         275 str_append_funcall($str, __PACKAGE__."::_".$fk, NOT_IN_LIST);
5435 97         214 str_append_thing ($str, $f->{value}, IN_LIST, NO_PARENS);
5436 97 100       228 str_append_thing ($str, $parens?1:0, IN_LIST, NO_PARENS);
5437 97         104 for my $l (@{ $thing->{arg} }) {
  97         187  
5438 188         308 str_append_thing ($str, $l, IN_LIST, PARENS);
5439             }
5440 97         205 str_append_end ($str);
5441             },
5442              
5443             'funcsep' => sub {
5444 6         8 $in_list= NOT_IN_LIST; # just to be sure
5445 6         11 str_append_join ($str, never_empty => 1, sep => ' ');
5446 6         20 str_append_str ($str, "$thing->{functor}{value}(");
5447 6         7 for my $t (@{ $thing->{arg} }) {
  6         13  
5448 27         60 str_append_thing ($str, $t, NOT_IN_LIST, NO_PARENS);
5449             }
5450 6         10 str_append_end ($str);
5451             },
5452              
5453             'case' => sub {
5454 47         76 $in_list= NOT_IN_LIST; # just to be sure
5455              
5456             # FIXME (maybe): we add parens here, so if there are no
5457             # when-then pairs at all and only the else part is printed,
5458             # it will get parens, too, no matter what. That's ok,
5459             # since it's a non-standard, marginal special case.
5460 47 100       94 return str_append_parens ($str, $thing, NOT_IN_LIST)
5461             if $parens;
5462              
5463 34         44 my $sw= $thing->{switchval};
5464 34 100       53 if ($sw) {
5465 22         52 str_append_funcall ($str, __PACKAGE__."::caseswitch", NOT_IN_LIST);
5466 22         43 str_append_thing ($str, $sw, NOT_IN_LIST, NO_PARENS);
5467             }
5468             else {
5469 12         19 str_append_funcall ($str, __PACKAGE__."::casecond", NOT_IN_LIST);
5470             }
5471              
5472 34 100       1478 if (my $e= $thing->{otherwise}) {
5473 24         51 str_append_thing ($str, $e, NOT_IN_LIST, NO_PARENS);
5474             }
5475             else {
5476 10         17 str_append_str ($str, 'NULL');
5477             }
5478              
5479 34         990 for my $wh (@{ $thing->{arg} }) {
  34         76  
5480 30 50       1060 if (ref($wh) eq 'ARRAY') {
5481 30         61 my ($when,$expr)= @$wh;
5482 30         66 str_append_funcall ($str, __PACKAGE__.'::whenthen', NOT_IN_LIST);
5483 30         65 str_append_thing ($str, $when, NOT_IN_LIST, NO_PARENS);
5484 30         1220 str_append_thing ($str, $expr, NOT_IN_LIST, NO_PARENS);
5485 30         1210 str_append_end ($str);
5486             }
5487             else {
5488 0         0 die 'expected array';
5489             }
5490             }
5491              
5492 34         55 str_append_end ($str);
5493             },
5494 1049     618   13182 );
5495             },
5496              
5497             'ColumnName' => sub {
5498             switch ($thing->{type},
5499             'interpol' => 'ident',
5500             'ident' => sub {
5501 48         82 str_append_typed ($str, 'colname', 'none', $thing, $in_list, hashkeys => 1);
5502             }
5503 48     11   156 );
5504             },
5505              
5506             'ColumnIndex' => sub {
5507 2 50 66 0   10 if (defined $thing->{length} || $thing->{desc}) {
5508 2         4 str_append_join ($str, sep => ' ');
5509 2         5 str_append_thing ($str, $thing->{name}, NOT_IN_LIST, NO_PARENS);
5510 2 100       50 if (defined $thing->{length}) {
5511 1         2 str_append_join ($str, prefix => '(', suffix => ')');
5512 1         2 str_append_thing ($str, $thing->{length}, NOT_IN_LIST, NO_PARENS);
5513 1         44 str_append_end ($str);
5514             }
5515 2 100       7 if ($thing->{desc}) {
5516 1         3 str_append_str ($str, 'DESC');
5517             }
5518 2         4 str_append_end ($str);
5519             }
5520             else {
5521 0         0 str_append_thing ($str, $thing->{name}, $in_list, $parens);
5522             }
5523             },
5524              
5525             'TableName' => sub {
5526             switch ($thing->{type},
5527             'interpol' => 'ident',
5528             'ident' => sub {
5529 5         11 str_append_typed ($str, 'tabname', 'none', $thing, $in_list, hashkeys => 1);
5530             }
5531 5     1   17 );
5532             },
5533              
5534             'Fetch' => 'Do',
5535             'Do' => sub {
5536 12     12   27 str_append_thing ($str, $thing->{stmt}, $in_list, $parens);
5537             },
5538 2463         49184 );
5539             }
5540              
5541             sub to_perl($$\@)
5542             {
5543 278     278 0 414 my ($line_start, $kind, $things)= @_;
5544 278         443 my $str= str_new($line_start);
5545 278         475 for my $thing (@$things) {
5546 386         722 str_append_thing ($str, $thing, IN_LIST, NO_PARENS);
5547             }
5548 275         654 my $text= str_get_string($str);
5549 275         1494 return "do{".__PACKAGE__."::_max1_if_scalar map{".__PACKAGE__."::${kind}->obj(\$_)} $text}",
5550             }
5551              
5552             ######################################################################
5553             # Top-level parser interface:
5554              
5555             sub lx_die_perhaps($;$)
5556             {
5557 559     559 0 622 my $lx= shift;
5558              
5559             # if a test value is given, check that it is defined:
5560 559 100       859 if (scalar(@_)) {
5561 280         354 my ($check_val)= @_;
5562 280 100       442 unless (defined $check_val) {
5563 1   50     4 $lx->{error}||= 'Unknown error';
5564             }
5565             }
5566              
5567             # if an error is set, then die:
5568 559 100       914 if ($lx->{error}) {
5569 2         6 die lx_pos($lx).": Error: $lx->{error}\n";
5570             }
5571             }
5572              
5573              
5574             sub parse_1_or_list($$$;$)
5575             {
5576 273     273 0 439 my ($lx, $parse_elem, $list_sep, $end)= @_;
5577 273         450 my $r= parse_list([], $lx, $parse_elem, $list_sep, $end);
5578 273         617 lx_die_perhaps($lx, $r);
5579 272         617 return @$r;
5580             }
5581              
5582             sub parse_0_try_list($$)
5583             {
5584 7     7 0 14 my ($lx, $parse_elem)= @_;
5585 7         14 my $r= parse_try_list([], $lx, $parse_elem);
5586 7         20 lx_die_perhaps($lx, $r);
5587 7         32 return @$r;
5588             }
5589              
5590             sub parse_stmt_list($)
5591             {
5592 137     137 0 390 parse_1_or_list ($_[0], \&parse_stmt, ';', ['}',')',']']);
5593             }
5594              
5595             sub parse_do_stmt($)
5596             {
5597 2     2 0 7 my ($lx) = @_;
5598             map {
5599 2         7 my $stmt = $_;
  2         6  
5600 2         7 my $r = create($lx, 'Do', qw(stmt));
5601 2         7 $r->{stmt} = $stmt;
5602 2         10 $r;
5603             }
5604             parse_stmt_list($lx);
5605             }
5606              
5607             sub parse_fetch_stmt($)
5608             {
5609 10     10 0 16 my ($lx) = @_;
5610             map {
5611 10         22 my $stmt = $_;
  10         13  
5612 10         20 my $r = create($lx, 'Fetch', qw(stmt));
5613 10         15 $r->{stmt} = $stmt;
5614 10         35 $r;
5615             }
5616             parse_stmt_list($lx);
5617             }
5618              
5619             my %top_parse= (
5620             # pure parse actions:
5621             'Stmt' => \&parse_stmt_list,
5622              
5623             'Join' => sub { parse_0_try_list($_[0], \&parse_join) },
5624             'TableOption' => sub { parse_0_try_list($_[0], \&parse_table_option) },
5625              
5626             'Expr' => sub { parse_1_or_list ($_[0], \&parse_expr, ',') },
5627             'Check' => sub { parse_1_or_list ($_[0], \&parse_check, ',') },
5628             'Type' => sub { parse_1_or_list ($_[0], \&parse_type, ',') },
5629             'Column' => sub { parse_1_or_list ($_[0], \&parse_column, ',') },
5630             'Table' => sub { parse_1_or_list ($_[0], \&parse_table, ',') },
5631             'Index' => sub { parse_1_or_list ($_[0], \&parse_index, ',') },
5632             'CharSet' => sub { parse_1_or_list ($_[0], \&parse_charset, ',') },
5633             'Collate' => sub { parse_1_or_list ($_[0], \&parse_collate, ',') },
5634             'Constraint' => sub { parse_1_or_list ($_[0], \&parse_constraint, ',') },
5635             'Transliteration' => sub { parse_1_or_list ($_[0], \&parse_transliteration, ',') },
5636             'Transcoding' => sub { parse_1_or_list ($_[0], \&parse_transcoding, ',') },
5637             'Order' => sub { parse_1_or_list ($_[0], \&parse_order, ',') },
5638             'ColumnSpec' => sub { parse_1_or_list ($_[0], \&parse_column_spec, ',') },
5639              
5640             # parse & execute actions:
5641             'Do' => sub { parse_do_stmt ($_[0]) },
5642             'Fetch' => sub { parse_fetch_stmt($_[0]) },
5643             );
5644             my $top_parse_re= '(?:'.join('|', sort { length($b) <=> length($a) } '', keys %top_parse).')';
5645             my $top_parse_re2= '(?:'.join('|', sort { length($b) <=> length($a) } 'none', keys %top_parse).')';
5646              
5647             sub interpol_set_context ($$)
5648             {
5649 515     515 0 807 my ($text, $ctxt)= @_;
5650 515         655 $text=~ s/(\Q${\SQL_MARK}\E$top_parse_re)(?::$top_parse_re2)?(\s*\{)/$1:$ctxt$2/gs;
  515         2768  
5651 515         2453 return $text;
5652             }
5653              
5654             sub good_interpol_type($)
5655             {
5656 8     8 0 16 my ($type)= @_;
5657 8         30 return !!$top_parse{$type};
5658             }
5659              
5660             sub mark_sql()
5661             {
5662             # Step 1:
5663             # This function will get the text without comments, strings, etc.,
5664             # and replace the initial SQL marking the start of SQL syntax by
5665             # our special SQL_MARK. Then, the unprocessed text will be
5666             # processed by replace_sql().
5667 5     5 0 340 s/\b\Q$sql_marker\E($top_parse_re\s*\{)/${\SQL_MARK}$1/gs;
  259         643  
5668              
5669             # Step 2:
5670             # Unmark false positives. The above finds false matches in
5671             # variables:
5672             #
5673             # $sql{...}
5674             #
5675             # We cannot(?) do this in one go, as we'd need a variable-width
5676             # negative look-behind regexp, which Perl does not have. This
5677             # is because there can be arbitrary white space between $ and
5678             # a variable name.
5679 5         11 s/([\$\@\%]\s*)\Q${\SQL_MARK}\E/$1$sql_marker/gs;
  5         242  
5680              
5681             # Note that there are still false positives, which are really hard
5682             # to find unless we start parsing Perl completely:
5683             #
5684             # ${ sql{blah} }
5685             }
5686              
5687             sub parse($$)
5688             {
5689 22     22 0 15465 my ($kind, $str)= @_;
5690 22         61 my $lx= lexer_new ($str, "", 0);
5691 22         49 my $func= $top_parse{$kind};
5692 22 50       48 return undef unless $func;
5693 22 50       49 return () if looking_at($lx, '');
5694 22         61 my @thing= $func->($lx);
5695 21         70 expect($lx, '', SHIFT);
5696 21         49 lx_die_perhaps ($lx);
5697 20         47 return to_perl(1, $kind, @thing);
5698             }
5699              
5700             sub replace_sql()
5701             {
5702 5     5 0 10286 my ($module, $file, $line)= caller(4); # find our from where we were invoked
5703              
5704 5         14 mark_sql();
5705             #print STDERR "DEBUG: BEFORE: $_\n";
5706              
5707 5         14 pos($_)= 0;
5708 5         10 REPLACEMENT: while (/(\Q${\SQL_MARK}\E($top_parse_re)(?::($top_parse_re2))?\s*\{)/gs) {
  263         5334  
5709             # prepare lexer:
5710 258   100     996 my $ctxt= $3 || 'Stmt';
5711 258         408 my $speckind= $2;
5712 258   66     538 my $kind= $speckind || $ctxt;
5713 258         420 my $start= pos($_) - length($1);
5714 258         1536 my $prefix= substr($_, 0, $start);
5715 258         5160 my $line_rel= ($prefix =~ tr/\n//);
5716 258         533 my $lx= lexer_new ($_, $file, $line + $line_rel);
5717              
5718             # select parser:
5719 258         419 my $func= $top_parse{$kind};
5720 258 50       451 unless ($func) {
5721 0         0 die "$file:".($line+$line_rel+1).
5722             ": Error: Plain ${sql_marker}${speckind}{...} is illegal, because the ".
5723             "surrounding block must not return an object.\n\tPlease use ".
5724             (english_or map "${sql_marker}${_}{...}", keys %top_parse)." to disambiguate.\n";
5725 0         0 last REPLACEMENT;
5726             }
5727              
5728             # parse (including closing brace):
5729 258         414 my @thing= $func->($lx);
5730 258         549 expect ($lx, '}', SHIFT);
5731 258         509 lx_die_perhaps ($lx);
5732              
5733 258         318 my $end= $lx->{token}{pos};
5734 258 50 33     805 my_confess unless defined $end && $start < $end;
5735              
5736             # Make Perl code:
5737             # Represent the parse result as a list in Perl (if it's only
5738             # one element, the parens don't hurt). Each thing is
5739             # handled individually by to_perl():
5740 258         467 my $perl= to_perl($line + $line_rel, $kind, @thing);
5741              
5742             # replace:
5743 258 50       484 print STDERR "$file:".($line+$line_rel+1).': DEBUG: '.__PACKAGE__." replacement: $perl\n"
5744             if $debug;
5745              
5746 258         12141 my $old_text= substr($_, $start, $end-$start, $perl); # extract and replace text
5747             # pos($_) is now undef, which is ok, we will
5748             # rescan the text anyway.
5749              
5750             # Insert newlines at the end that have been dropped so that the line
5751             # count does not change and Perl's error messages are useful:
5752 258         573 my $line_cnt_old= ($old_text =~ tr/\n//);
5753 258         443 my $line_cnt_new= ($perl =~ tr/\n//);
5754 258 50       447 my_confess "More newlines than before" #.": \n###\n$old_text\n###$perl\n###\n"
5755             if $line_cnt_new > $line_cnt_old;
5756              
5757 258 100       444 if (my $line_cnt_less= $line_cnt_old - $line_cnt_new) {
5758 101         1288 substr($_, $start + length($perl), 0, "\n" x $line_cnt_less);
5759             }
5760              
5761             # rescan everything in order to recurse into embedded sql{...}:
5762 258         4145 pos($_)= 0;
5763             }
5764 5         23 pos($_)= undef;
5765              
5766             #print STDERR "DEBUG: AFTER: $_\n";
5767             };
5768              
5769             FILTER_ONLY
5770             # code_no_comments => \&mark_sql, # This is way to slow.
5771             all => \&replace_sql;
5772              
5773             ######################################################################
5774             # Functions used in generated code:
5775              
5776             # Obj:
5777             {
5778             package SQL::Yapp::Obj;
5779              
5780 5     5   46 use strict;
  5         8  
  5         146  
5781 5     5   27 use warnings;
  5         7  
  5         180  
5782 5     5   24 use Carp qw(croak);
  5         7  
  5         561  
5783              
5784 0     0   0 sub op($) { return ''; }
5785              
5786             ######################################################################
5787             # stringify: simply return second entry in array, the string:
5788             use overload '""' => 'value',
5789 5     5   33 cmp => sub { "$_[0]" cmp "$_[1]" };
  5     173   16  
  5         58  
  173         33514  
5790              
5791             sub type_error($$)
5792             {
5793 3     3   10 my ($x, $want)= @_;
5794 3         6 my $r= ref($x);
5795 3         13 $r=~ s/^SQL::Yapp:://;
5796 3         389 croak "Error: Expected $want, but found ".$r;
5797             }
5798              
5799 0     0   0 sub asc($) { $_[0]->type_error('Asc'); }
5800 0     0   0 sub assign($) { $_[0]->type_error('assignment'); }
5801 0     0   0 sub charset($) { $_[0]->type_error('CharSet'); }
5802 0     0   0 sub constraint($) { $_[0]->type_error('Constraint'); }
5803 0     0   0 sub charset1($) { $_[0]->type_error('CharSet'); }
5804 0     0   0 sub collate1($) { $_[0]->type_error('Collate'); }
5805 0     0   0 sub colname($) { $_[0]->type_error('ColumnName'); }
5806 0     0   0 sub colspec($) { $_[0]->type_error('ColumnSpec'); }
5807 0     0   0 sub column1($) { $_[0]->type_error('Column'); }
5808 1     1   8 sub column1_single($) { $_[0]->type_error('Column'); }
5809 0     0   0 sub constraint1($) { $_[0]->type_error('Constraint'); }
5810 0     0   0 sub desc($) { $_[0]->type_error('Desc'); }
5811 0     0   0 sub engine1($) { $_[0]->type_error('Engine'); }
5812 0     0   0 sub expr($) { $_[0]->type_error('Expr'); }
5813 0     0   0 sub expr_or_check($) { $_[0]->type_error('Expr or Check'); }
5814 0     0   0 sub check($) { $_[0]->type_error('Check'); }
5815 1     1   4 sub exprparen($) { $_[0]->type_error('Expr'); }
5816 0     0   0 sub index1($) { $_[0]->type_error('Index'); }
5817 0     0   0 sub joinclause($) { $_[0]->type_error('JOIN clause'); }
5818 0     0   0 sub limit_number($) { $_[0]->type_error('number or ?'); }
5819 0     0   0 sub stmt($) { $_[0]->type_error('Stmt'); }
5820 0     0   0 sub subquery($) { $_[0]->type_error('subquery'); }
5821 0     0   0 sub table1($) { $_[0]->type_error('Table'); }
5822 0     0   0 sub tabname($) { $_[0]->type_error('TableName'); }
5823 0     0   0 sub tableopt($) { $_[0]->type_error('TableOption'); }
5824 0     0   0 sub transcoding($) { $_[0]->type_error('Transcoding'); }
5825 0     0   0 sub transliteration1($) { $_[0]->type_error('Transliteration'); }
5826 0     0   0 sub type($) { $_[0]->type_error('Type'); }
5827              
5828 0     0   0 sub do($) { $_[0]->type_error('Do'); }
5829 0     0   0 sub fetch($) { $_[0]->type_error('Fetch'); }
5830             }
5831              
5832             # Obj1:
5833             {
5834             package SQL::Yapp::Obj1;
5835              
5836 5     5   3864 use strict;
  5         7  
  5         99  
5837 5     5   19 use warnings;
  5         25  
  5         148  
5838 5     5   24 use base qw(SQL::Yapp::Obj);
  5         6  
  5         1656  
5839 5     5   29 use Scalar::Util qw(blessed);
  5         10  
  5         724  
5840              
5841             sub obj($$)
5842             {
5843 890     890   4271 my ($class,$x)= @_;
5844 890 100 66     2430 return $x
5845             if blessed($x) && $x->isa(__PACKAGE__);
5846 731         2369 return bless([$x], $class);
5847             }
5848              
5849 817     817   3875 sub value($) { return $_[0][0]; }
5850             }
5851              
5852             ###############
5853             # Asterisk:
5854             {
5855             package SQL::Yapp::Asterisk;
5856              
5857 5     5   25 use strict;
  5         7  
  5         116  
5858 5     5   19 use warnings;
  5         6  
  5         146  
5859 5     5   20 use base qw(SQL::Yapp::Obj);
  5         9  
  5         1777  
5860              
5861             sub obj($)
5862             {
5863 2     2   180 my ($class)= @_;
5864 2         9 return bless([], $class);
5865             }
5866              
5867 2     2   117 sub value($) { return '*'; }
5868              
5869 2     2   8 sub column1($) { return $_[0]; }
5870 0     0   0 sub column1_single($) { return $_[0]; }
5871 0     0   0 sub expr($) { return $_[0]; }
5872 0     0   0 sub expr_or_check($) { return $_[0]; }
5873              
5874 0     0   0 sub asterisk($) { return $_[0]; }
5875             }
5876              
5877             # Question:
5878             {
5879             package SQL::Yapp::Question;
5880              
5881 5     5   27 use strict;
  5         13  
  5         123  
5882 5     5   21 use warnings;
  5         7  
  5         120  
5883 5     5   21 use base qw(SQL::Yapp::Obj);
  5         6  
  5         1861  
5884              
5885             sub obj($)
5886             {
5887 1     1   3 my ($class)= @_;
5888 1         4 return bless([], $class);
5889             }
5890              
5891 1     1   4 sub value($) { return '?' }
5892              
5893 0     0   0 sub limit_number($) { return $_[0]; }
5894 1     1   3 sub exprparen($) { return $_[0]; }
5895 0     0   0 sub expr($) { return $_[0]; }
5896 0     0   0 sub expr_or_check($) { return $_[0]; }
5897 0     0   0 sub asc($) { return $_[0]; }
5898 0     0   0 sub desc($) { return SQL::Yapp::Desc->obj($_[0]); }
5899             }
5900              
5901             # ExprSpecial:
5902             {
5903             package SQL::Yapp::ExprSpecial;
5904              
5905 5     5   31 use strict;
  5         6  
  5         100  
5906 5     5   18 use warnings;
  5         9  
  5         153  
5907 5     5   22 use base qw(SQL::Yapp::Obj1);
  5         7  
  5         1654  
5908              
5909 0     0   0 sub exprparen($) { return $_[0]; }
5910 0     0   0 sub expr($) { return $_[0]; }
5911 0     0   0 sub expr_or_check($) { return $_[0]; }
5912 0     0   0 sub asc($) { return $_[0]; }
5913 0     0   0 sub desc($) { return SQL::Yapp::Desc->obj($_[0]); }
5914             }
5915              
5916             # Stmt:
5917             {
5918             package SQL::Yapp::Stmt;
5919              
5920 5     5   27 use strict;
  5         8  
  5         104  
5921 5     5   31 use warnings;
  5         5  
  5         133  
5922 5     5   19 use Carp qw(croak);
  5         9  
  5         200  
5923 5     5   23 use base qw(SQL::Yapp::Obj1);
  5         12  
  5         1796  
5924              
5925 1     1   8 sub subquery($) { $_[0]->type_error('SELECT statement'); }
5926 0     0   0 sub exprparen($) { $_[0]->subquery(); }
5927 2     2   7 sub expr($) { $_[0]->subquery(); }
5928 0     0   0 sub expr_or_check($) { $_[0]->subquery(); }
5929 0     0   0 sub table1($) { $_[0]->subquery(); }
5930              
5931 3     3   7 sub stmt($) { return $_[0]; }
5932              
5933             sub do($)
5934             {
5935 0     0   0 my ($stmt) = @_;
5936 0         0 my $dbh = SQL::Yapp::get_dbh();
5937 0         0 $dbh->do($stmt);
5938 0         0 return; # return no statements so that _max1_if_scalar is ok with void context
5939             }
5940             }
5941              
5942             # SelectStmt:
5943             {
5944             package SQL::Yapp::SelectStmt;
5945              
5946 5     5   26 use strict;
  5         13  
  5         126  
5947 5     5   20 use warnings;
  5         5  
  5         131  
5948 5     5   21 use Carp qw(croak);
  5         6  
  5         199  
5949 5     5   22 use base qw(SQL::Yapp::Stmt);
  5         8  
  5         1663  
5950              
5951 2     2   9 sub subquery($) { return '('.($_[0]->value).')'; }
5952              
5953             sub fetch($)
5954             {
5955 0     0   0 my ($stmt) = @_;
5956 0         0 my $dbh = SQL::Yapp::get_dbh();
5957 0         0 my $sth = $dbh->prepare($stmt);
5958 0         0 my $aref = $dbh->selectall_arrayref($sth, { Slice => {} });
5959 0 0       0 return unless $aref;
5960 0         0 return @$aref;
5961             }
5962             }
5963              
5964             # SelectStmtSingle:
5965             {
5966             package SQL::Yapp::SelectStmtSingle;
5967              
5968 5     5   28 use strict;
  5         7  
  5         123  
5969 5     5   20 use warnings;
  5         8  
  5         149  
5970 5     5   20 use Carp qw(croak);
  5         7  
  5         195  
5971 5     5   25 use base qw(SQL::Yapp::SelectStmt);
  5         12  
  5         1704  
5972              
5973             sub fetch($)
5974             {
5975 0     0   0 my ($stmt) = @_;
5976 0         0 my $dbh = SQL::Yapp::get_dbh();
5977 0         0 my $sth = $dbh->prepare($stmt);
5978 0 0       0 return unless $sth->execute;
5979 0         0 my @r= ();
5980 0         0 while (my $a= $sth->fetchrow_arrayref) {
5981 0 0       0 die unless scalar(@$a) == 1;
5982 0         0 push @r, $a->[0];
5983             }
5984 0         0 return @r;
5985             }
5986             }
5987              
5988             # Do:
5989             # This is a bit different, since the obj() method will actually execute the statement.
5990             {
5991             package SQL::Yapp::Do;
5992              
5993 5     5   28 use strict;
  5         8  
  5         125  
5994 5     5   27 use warnings;
  5         7  
  5         149  
5995 5     5   20 use Carp qw(confess);
  5         9  
  5         414  
5996              
5997             sub obj($$)
5998             {
5999 0     0   0 my ($class, $stmt) = @_;
6000 0         0 return $stmt->do;
6001             }
6002             }
6003              
6004             # Fetch:
6005             # This is a bit different, since the obj() method will actually execute the statement.
6006             {
6007             package SQL::Yapp::Fetch;
6008              
6009 5     5   26 use strict;
  5         7  
  5         105  
6010 5     5   19 use warnings;
  5         7  
  5         323  
6011              
6012             sub obj($$)
6013             {
6014 0     0   0 my ($class, $stmt) = @_;
6015 0         0 return $stmt->fetch;
6016             }
6017             }
6018              
6019             # ColumnName:
6020             {
6021             package SQL::Yapp::ColumnName;
6022              
6023 5     5   29 use strict;
  5         13  
  5         100  
6024 5     5   20 use warnings;
  5         6  
  5         115  
6025 5     5   18 use base qw(SQL::Yapp::Obj1);
  5         7  
  5         1245  
6026              
6027 0     0   0 sub colname($) { return $_[0]; }
6028             }
6029              
6030             # TableName:
6031             {
6032             package SQL::Yapp::TableName;
6033              
6034 5     5   25 use strict;
  5         9  
  5         77  
6035 5     5   18 use warnings;
  5         7  
  5         127  
6036 5     5   19 use base qw(SQL::Yapp::Obj1);
  5         112  
  5         1133  
6037              
6038 0     0   0 sub tabname($) { return $_[0]; }
6039             }
6040              
6041             # Column:
6042             {
6043             package SQL::Yapp::Column;
6044              
6045 5     5   40 use strict;
  5         7  
  5         125  
6046 5     5   30 use warnings;
  5         8  
  5         157  
6047 5     5   29 use base qw(SQL::Yapp::Obj1);
  5         6  
  5         1790  
6048              
6049 2     2   6 sub column1($) { return $_[0]; }
6050 3     3   9 sub exprparen($) { return $_[0]; }
6051 1     1   4 sub expr($) { return $_[0]; }
6052 0     0   0 sub expr_or_check($) { return $_[0]; }
6053 2     2   4 sub asc($) { return $_[0]; }
6054 5     5   18 sub desc($) { return SQL::Yapp::Desc->obj($_[0]); }
6055             }
6056              
6057             # Table:
6058             {
6059             package SQL::Yapp::Table;
6060 5     5   28 use strict;
  5         7  
  5         101  
6061 5     5   18 use warnings;
  5         15  
  5         121  
6062 5     5   18 use base qw(SQL::Yapp::Obj1);
  5         8  
  5         1066  
6063 5     5   14 sub table1($) { return $_[0]; }
6064             }
6065              
6066             # CharSet:
6067             {
6068             package SQL::Yapp::CharSet;
6069 5     5   32 use strict;
  5         6  
  5         93  
6070 5     5   18 use warnings;
  5         12  
  5         138  
6071 5     5   62 use base qw(SQL::Yapp::Obj1);
  5         13  
  5         1043  
6072 0     0   0 sub charset1($) { return $_[0]; }
6073             }
6074              
6075             # Collate:
6076             {
6077             package SQL::Yapp::Collate;
6078 5     5   34 use strict;
  5         8  
  5         125  
6079 5     5   25 use warnings;
  5         7  
  5         135  
6080 5     5   21 use base qw(SQL::Yapp::Obj1);
  5         6  
  5         1069  
6081 0     0   0 sub collate1($) { return $_[0]; }
6082             }
6083              
6084             # Constraint:
6085             {
6086             package SQL::Yapp::Constraint;
6087 5     5   25 use strict;
  5         14  
  5         107  
6088 5     5   17 use warnings;
  5         15  
  5         137  
6089 5     5   22 use base qw(SQL::Yapp::Obj1);
  5         7  
  5         1075  
6090 0     0   0 sub constraint1($) { return $_[0]; }
6091             }
6092              
6093             # Index:
6094             {
6095             package SQL::Yapp::Index;
6096 5     5   27 use strict;
  5         8  
  5         106  
6097 5     5   23 use warnings;
  5         17  
  5         125  
6098 5     5   26 use base qw(SQL::Yapp::Obj1);
  5         7  
  5         1058  
6099 0     0   0 sub index1($) { return $_[0]; }
6100             }
6101              
6102             # Transliteration:
6103             {
6104             package SQL::Yapp::Transliteration;
6105 5     5   26 use strict;
  5         5  
  5         96  
6106 5     5   18 use warnings;
  5         6  
  5         121  
6107 5     5   29 use base qw(SQL::Yapp::Obj1);
  5         9  
  5         1059  
6108 0     0   0 sub transliteration($) { return $_[0]; }
6109             }
6110              
6111             # Transcoding:
6112             {
6113             package SQL::Yapp::Transcoding;
6114 5     5   28 use strict;
  5         6  
  5         96  
6115 5     5   18 use warnings;
  5         8  
  5         175  
6116 5     5   27 use base qw(SQL::Yapp::Obj1);
  5         8  
  5         1077  
6117 0     0   0 sub transcoding($) { return $_[0]; }
6118             }
6119              
6120             # TableOption:
6121             {
6122             package SQL::Yapp::TableOption;
6123 5     5   30 use strict;
  5         7  
  5         91  
6124 5     5   19 use warnings;
  5         8  
  5         117  
6125 5     5   25 use base qw(SQL::Yapp::Obj1);
  5         8  
  5         1032  
6126 2     2   6 sub tableopt($) { return $_[0]; }
6127             }
6128              
6129             # Engine:
6130             {
6131             package SQL::Yapp::Engine;
6132 5     5   34 use strict;
  5         7  
  5         90  
6133 5     5   25 use warnings;
  5         7  
  5         119  
6134 5     5   21 use base qw(SQL::Yapp::Obj1);
  5         8  
  5         1073  
6135 0     0   0 sub engine1($) { return $_[0]; }
6136             }
6137              
6138              
6139             # Join:
6140             {
6141             package SQL::Yapp::Join;
6142              
6143 5     5   25 use strict;
  5         8  
  5         96  
6144 5     5   26 use warnings;
  5         8  
  5         127  
6145 5     5   27 use base qw(SQL::Yapp::Obj1);
  5         6  
  5         1068  
6146              
6147 4     4   10 sub joinclause($) { return $_[0]; }
6148             }
6149              
6150             # Check:
6151             {
6152             package SQL::Yapp::Check;
6153              
6154 5     5   24 use strict;
  5         8  
  5         86  
6155 5     5   17 use warnings;
  5         14  
  5         125  
6156 5     5   20 use base qw(SQL::Yapp::Obj1);
  5         7  
  5         1509  
6157              
6158 3     3   23 sub check($) { return $_[0]; }
6159 3     3   8 sub expr_or_check($) { return $_[0]; }
6160              
6161             sub obj($$)
6162             {
6163 18 100   18   54 if (ref($_[1]) eq $_[0]) {
    100          
6164 9         31 return $_[1];
6165             }
6166             elsif (ref($_[1])) {
6167 7         18 bless($_[1], $_[0]);
6168             }
6169             else {
6170 2         9 $_[0]->SUPER::obj($_[1]);
6171             }
6172             }
6173             }
6174              
6175             # Expr:
6176             {
6177             package SQL::Yapp::Expr;
6178              
6179 5     5   26 use strict;
  5         11  
  5         108  
6180 5     5   18 use warnings;
  5         12  
  5         136  
6181 5     5   19 use base qw(SQL::Yapp::Obj1);
  5         11  
  5         1540  
6182              
6183 0     0   0 sub exprparen($) { return '('.($_[0]->value).')'; }
6184 18     18   54 sub expr($) { return $_[0]; }
6185 0     0   0 sub expr_or_check($) { return $_[0]; }
6186 0     0   0 sub asc($) { return $_[0]; }
6187 0     0   0 sub desc($) { return SQL::Yapp::Desc->obj($_[0]); }
6188             }
6189              
6190             # Infix:
6191             {
6192             package SQL::Yapp::Infix;
6193              
6194 5     5   27 use strict;
  5         7  
  5         96  
6195 5     5   18 use warnings;
  5         20  
  5         148  
6196 5     5   20 use base qw(SQL::Yapp::Expr);
  5         7  
  5         1234  
6197 5     5   28 use Carp qw(croak);
  5         7  
  5         1138  
6198              
6199             sub obj($$$$)
6200             {
6201 56     56   112 my ($class, $op, $a1, $a2)= @_;
6202 56         144 return bless(["$a1 $op $a2", $op, $a1, $a2], $class);
6203             }
6204              
6205 14     14   31 sub op($) { return $_[0][1]; }
6206 14     14   33 sub arg1($) { return $_[0][2]; }
6207 14     14   22 sub arg2($) { return $_[0][3]; }
6208              
6209             sub assign($)
6210             {
6211 14     14   17 my ($self)= @_;
6212 14 50       20 if ($self->op() eq '=') { # we're not checking everything, just whether it's an assignment
6213 14         27 return $self;
6214             }
6215 0         0 croak "Assignment expected, but found top-level operator '".($self->op)."'.";
6216             }
6217             }
6218              
6219             # Order:
6220             {
6221             package SQL::Yapp::Order;
6222              
6223 5     5   27 use strict;
  5         6  
  5         99  
6224 5     5   17 use warnings;
  5         9  
  5         126  
6225 5     5   20 use base qw(SQL::Yapp::Obj1);
  5         7  
  5         1170  
6226 5     5   31 use Scalar::Util qw(blessed);
  5         8  
  5         483  
6227              
6228             sub obj($$)
6229             {
6230 7     7   41 my ($class,$x)= @_;
6231 7 50 33     68 return $x
6232             if blessed($x) && $x->isa('SQL::Yapp::Obj');
6233 0         0 return bless([$x], 'SQL::Yapp::Asc'); # not Order, but Asc.
6234             }
6235             }
6236              
6237             # Asc:
6238             {
6239             package SQL::Yapp::Asc;
6240              
6241 5     5   25 use strict;
  5         6  
  5         131  
6242 5     5   22 use warnings;
  5         6  
  5         124  
6243 5     5   20 use base qw(SQL::Yapp::Order);
  5         14  
  5         1406  
6244              
6245 0     0   0 sub asc($) { return $_[0]; }
6246 0     0   0 sub desc($) { return SQL::Yapp::Desc->obj($_[0]); }
6247             }
6248              
6249             # Desc:
6250             {
6251             package SQL::Yapp::Desc;
6252              
6253 5     5   28 use strict;
  5         7  
  5         93  
6254 5     5   27 use warnings;
  5         8  
  5         126  
6255 5     5   21 use base qw(SQL::Yapp::Order);
  5         7  
  5         1508  
6256              
6257             sub obj($$)
6258             {
6259 9     9   15 my ($class, $orig)= @_;
6260 9         17 return bless(["$orig DESC",$orig],$class);
6261             }
6262              
6263 1     1   3 sub orig($) { return $_[0][1]; }
6264              
6265 2     2   7 sub asc($) { return $_[0]; }
6266 1     1   3 sub desc($) { return &orig; }
6267             }
6268              
6269             # Type:
6270             {
6271             package SQL::Yapp::Type;
6272              
6273 5     5   27 use strict;
  5         6  
  5         114  
6274 5     5   21 use warnings;
  5         13  
  5         151  
6275 5     5   21 use base qw(SQL::Yapp::Obj);
  5         9  
  5         1092  
6276 5     5   27 use Hash::Util qw(lock_keys);
  5         8  
  5         35  
6277 5     5   247 use Carp qw(croak);
  5         6  
  5         3222  
6278              
6279             sub set_base($$$)
6280             {
6281 11     11   22 my ($self, $base, $spec)= @_;
6282              
6283             # set new spec:
6284 11         19 $self->{base}= $base;
6285 11         17 $self->{spec}= $spec;
6286              
6287             # filter options by new spec:
6288 11         12 for my $o (keys %{ $self->{option} }) {
  11         30  
6289 3 100       10 unless ($spec->{$o}) {
6290 1         2 delete $self->{option}{$o};
6291             }
6292             }
6293              
6294 11         93 return $self;
6295             }
6296              
6297             sub set_property($$$)
6298             {
6299 13     13   21 my ($self, $key, $value)= @_;
6300 13         41 my %a= %$self;
6301 13 50       34 croak "No $key for $self->{base} allowed." unless $self->{spec}{$key};
6302 13         25 $self->{option}{$key}= $value;
6303 13         23 return $self;
6304             }
6305              
6306             sub new($)
6307             {
6308 9     9   1517 my $r= bless({ base => undef, spec => undef, option => {} }, $_[0]);
6309 9         85 lock_keys %$r;
6310 9         128 return $r;
6311             }
6312              
6313             sub obj($$)
6314             {
6315 13     13   143 return $_[1];
6316             }
6317              
6318             sub clone($)
6319             {
6320 11     11   17 my ($self)= @_;
6321             my $r= bless({
6322             %$self,
6323             # no need to make a deep copy of 'spec', because it is never changed.
6324 11         22 option => { %{ $self->{option} } },
  11         45  
6325             }, __PACKAGE__);
6326 11         41 lock_keys %$r;
6327 11         117 return $r;
6328             }
6329              
6330             sub type($)
6331             {
6332 7     7   13 return $_[0]->clone(); # make a copy before trying to modify this
6333             }
6334              
6335             sub colspec($)
6336             {
6337 1     1   8 return SQL::Yapp::ColumnSpec->new($_[0]); # make a copy producing a ColumnSpec
6338             }
6339              
6340             sub value($)
6341             {
6342 38     38   63 my ($self)= @_;
6343 38 100       91 return '' unless $self->{base};
6344 20         36 my @r= ($self->{base});
6345 20 100 66     79 if ($self->{spec}{prec1} && defined $self->{option}{prec1}) {
6346 19         25 my $len_str= '';
6347 19         32 $len_str.= $self->{option}{prec1};
6348 19 50 66     41 if ($self->{spec}{prec2} && defined $self->{option}{prec2}) {
6349 0         0 $len_str.= ', '.$self->{option}{prec2};
6350             }
6351             else {
6352 19 0 33     33 if ($self->{spec}{prec_mul} && $self->{option}{prec_mul}) {
6353 0         0 $len_str.= ' '.$self->{option}{prec_mul};
6354             }
6355 19 0 33     30 if ($self->{spec}{prec_unit} && $self->{option}{prec_unit}) {
6356 0         0 $len_str.= ' '.$self->{option}{prec_unit};
6357             }
6358             }
6359 19         43 push @r, '('.$len_str.')';
6360             }
6361 20 50 33     49 if (my $value_list= $self->{spec}{value_list} && $self->{option}{value_list}) {
6362 0         0 push @r, '('.join(', ',@$value_list).')';
6363             }
6364 20 100 100     63 if (my $x= $self->{spec}{charset} && $self->{option}{charset}) {
6365 3         4 push @r, 'CHARACTER SET', $x;
6366             }
6367 20 50 66     58 if (my $x= $self->{spec}{collate} && $self->{option}{collate}) {
6368 0         0 push @r, 'COLLATE', $x;
6369             }
6370 20         35 for my $key ('sign', 'zerofill', 'timezone') {
6371 60 50 66     134 if (my $x= $self->{spec}{$key} && $self->{option}{$key}) {
6372 0         0 push @r, $x;
6373             }
6374             }
6375              
6376 20         132 return join(' ', @r);
6377             }
6378             }
6379              
6380              
6381             # ColumnSpec:
6382             {
6383             package SQL::Yapp::ColumnSpec;
6384              
6385 5     5   31 use strict;
  5         7  
  5         93  
6386 5     5   17 use warnings;
  5         7  
  5         150  
6387 5     5   19 use base qw(SQL::Yapp::Obj);
  5         9  
  5         982  
6388 5     5   27 use Hash::Util qw(lock_keys);
  5         13  
  5         17  
6389 5     5   241 use Carp qw(croak);
  5         7  
  5         30917  
6390              
6391             sub new($$)
6392             {
6393 3     3   17 my ($class, $type)= @_;
6394 3         11 my $r= bless({ datatype => $type->clone(), name => {}, option => {} }, $class);
6395 3         39 lock_keys %$r;
6396 3         50 return $r;
6397             }
6398              
6399             sub obj($$)
6400             {
6401 3     3   21 return $_[1];
6402             }
6403              
6404             sub clone($)
6405             {
6406 1     1   3 my ($self)= @_;
6407             my $r= bless({
6408             datatype => $self->{datatype}->clone(),
6409 1         3 name => { %{ $self->{name} } },
6410 1         5 option => { %{ $self->{option} } },
  1         5  
6411             }, __PACKAGE__);
6412 1         4 lock_keys %$r;
6413 1         8 return $r;
6414             }
6415              
6416             sub colspec($)
6417             {
6418 1     1   4 return $_[0]->clone(); # make a copy before trying to modify this
6419             }
6420              
6421             sub name($$)
6422             {
6423 6     6   13 my ($self, $key)= @_;
6424 6 50       16 if (my $x= $self->{name}{$key}) {
6425 0         0 return ('CONSTRAINT', $x);
6426             }
6427 6         17 return;
6428             }
6429              
6430             sub value($)
6431             {
6432 4     4   11 my ($self)= @_;
6433 4         12 my @r= ($self->{datatype});
6434              
6435 4         13 for my $key ('notnull', 'autoinc', 'unique', 'primary', 'key') {
6436 20 100       47 if (my $x= $self->{option}{$key}) {
6437 4         13 push @r, $self->name($key), $x;
6438             }
6439             }
6440              
6441 4         13 for my $key ('default', 'column_format', 'storage') {
6442 12 100       32 if (my $x= $self->{option}{$key}) {
6443 2         5 push @r, $self->name($key), uc($key), $x;
6444             }
6445             }
6446              
6447 4         8 for my $key ('check') {
6448 4 50       24 if (my $x= $self->{option}{$key}) {
6449 0         0 push @r, $self->name($key), uc($key), '('.$x.')';
6450             }
6451             }
6452              
6453 4         9 for my $key ('references') {
6454 4 50       11 if (my $x= $self->{option}{$key}) {
6455 0         0 push @r, $self->name($key), $x;
6456             }
6457             }
6458              
6459 4         12 return join(' ', @r);
6460             }
6461             }
6462              
6463              
6464             # Special Constants:
6465 2     2 0 259 sub ASTERISK { SQL::Yapp::Asterisk->obj(); }
6466 1     1 0 7 sub QUESTION { SQL::Yapp::Question->obj(); }
6467 0     0 0 0 sub NULL { SQL::Yapp::ExprSpecial->obj('NULL'); }
6468 0     0 0 0 sub TRUE { SQL::Yapp::ExprSpecial->obj('TRUE'); }
6469 0     0 0 0 sub FALSE { SQL::Yapp::ExprSpecial->obj('FALSE'); }
6470 0     0 0 0 sub UNKNOWN { SQL::Yapp::ExprSpecial->obj('UNKNOWN'); }
6471 0     0 0 0 sub DEFAULT { SQL::Yapp::ExprSpecial->obj('DEFAULT'); }
6472              
6473              
6474             # Wrapped DBI methods:
6475             sub croak_no_ref($)
6476             {
6477 1     1 0 2 my ($self)= @_;
6478 1         5 croak "Error: Wrong type argument from interpolated code:\n".
6479             "\tExpected scalar, but found ".my_dumper($self);
6480             }
6481              
6482             ########################################
6483             # Generators:
6484              
6485             # These functions are used to typecheck interpolated Perl code's
6486             # result values and to generate objects on the fly if that's possible.
6487             # Usually on-the-fly generation coerces basic Perl types to a blessed
6488             # object, but it would also be feasible to coerce objects to objects.
6489             # Some 'generator' functions don't generate at all, but simply type
6490             # check.
6491             #
6492             # Note: often these functions are invoked in string context, which
6493             # means that directly after their invocation, the string cast operator
6494             # is invoked. However, there's no easy way to prevent object creation
6495             # in that case, because there is no such thing as 'wantstring'
6496             # (would-be analog to 'wantarray'). So these functions must always
6497             # return a blessed reference.
6498              
6499             sub _functor($$@)
6500             {
6501 106     106   171 my ($functor, $parens, @arg)= @_;
6502              
6503             # possibly translate the functor to a different SQL dialect:
6504 106 100       206 if (my $dialect= $functor->{dialect}) {
6505 97 100       157 if (my $f2= find_ref(%$dialect, $write_dialect)) {
6506 28         35 $functor= $f2;
6507             }
6508             }
6509              
6510             # print it:
6511 106         174 my $name= $functor->{value};
6512              
6513             # prefix and suffix are not handled here, because they behave
6514             # differently: they assume exactly one argument are applied
6515             # point-wise. They cannot be switched (ok, we might switch
6516             # between prefix and suffix, but that's not supported yet).
6517             my $s= switch ($functor->{type},
6518             'infix()' => sub {
6519             (scalar(@arg) ?
6520             join(" $name ", @arg)
6521             : defined($functor->{result0}) ?
6522             get_quote_val->($functor->{result0})
6523 52 50   52   190 : die "Error: Functor $functor->{value} used with 0 args, but requires at least one."
    100          
6524             );
6525             },
6526             'funcall' => sub {
6527 21     21   24 $parens= 0;
6528 21         69 "$name(".join(", ", @arg).")";
6529             },
6530             'prefix' => sub {
6531 26 100   26   47 die "Error: Exactly one argument expected for operator $functor->{value},\n".
6532             "\tfound (".join(",", @arg).")"
6533             unless scalar(@arg) == 1;
6534 25         51 "$name $arg[0]"
6535             },
6536             'suffix' => sub {
6537 7 50   7   18 die "Error: exactly one argument expected, found @arg" unless scalar(@arg) == 1;
6538 7         14 "$arg[0] $name"
6539             },
6540 106         595 );
6541 105 100       851 return $parens ? "($s)" : $s;
6542             }
6543              
6544             sub _prefix($$@)
6545             {
6546 67     67   182 my ($name, $parens)= splice @_,0,2;
6547 67   100     186 return _functor($functor_prefix{$name} || { value => $name, type => 'funcall' } , $parens, @_);
6548             }
6549              
6550             sub _suffix($$@)
6551             {
6552 39     39   333 my ($name, $parens)= splice @_,0,2;
6553 39         89 return _functor($functor_suffix{$name}, $parens, @_);
6554             }
6555              
6556             sub _max1_if_scalar(@)
6557             {
6558             # void context:
6559 219 100   219   416 unless (defined wantarray) {
6560 1 50       5 return if scalar(@_) == 0; # allow void context with no params (e.g. after Do)
6561 1         173 croak 'Error: NYI: void context is currently not supported for SQL blocks.';
6562             }
6563              
6564             # list context:
6565 218 100       344 return @_ if wantarray;
6566              
6567             # scalar context:
6568 191 100       444 croak 'Error: Multiple results cannot be assigned to scalar'
6569             if scalar(@_) > 1;
6570 190         488 return $_[0];
6571             }
6572              
6573             sub min1(@)
6574             {
6575 0 0   0 0 0 croak 'Error: Expected at least one element, but found an empty list'
6576             if scalar(@_) == 0;
6577 0         0 return @_;
6578             }
6579              
6580             sub min1default($@)
6581             {
6582 0 0   0 0 0 return @_ if scalar(@_) == 1;
6583 0         0 shift;
6584 0         0 return @_;
6585             }
6586              
6587             sub joinlist($$$$$@)
6588             {
6589 213 100   213 0 374 if (scalar(@_) == 5) {
6590 1 50       7 return $_[1] if defined $_[1];
6591 0         0 my ($module, $file, $line)= caller;
6592 0         0 croak "$file:$_[0]: Error: Expected at least one element, but found an empty list";
6593             }
6594 212         667 return $_[2].join ($_[3], @_[5..$#_]).$_[4];
6595             }
6596              
6597             sub assign($) # check that the result is an assignment, i.e.:`a` =
6598             {
6599 14     14 0 17 my ($x)= @_;
6600 14 50       21 if (ref($x)) {
6601 14         21 return $x->assign();
6602             }
6603             else {
6604 0         0 croak "Assignment expected, but found non-reference.";
6605             }
6606             }
6607              
6608             sub set2values(@)
6609             {
6610 7 50   7 0 18 croak "At least one value expected" if scalar(@_) == 0;
6611             return
6612             ' ('.
6613 14         22 join(',', map { assign($_)->arg1() } @_).
6614             ') VALUES ('.
6615 7         11 join(',', map { $_->arg2() } @_).
  14         20  
6616             ')';
6617             }
6618              
6619             sub exprlist($)
6620             {
6621 7     7 0 25 my ($x)= @_;
6622 7 50       20 croak "Array reference expected for expression list"
6623             unless ref($x) eq 'ARRAY';
6624 7 50       21 croak "At least one element expected in expression list"
6625             unless scalar(@$x) >= 1;
6626 7         16 return '('.join(', ', map { expr($_) } @$x).')';
  14         31  
6627             }
6628              
6629             ####################
6630             # Type
6631              
6632             sub type($)
6633             {
6634 7     7 0 1279 my ($x)= @_;
6635 7 50       16 if (ref($x)) {
6636 7         14 return $x->type();
6637             }
6638             else {
6639 0         0 croak "Type expected, but found non-reference (user types are not supported yet).";
6640             }
6641             }
6642              
6643             # These have $self at the end because it's easier to generate code like that.
6644             sub type_base($$)
6645             {
6646 11     11 0 38 my $self= pop @_;
6647 11         35 my ($base)= @_;
6648 11 50       27 croak "Unrecognised base type '$base'" unless
6649             my $spec= find_ref(%type_spec, $base);
6650 11 50       31 die unless $self;
6651 11         27 return $self->set_base($base, $spec);
6652             }
6653              
6654             sub type_basewlist($@)
6655             {
6656 0     0 0 0 my $self= pop @_;
6657 0         0 my ($base, @value)= @_;
6658 0 0       0 croak "Unrecognised base type '$base'" unless
6659             my $spec= find_ref(%type_spec, $base);
6660 0 0       0 die unless $self;
6661 0         0 $self->set_base($base, $spec);
6662 0         0 $self->set_property('value_list', \@value);
6663 0         0 return $self;
6664             }
6665              
6666             sub type_length($$;$)
6667             {
6668 11     11 0 18 my $self= pop @_;
6669 11         17 my ($prec1, $prec2)= @_;
6670 11         27 $self->set_property('prec1', $prec1);
6671 11 50       22 $self->set_property('prec2', $prec2) if defined $prec2;
6672 11         44 return $self;
6673             }
6674              
6675             sub type_largelength($$$;$)
6676             {
6677 0     0 0 0 my $self= pop @_;
6678 0         0 my ($coeff, $mul, $unit)= @_;
6679 0         0 $self->set_property('prec1', $coeff);
6680 0 0       0 $self->set_property('prec_mul', $mul) if defined $mul;
6681 0 0       0 $self->set_property('prec_unit', $unit) if defined $unit;
6682 0         0 return $self;
6683             }
6684              
6685             sub type_property($$$)
6686             {
6687 2     2 0 5 my $self= pop @_;
6688 2         3 my ($key,$value)= @_;
6689 2         6 $self->set_property($key,$value);
6690 2         6 return $self;
6691             }
6692              
6693             ####################
6694             # ColumnSpec
6695              
6696             sub colspec($)
6697             {
6698 2     2 0 239 my ($x)= @_;
6699 2 50       6 if (ref($x)) {
6700 2         5 return $x->colspec();
6701             }
6702             else {
6703 0         0 croak "ColumnSpec expected, but found non-reference (user types are not supported yet).";
6704             }
6705             }
6706              
6707             sub colspec_property($$$$)
6708             {
6709 4     4 0 29 my $self= pop @_;
6710 4         12 my ($name, $key, $value)= @_;
6711 4         12 $self->{name}{$key}= $name;
6712 4         9 $self->{option}{$key}= $value;
6713 4         26 return $self;
6714             }
6715              
6716             sub colspec_type_base($$)
6717             {
6718 0     0 0 0 my $self= pop @_;
6719 0         0 my ($base)= @_;
6720 0         0 type_base($base, $self->{datatype});
6721 0         0 return $self;
6722             }
6723              
6724             sub colspec_type_property($$$)
6725             {
6726 0     0 0 0 my $self= pop @_;
6727 0         0 my ($key, $value)= @_;
6728 0         0 type_property($key, $value, $self->{datatype});
6729 0         0 return $self;
6730             }
6731              
6732             sub colspec_type_basewlist($@)
6733             {
6734 0     0 0 0 my $self= pop @_;
6735 0         0 my ($base, @value)= @_;
6736 0         0 type_basewlist($base, @value, $self->{datatype});
6737 0         0 return $self;
6738             }
6739              
6740             sub colspec_type_length($$;$)
6741             {
6742 0     0 0 0 my $self= pop @_;
6743 0         0 my ($prec1, $prec2)= @_;
6744 0         0 type_length($prec1, $prec2, $self->{datatype});
6745 0         0 return $self;
6746             }
6747              
6748             sub colspec_type_largelength($$$;$)
6749             {
6750 0     0 0 0 my $self= pop @_;
6751 0         0 my ($coeff, $mul, $unit)= @_;
6752 0         0 type_largelength($coeff, $mul, $unit, $self->{datatype});
6753 0         0 return $self;
6754             }
6755              
6756             ####################
6757             # identifier interpolation, column and table:
6758              
6759             sub tabname($)
6760             {
6761 1     1 0 4 my ($x)= @_;
6762 1 50       4 if (ref($x)) {
    50          
6763 0         0 return $x->tabname;
6764             }
6765             elsif (defined $x) {
6766 1         3 return SQL::Yapp::TableName->obj(get_quote_id->($xlat_table->($x)));
6767             }
6768             else {
6769 0         0 croak "Error: Cannot use undef/NULL as a table name";
6770             }
6771             }
6772              
6773             # Schema-qualified names:
6774             sub schemaname1($$$)
6775             {
6776 122     122 0 167 my ($class,$xlat,$x)= @_;
6777 122 50       153 if (defined $x) {
6778 122         194 return $class->obj(get_quote_id->($xlat->($x)));
6779             }
6780             else {
6781 0         0 croak "Error: Cannot use undef/NULL as a table name";
6782             }
6783             }
6784              
6785             sub schemaname2($$$$)
6786             {
6787 2     2 0 7 my ($class,$xlat,$x,$y)= @_;
6788              
6789 2 100       10 if (ref($x)) { croak_no_ref($x); }
  1         3  
6790 1 50       2 if (ref($y)) { croak_no_ref($y); }
  0         0  
6791 1 50       3 croak "Error: Cannot use undef/NULL as an identifier"
6792             unless defined $y;
6793              
6794 1 50       6 return $class->obj(
6795             get_quote_id->(
6796             undef,
6797             (defined $x ? $xlat_schema->($x) : undef),
6798             $xlat->($y)));
6799             }
6800              
6801             sub schemaname3($$$$$)
6802             {
6803 3     3 0 7 my ($class,$xlat,$x,$y,$z)= @_;
6804 3 50       8 if (ref($x)) { croak_no_ref($x); }
  0         0  
6805 3 50       5 if (ref($y)) { croak_no_ref($y); }
  0         0  
6806 3 50       5 if (ref($z)) { croak_no_ref($z); }
  0         0  
6807 3 50       8 croak "Error: Cannot use undef/NULL as an identifier"
6808             unless defined $z;
6809              
6810 3 50       11 return $class->obj(
    50          
6811             get_quote_id->(
6812             (defined $x ? $xlat_catalog->($x) : undef),
6813             (defined $y ? $xlat_schema->($y) : undef),
6814             $xlat->($z)));
6815             }
6816              
6817              
6818             # Table:
6819             sub table1($)
6820             {
6821 124     124 0 4052 my ($x)= @_;
6822 124 100       245 return ref($x) ? $x->table1 : schemaname1('SQL::Yapp::Table', $xlat_table, $x);
6823             }
6824              
6825             sub table2($$)
6826             {
6827 2     2 0 551 my ($x,$y)= @_;
6828 2         6 return schemaname2('SQL::Yapp::Table', $xlat_table, $x, $y);
6829             }
6830              
6831             sub table3($$$)
6832             {
6833 3     3 0 261 my ($x,$y,$z)= @_;
6834 3         7 return schemaname3('SQL::Yapp::Table', $xlat_table, $x, $y, $z);
6835             }
6836              
6837              
6838             # Index:
6839             sub index1($)
6840             {
6841 0     0 0 0 my ($x)= @_;
6842 0 0       0 return ref($x) ? $x->index1 : schemaname1('SQL::Yapp::Index', $xlat_index, $x);
6843             }
6844              
6845             sub index2($$)
6846             {
6847 0     0 0 0 my ($x,$y)= @_;
6848 0         0 return schemaname2('SQL::Yapp::Index', $xlat_index, $x, $y);
6849             }
6850              
6851             sub index3($$$)
6852             {
6853 0     0 0 0 my ($x,$y,$z)= @_;
6854 0         0 return schemaname3('SQL::Yapp::Index', $xlat_index, $x, $y, $z);
6855             }
6856              
6857              
6858             # CharSet:
6859             sub charset1($)
6860             {
6861 2     2 0 7 my ($x)= @_;
6862 2 50       9 return ref($x) ? $x->charset1 : schemaname1('SQL::Yapp::CharSet', $xlat_charset, $x);
6863             }
6864              
6865             sub charset2($$)
6866             {
6867 0     0 0 0 my ($x,$y)= @_;
6868 0         0 return schemaname2('SQL::Yapp::CharSet', $xlat_charset, $x, $y);
6869             }
6870              
6871             sub charset3($$$)
6872             {
6873 0     0 0 0 my ($x,$y,$z)= @_;
6874 0         0 return schemaname3('SQL::Yapp::CharSet', $xlat_charset, $x, $y, $z);
6875             }
6876              
6877              
6878             # Collate:
6879             sub collate1($)
6880             {
6881 0     0 0 0 my ($x)= @_;
6882 0 0       0 return ref($x) ? $x->collate1 : schemaname1('SQL::Yapp::Collate', $xlat_collate, $x);
6883             }
6884              
6885             sub collate2($$)
6886             {
6887 0     0 0 0 my ($x,$y)= @_;
6888 0         0 return schemaname2('SQL::Yapp::Collate', $xlat_collate, $x, $y);
6889             }
6890              
6891             sub collate3($$$)
6892             {
6893 0     0 0 0 my ($x,$y,$z)= @_;
6894 0         0 return schemaname3('SQL::Yapp::Collate', $xlat_collate, $x, $y, $z);
6895             }
6896              
6897              
6898             # Constraint:
6899             sub constraint1($)
6900             {
6901 0     0 0 0 my ($x)= @_;
6902 0 0       0 return ref($x) ? $x->constraint1 : schemaname1('SQL::Yapp::Constraint', $xlat_constraint, $x);
6903             }
6904              
6905             sub constraint2($$)
6906             {
6907 0     0 0 0 my ($x,$y)= @_;
6908 0         0 return schemaname2('SQL::Yapp::Constraint', $xlat_constraint, $x, $y);
6909             }
6910              
6911             sub constraint3($$$)
6912             {
6913 0     0 0 0 my ($x,$y,$z)= @_;
6914 0         0 return schemaname3('SQL::Yapp::Constraint', $xlat_constraint, $x, $y, $z);
6915             }
6916              
6917              
6918             # Transliteration:
6919             sub transliteration1($)
6920             {
6921 0     0 0 0 my ($x)= @_;
6922 0 0       0 return ref($x) ? $x->transliteration1 : schemaname1('SQL::Yapp::Transliteration', $xlat_transliteration, $x);
6923             }
6924              
6925             sub transliteration2($$)
6926             {
6927 0     0 0 0 my ($x,$y)= @_;
6928 0         0 return schemaname2('SQL::Yapp::Transliteration', $xlat_transliteration, $x, $y);
6929             }
6930              
6931             sub transliteration3($$$)
6932             {
6933 0     0 0 0 my ($x,$y,$z)= @_;
6934 0         0 return schemaname3('SQL::Yapp::Transliteration', $xlat_transliteration, $x, $y, $z);
6935             }
6936              
6937              
6938             # Transcoding:
6939             sub transcoding1($)
6940             {
6941 0     0 0 0 my ($x)= @_;
6942 0 0       0 return ref($x) ? $x->transcoding1 : schemaname1('SQL::Yapp::Transcoding', $xlat_transcoding, $x);
6943             }
6944              
6945             sub transcoding2($$)
6946             {
6947 0     0 0 0 my ($x,$y)= @_;
6948 0         0 return schemaname2('SQL::Yapp::Transcoding', $xlat_transcoding, $x, $y);
6949             }
6950              
6951             sub transcoding3($$$)
6952             {
6953 0     0 0 0 my ($x,$y,$z)= @_;
6954 0         0 return schemaname3('SQL::Yapp::Transcoding', $xlat_transcoding, $x, $y, $z);
6955             }
6956              
6957              
6958             # Engine:
6959             sub engine1($)
6960             {
6961 1     1 0 239 my ($x)= @_;
6962 1 50       7 return ref($x) ? $x->engine1 : schemaname1('SQL::Yapp::Engine', $xlat_engine, $x);
6963             }
6964              
6965             sub engine2($$)
6966             {
6967 0     0 0 0 my ($x,$y)= @_;
6968 0         0 return schemaname2('SQL::Yapp::Engine', $xlat_engine, $x, $y);
6969             }
6970              
6971             sub engine3($$$)
6972             {
6973 0     0 0 0 my ($x,$y,$z)= @_;
6974 0         0 return schemaname3('SQL::Yapp::Engine', $xlat_engine, $x, $y, $z);
6975             }
6976              
6977              
6978             # Columns:
6979             sub colname($)
6980             {
6981 10     10 0 25 my ($x)= @_;
6982 10 50       30 if (ref($x)) {
    50          
6983 0         0 return $x->colname;
6984             }
6985             elsif (defined $x) {
6986 10         21 return SQL::Yapp::ColumnName->obj(get_quote_id->($xlat_column->($x)));
6987             }
6988             else {
6989 0         0 croak "Error: Cannot use undef/NULL as a column name";
6990             }
6991             }
6992              
6993             sub column1($)
6994             {
6995 209     209 0 18078 my ($x)= @_;
6996 209 100       437 if (ref($x)) {
    50          
6997 4         13 return $x->column1;
6998             }
6999             elsif (defined $x) {
7000 205         369 return SQL::Yapp::Column->obj(get_quote_id->($xlat_column->($x)));
7001             }
7002             else {
7003 0         0 croak "Error: Cannot use undef/NULL as an identifier";
7004             }
7005             }
7006              
7007             sub column1_single($) #internal
7008             {
7009 25     25 0 34 my ($x)= @_;
7010 25 100       44 if (ref($x)) {
    50          
7011 1         8 return $x->column1_single;
7012             }
7013             elsif (defined $x) {
7014 24         35 return get_quote_id->($xlat_column->($x));
7015             }
7016             else {
7017 0         0 croak "Error: Cannot use undef/NULL as an identifier";
7018             }
7019             }
7020              
7021             sub column2($$)
7022             {
7023 25     25 0 1748 my ($x,$y)= @_;
7024 25         33 return SQL::Yapp::Column->obj(table1($x).'.'.column1_single($y));
7025             }
7026              
7027             sub column3($$$)
7028             {
7029 0     0 0 0 my ($x,$y,$z)= @_;
7030 0         0 return SQL::Yapp::Column->obj(table2($x,$y).'.'.column1_single($z));
7031             }
7032              
7033             sub column4($$$$)
7034             {
7035 0     0 0 0 my ($w,$x,$y,$z)= @_;
7036 0         0 return SQL::Yapp::Column->obj(table3($w,$x,$y).'.'.column1_single($z));
7037             }
7038              
7039             # Generated with mkidentn.pl:
7040 0     0 0 0 sub table1_n($) { map { table1 ($_ ) } @{ $_[0] } }
  0         0  
  0         0  
7041 0     0 0 0 sub table2_1n($$) { map { table2 ($_[0], $_ ) } @{ $_[1] } }
  0         0  
  0         0  
7042 0     0 0 0 sub table2_n1($$) { map { table2 ($_ , $_[1]) } @{ $_[0] } }
  0         0  
  0         0  
7043 0     0 0 0 sub table2_nn($$) { map { table2_1n ($_ , $_[1]) } @{ $_[0] } }
  0         0  
  0         0  
7044 0     0 0 0 sub table3_11n($$$) { map { table3 ($_[0], $_[1], $_ ) } @{ $_[2] } }
  0         0  
  0         0  
7045 0     0 0 0 sub table3_1n1($$$) { map { table3 ($_[0], $_ , $_[2]) } @{ $_[1] } }
  0         0  
  0         0  
7046 0     0 0 0 sub table3_1nn($$$) { map { table3_11n ($_[0], $_ , $_[2]) } @{ $_[1] } }
  0         0  
  0         0  
7047 0     0 0 0 sub table3_n11($$$) { map { table3 ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7048 0     0 0 0 sub table3_n1n($$$) { map { table3_11n ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7049 0     0 0 0 sub table3_nn1($$$) { map { table3_1n1 ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7050 0     0 0 0 sub table3_nnn($$$) { map { table3_1nn ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7051              
7052 0     0 0 0 sub column1_n($) { map { column1 ($_ ) } @{ $_[0] } }
  0         0  
  0         0  
7053 4     4 0 5 sub column2_1n($$) { map { column2 ($_[0], $_ ) } @{ $_[1] } }
  8         11  
  4         8  
7054 0     0 0 0 sub column2_n1($$) { map { column2 ($_ , $_[1]) } @{ $_[0] } }
  0         0  
  0         0  
7055 2     2 0 504 sub column2_nn($$) { map { column2_1n ($_ , $_[1]) } @{ $_[0] } }
  4         11  
  2         8  
7056 0     0 0 0 sub column3_11n($$$) { map { column3 ($_[0], $_[1], $_ ) } @{ $_[2] } }
  0         0  
  0         0  
7057 0     0 0 0 sub column3_1n1($$$) { map { column3 ($_[0], $_ , $_[2]) } @{ $_[1] } }
  0         0  
  0         0  
7058 0     0 0 0 sub column3_1nn($$$) { map { column3_11n ($_[0], $_ , $_[2]) } @{ $_[1] } }
  0         0  
  0         0  
7059 0     0 0 0 sub column3_n11($$$) { map { column3 ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7060 0     0 0 0 sub column3_n1n($$$) { map { column3_11n ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7061 0     0 0 0 sub column3_nn1($$$) { map { column3_1n1 ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7062 0     0 0 0 sub column3_nnn($$$) { map { column3_1nn ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7063 0     0 0 0 sub column4_111n($$$$) { map { column4 ($_[0], $_[1], $_[2], $_ ) } @{ $_[3] } }
  0         0  
  0         0  
7064 0     0 0 0 sub column4_11n1($$$$) { map { column4 ($_[0], $_[1], $_ , $_[3]) } @{ $_[2] } }
  0         0  
  0         0  
7065 0     0 0 0 sub column4_11nn($$$$) { map { column4_111n ($_[0], $_[1], $_ , $_[3]) } @{ $_[2] } }
  0         0  
  0         0  
7066 0     0 0 0 sub column4_1n11($$$$) { map { column4 ($_[0], $_ , $_[2], $_[3]) } @{ $_[1] } }
  0         0  
  0         0  
7067 0     0 0 0 sub column4_1n1n($$$$) { map { column4_111n ($_[0], $_ , $_[2], $_[3]) } @{ $_[1] } }
  0         0  
  0         0  
7068 0     0 0 0 sub column4_1nn1($$$$) { map { column4_11n1 ($_[0], $_ , $_[2], $_[3]) } @{ $_[1] } }
  0         0  
  0         0  
7069 0     0 0 0 sub column4_1nnn($$$$) { map { column4_11nn ($_[0], $_ , $_[2], $_[3]) } @{ $_[1] } }
  0         0  
  0         0  
7070 0     0 0 0 sub column4_n111($$$$) { map { column4 ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7071 0     0 0 0 sub column4_n11n($$$$) { map { column4_111n ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7072 0     0 0 0 sub column4_n1n1($$$$) { map { column4_11n1 ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7073 0     0 0 0 sub column4_n1nn($$$$) { map { column4_11nn ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7074 0     0 0 0 sub column4_nn11($$$$) { map { column4_1n11 ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7075 0     0 0 0 sub column4_nn1n($$$$) { map { column4_1n1n ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7076 0     0 0 0 sub column4_nnn1($$$$) { map { column4_1nn1 ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7077 0     0 0 0 sub column4_nnnn($$$$) { map { column4_1nnn ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7078              
7079             ####################
7080             # stmt interpolation:
7081              
7082             sub stmt($)
7083             {
7084 3     3 0 15 my ($x)= @_;
7085 3 50       6 if (ref($x)) {
7086 3         8 return $x->stmt;
7087             }
7088             else {
7089 0         0 croak "Error: Expected 'Stmt' object, but found: ".my_dumper($x);
7090             }
7091             }
7092              
7093             sub subquery($)
7094             {
7095 1     1 0 2 my ($x1)= @_;
7096 1         5 my $x= SQL::Yapp::Stmt->obj($x1);
7097 1         6 return $x->subquery;
7098             }
7099              
7100             ####################
7101             # expr interpolation:
7102              
7103             sub exprparen($)
7104             {
7105 70     70 0 1297 my ($x)= @_;
7106 70 100       99 if (ref($x)) {
7107 5 50       13 die Dumper($x) if ref($x) eq 'HASH';
7108 5 50       14 die Dumper($x) if ref($x) eq 'ARRAY';
7109 5 50       10 die Dumper($x) if ref($x) eq 'CODE';
7110 5 50       10 die Dumper($x) if ref($x) eq 'SCALAR';
7111 5         17 return $x->exprparen;
7112             }
7113             else {
7114 65         90 return SQL::Yapp::Expr->obj(get_quote_val->($x)); # raw perl scalar: quote as value, no parens
7115             }
7116             }
7117              
7118             sub expr($)
7119             {
7120 153     153 0 4932 my ($x)= @_;
7121 153 100       221 if (ref($x)) {
7122 21 50       68 confess 'Error: Trying to invoke $x->expr() on unblessed reference $x ".
7123             "(maybe missing nested sqlExpr{...} inside a block, or ".
7124             "additional () around {} interpolation?)'
7125             unless blessed($x);
7126 21         58 return $x->expr;
7127             }
7128             else {
7129 132         203 return SQL::Yapp::Expr->obj(get_quote_val->($x)); # raw perl scalar: quote as value
7130             }
7131             }
7132              
7133             sub expr_or_check($)
7134             {
7135 5     5 0 10 my ($x)= @_;
7136 5 100       14 if (ref($x)) {
7137 3         7 return $x->expr_or_check;
7138             }
7139             else {
7140 2         5 return SQL::Yapp::Expr->obj(get_quote_val->($x)); # raw perl scalar: quote as value
7141             }
7142             }
7143              
7144             sub exprparen_hash(\%)
7145             {
7146 3     3 0 6 my ($x)= @_;
7147             return map {
7148 3         16 my $n= $_;
  6         11  
7149 6         11 my $e= $x->{$n};
7150 6 100 66     41 (blessed($e) && $e->isa('SQL::Yapp::Check') ?
7151             '('.get_quote_id->($n).' '.$e->check.')'
7152             : '('.get_quote_id->($n).' = '.exprparen($e).')'
7153             )
7154             }
7155             sort keys %$x;
7156             }
7157              
7158             sub expr_hash(\%)
7159             {
7160 4     4 0 11 my ($x)= @_;
7161             return map {
7162 4         14 my $n= $_;
  7         11  
7163 7         11 my $e= $x->{$n};
7164 7 50 33     24 (blessed($e) && $e->isa('SQL::Yapp::Check') ?
7165             '('.get_quote_id->($n).' '.$e->check.')'
7166             : SQL::Yapp::Infix->obj('=', get_quote_id->($n), exprparen($e))
7167             )
7168             }
7169             sort keys %$x;
7170             }
7171              
7172             ####################
7173             # order interpolation:
7174              
7175             sub asc($)
7176             {
7177 9     9 0 275 my ($x)= @_;
7178 9 100       18 if (ref($x)) {
    50          
7179 4         11 return $x->asc;
7180             }
7181             elsif (defined $x) {
7182 5         9 return column1($x);
7183             }
7184             else {
7185 0         0 return NULL;
7186             }
7187             }
7188              
7189             sub desc($)
7190             {
7191 10     10 0 40 my ($x)= @_;
7192 10 100       21 if (ref($x)) {
    50          
7193 6         13 return $x->desc;
7194             }
7195             elsif (defined $x) {
7196 4         5 return SQL::Yapp::Desc->obj(column1($x));
7197             }
7198             else {
7199 0         0 return NULL;
7200             }
7201             }
7202              
7203             ####################
7204             # table option:
7205              
7206             sub tableopt($)
7207             {
7208 2     2 0 7 my ($x)= @_;
7209 2 50       5 if (ref($x)) {
7210 2         4 return $x->tableopt;
7211             }
7212             else {
7213 0         0 croak "Error: Expected 'TableOption' object, but found: ".my_dumper($x);
7214             }
7215             }
7216              
7217             ####################
7218             # join interpolation:
7219              
7220             sub joinclause($)
7221             {
7222 4     4 0 9 my ($x)= @_;
7223 4 50       8 if (ref($x)) {
7224 4         10 return $x->joinclause;
7225             }
7226             else {
7227 0         0 croak "Error: Expected 'Join' object, but found: ".my_dumper($x);
7228             }
7229             }
7230              
7231             ####################
7232             # limit interpolation:
7233              
7234             sub limit_number($)
7235             {
7236 14     14 0 56 my ($x)= @_;
7237 14 50       47 if (ref($x)) {
    50          
7238 0         0 return $x->limit_number;
7239             }
7240             elsif (looks_like_number $x) {
7241 14         43 return $x;
7242             }
7243             else {
7244 0         0 croak "Error: Expected number or ?, but found: ".my_dumper($x);
7245             }
7246             }
7247              
7248             sub limit($$)
7249             {
7250 2     2 0 8 my ($cnt, $offset)= @_;
7251              
7252             # FIXME: if dialect is 'std' (or maybe 'std2008'), produce OFFSET/FETCH
7253             # clause (SQL-2008).
7254 2 100       6 if (defined $cnt) {
7255 1 50       3 if (defined $offset) {
7256 1         3 return " LIMIT ".limit_number($cnt)." OFFSET ".limit_number($offset);
7257             }
7258             else {
7259 0         0 return " LIMIT ".limit_number($cnt);
7260             }
7261             }
7262             else {
7263 1 50       4 if (defined $offset) {
7264 1 50       3 if ($write_dialect eq 'postgresql') {
7265 0         0 return " LIMIT ALL OFFSET ".limit_number($offset);
7266             }
7267             else {
7268 1         2 return " LIMIT ${\LARGE_LIMIT_CNT} OFFSET ".limit_number($offset);
  1         4  
7269             }
7270             }
7271             else {
7272 0         0 return '';
7273             }
7274             }
7275             }
7276              
7277             ####################
7278             # case:
7279              
7280             sub whenthen($$)
7281             {
7282 6     6 0 10 my ($expr, $then)= @_;
7283 6         50 return 'WHEN '.$expr.' THEN '.$then;
7284             }
7285              
7286             sub caseswitch($$@)
7287             {
7288             #my ($switchval, $default, @whenthen)
7289 8 100   8 0 19 if (scalar(@_) == 2) { # @whenthen is empty => always use default
7290 2         7 return $_[1]; # return default
7291             }
7292             return
7293 6         45 join(' ',
7294             'CASE',
7295             $_[0],
7296             @_[2..$#_], # @whenthen
7297             'ELSE', # always generate default, it's easier.
7298             $_[1],
7299             'END'
7300             );
7301             }
7302              
7303             sub casecond($@)
7304             {
7305             #my ($default, @whenthen)
7306 0 0   0 0   if (scalar(@_) == 1) { # @whenthen is empty => always use default
7307 0           return $_[0]; # return default
7308             }
7309             return
7310 0           join(' ',
7311             'CASE',
7312             @_[1..$#_], # @whenthen
7313             'ELSE', # always generate default, it's easier.
7314             $_[0],
7315             'END'
7316             );
7317             }
7318              
7319             1;
7320              
7321             ######################################################################
7322             ######################################################################
7323             ######################################################################
7324              
7325             __END__