File Coverage

blib/lib/SQL/Yapp.pm
Criterion Covered Total %
statement 2376 2938 80.8
branch 712 1106 64.3
condition 147 293 50.1
subroutine 519 722 71.8
pod 0 298 0.0
total 3754 5357 70.0


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