File Coverage

blib/lib/SQL/Yapp.pm
Criterion Covered Total %
statement 2374 2937 80.8
branch 710 1108 64.0
condition 146 293 49.8
subroutine 519 722 71.8
pod 0 298 0.0
total 3749 5358 69.9


line stmt bran cond sub pod time code
1             #! /usr/bin/perl -w
2              
3             package SQL::Yapp;
4              
5 5     5   101448 use strict;
  5         8  
  5         143  
6 5     5   23 use warnings;
  5         5  
  5         145  
7 5     5   19 use vars qw($VERSION @EXPORT_OK);
  5         9  
  5         288  
8 5     5   20 use base qw(Exporter);
  5         5  
  5         435  
9 5     5   18 use Carp qw(longmess carp croak confess);
  5         5  
  5         299  
10 5     5   2885 use Hash::Util qw(lock_keys lock_hash);
  5         10177  
  5         22  
11 5     5   347 use Scalar::Util qw(looks_like_number blessed);
  5         6  
  5         175  
12 5     5   539 use Data::Dumper;
  5         6108  
  5         173  
13 5     5   2284 use Filter::Simple;
  5         80715  
  5         25  
14 5     5   222 use Text::Balanced;
  5         7  
  5         354  
15              
16             require v5.8;
17              
18             $VERSION= 2.001;
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   17 use constant SQL_MARK => "\0__SQL__";
  5         5  
  5         326  
49 5     5   17 use constant COMMA_STR => ', ';
  5         2  
  5         184  
50 5     5   14 use constant LARGE_LIMIT_CNT => '18446744073709551615';
  5         5  
  5         168  
51              
52 5     5   18 use constant NOT_IN_LIST => 0;
  5         7  
  5         175  
53 5     5   15 use constant IN_LIST => 1;
  5         5  
  5         165  
54              
55 5     5   365 use constant NO_PARENS => 0;
  5         8  
  5         208  
56 5     5   17 use constant PARENS => 1;
  5         4  
  5         170  
57              
58 5     5   15 use constant NO_SHIFT => 0;
  5         4  
  5         149  
59 5     5   14 use constant SHIFT => 1;
  5         5  
  5         9937  
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 18 my $var= shift;
322 20         18 my $r= $$var;
323 20 50       42 ($$var)= @_ if scalar(@_);
324 20         40 return $;
325             }
326              
327             sub get_dbh()
328             {
329 0     0 0 0 return $get_dbh->();
330             }
331              
332             sub dbh(;&)
333             {
334 1     1 0 2 get_set (\$get_dbh, @_);
335 1 50       1 if ($get_dbh) {
336 1     0   3 $quote_id_default= sub(@) { $get_dbh->()->quote_identifier(@_); };
  0         0  
337 1     0   3 $quote_val_default= sub($) { $get_dbh->()->quote($_[0]); };
  0         0  
338             }
339             else {
340 0         0 $quote_id_default= undef;
341 0         0 $quote_val_default= undef;
342             }
343             }
344              
345 4     4 0 11 sub quote_identifier(;&) { get_set (\$quote_id, @_); }
346 4     4 0 10 sub quote(;&) { get_set (\$quote_val, @_); }
347 2     2 0 4 sub xlat_catalog(;&) { get_set (\$xlat_catalog, @_); }
348 2     2 0 3 sub xlat_schema(;&) { get_set (\$xlat_schema, @_); }
349 4     4 0 12 sub xlat_table(;&) { get_set (\$xlat_table, @_); }
350 2     2 0 6 sub xlat_column(;&) { get_set (\$xlat_column, @_); }
351 0     0 0 0 sub xlat_charset(;&) { get_set (\$xlat_charset, @_); }
352 0     0 0 0 sub xlat_collate(;&) { get_set (\$xlat_collate, @_); }
353 0     0 0 0 sub xlat_constraint(;&) { get_set (\$xlat_constraint, @_); }
354 0     0 0 0 sub xlat_index(;&) { get_set (\$xlat_index, @_); }
355 0     0 0 0 sub xlat_transcoding(;&) { get_set (\$xlat_transcoding, @_); }
356 0     0 0 0 sub xlat_transliteration(;&) { get_set (\$xlat_transliteration, @_); }
357 0     0 0 0 sub xlat_engine(;&) { get_set (\$xlat_engine, @_); }
358              
359 0     0 0 0 sub check_identifier(;&) { get_set (\$check_identifier, @_); }
360 0     0 0 0 sub runtime_check(;$) { get_set (\$runtime_check, @_); }
361              
362 1     1 0 3 sub sql_marker(;$) { get_set (\$sql_marker, @_); } # used only internally
363              
364 2     2 0 6 sub catalog_prefix($) { my ($p)= @_; xlat_catalog { $p.$_[0] }; }
  2     2   7  
  2         6  
365 2     2 0 4 sub schema_prefix($) { my ($p)= @_; xlat_schema { $p.$_[0] }; }
  2     2   6  
  2         6  
366 124     124 0 230 sub table_prefix($) { my ($p)= @_; xlat_table { $p.$_[0] }; }
  4     4   5  
  4         15  
367 68     68 0 117 sub column_prefix($) { my ($p)= @_; xlat_column { $p.$_[0] }; }
  2     2   543  
  2         9  
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 724 my ($s)= @_;
406 9 50       28 croak "Unknown dialect: write_dialect=$s" unless $dialect{$s};
407 9         14 $write_dialect= $s;
408             }
409              
410             sub dialect($)
411             {
412 0     0 0 0 my ($s)= @_;
413 0         0 read_dialect($s);
414 0         0 write_dialect($s);
415             }
416              
417             ######################################################################
418             # Init
419              
420             my %import_handler_nonref= (
421             'marker' => \&sql_marker,
422             'catalog_prefix' => \&catalog_prefix,
423             'schema_prefix' => \&schema_prefix,
424             'table_prefix' => \&table_prefix,
425             'column_prefix' => \&column_prefix,
426             'constraint_prefix' => \&constraint_prefix,
427             'debug' => \&debug,
428             'read_dialect' => \&read_dialect,
429             'write_dialect' => \&write_dialect,
430             'dialect' => \&dialect,
431             );
432             my %import_handler_bool= (
433             'runtime_check' => \&runtime_check,
434             );
435             my %import_handler_ref= (
436             'dbh' => \&dbh,
437             'quote' => \"e,
438             'quote_identifier' => \"e_identifier,
439             'xlat_catalog' => \&xlat_catalog,
440             'xlat_schema' => \&xlat_schema,
441             'xlat_table' => \&xlat_table,
442             'xlat_column' => \&xlat_column,
443             'xlat_charset' => \&xlat_charset,
444             'xlat_collate' => \&xlat_collate,
445             'xlat_constraint' => \&xlat_constraint,
446             'xlat_index' => \&xlat_index,
447             'xlat_transliteration' => \&xlat_transliteration,
448             'xlat_transcoding' => \&xlat_transcoding,
449             'xlat_engine' => \&xlat_engine,
450             'check_identifier' => \&check_identifier,
451             );
452              
453             sub type_spec()
454             {
455             return (
456             'DOUBLE PRECISION' => 'INT',
457             'REAL' => 'INT',
458             'BIGINT' => 'INT',
459             'SMALLINT' => 'INT',
460             'INT' => {
461             },
462              
463             # numbers with 0 or 1 precision marker:
464             'FLOAT' => {
465             prec1 => 1,
466             },
467              
468             # numbers with 0, 1, or 2 precision numbers:
469             'NUMERIC' => 'DECIMAL',
470             'DECIMAL' => {
471             prec1 => 1,
472             prec2 => 1,
473             },
474              
475             # character strings:
476             'VARCHAR' => 'CHARACTER',
477             'CHARACTER' => {
478             prec1 => 1,
479             charset => 1,
480             collate => 1,
481             },
482              
483             # clobs:
484             'CLOB' => {
485             prec1 => 1,
486             prec_mul => 1,
487             prec_unit => 1,
488             charset => 1,
489             collate => 1,
490             },
491              
492             # nchar:
493             'NCHAR VARYING' => 'NCHAR',
494             'NCHAR' => {
495             prec1 => 1,
496             collate => 1,
497             },
498              
499             # nclobs:
500             'NCLOB' => {
501             prec1 => 1,
502             prec_mul => 1,
503             prec_unit => 1,
504             collate => 1,
505             },
506              
507             # binary strings:
508             'VARBINARY' => 'BINARY', # not standard
509             'BINARY' => {
510             prec1 => 1,
511             },
512              
513             # blobs:
514             'BLOB' => {
515             prec1 => 1,
516             prec_mul => 1,
517             prec_unit => 1,
518             },
519              
520             # simple types without further attributes or lengths:
521             'SERIAL' => 'BOOLEAN', # column spec, but handled as type for simplicity reasons
522             'BOOLEAN' => {
523             },
524              
525             # date/time:
526             'DATE' => 'TIME',
527             'TIMESTAMP' => 'TIME',
528             'TIME' => {
529             timezone => 1
530             },
531              
532             # Dialects come last because they may redefine above settings:
533             # If two dialects are contracting, you must find a common solution
534             # and put it at the end of this list:
535             ($read_dialect{mysql} ?
536             (
537             'SMALLINT' => 'INT',
538             'BIGINT' => 'INT',
539             'TINYINT' => 'INT',
540             'MEDIUMINT' => 'INT',
541             'BIT' => 'INT',
542             'BIT VARYING' => 'INT',
543             'FLOAT' => 'INT',
544             'INT' => {
545             prec1 => 1,
546             zerofill => 1,
547             sign => 1,
548             },
549              
550             'FLOAT' => 'NUMERIC',
551             'DECIMAL' => 'NUMERIC',
552             'REAL' => 'NUMERIC',
553             'DOUBLE' => 'NUMERIC',
554             'NUMERIC' => {
555             prec1 => 1,
556             prec2 => 1,
557             zerofill => 1,
558             sign => 1,
559             },
560              
561             'DATETIME' => 'TIME',
562             'YEAR' => 'TIME',
563              
564             'TINYBLOB' => 'BINARY',
565             'MEDIUMBLOB' => 'BINARY',
566             'LONGBLOB' => 'BINARY',
567              
568             'TINYTEXT' => 'CHARACTER',
569             'MEDIUMTEXT' => 'CHARACTER',
570             'LONGTEXT' => 'CHARACTER',
571             'TEXT' => 'CHARACTER',
572              
573             'TINYTEXT BINARY' => 'CHARACTER',
574             'MEDIUMTEXT BINARY' => 'CHARACTER',
575             'LONGTEXT BINARY' => 'CHARACTER',
576             'TEXT BINARY' => 'CHARACTER',
577              
578             'ENUM' => {
579             value_list => 1,
580             charset => 1,
581             collate => 1,
582             },
583              
584             'SET' => {
585             value_list => 1,
586             charset => 1,
587             collate => 1,
588             },
589             )
590             : ()
591             ),
592             ($read_dialect{postgresql} ?
593             (
594             'BYTEA' => 'BINARY',
595             'INT2' => 'INT',
596             'INT4' => 'INT',
597             'INT8' => 'INT',
598             'POINT' => 'BOOLEAN',
599             'LINE' => 'BOOLEAN',
600             'LSEG' => 'BOOLEAN',
601             'BOX' => 'BOOLEAN',
602             'PATH' => 'BOOLEAN',
603             'POLYGON' => 'BOOLEAN',
604             'CIRCLE' => 'BOOLEAN',
605             'MONEY' => 'BOOLEAN',
606             'IRDR' => 'BOOLEAN',
607             'INET' => 'BOOLEAN',
608             'MACADDR' => 'BOOLEAN',
609             'UUID' => 'BOOLEAN',
610             'TEXT' => 'CHARACTER',
611             'SERIAL4' => 'SERIAL',
612             'SERIAL8' => 'SERIAL',
613             'BIGSERIAL' => 'SERIAL',
614             )
615             : ()
616             ),
617             ($read_dialect{oracle} ?
618             (
619 5 50   5 0 320 'NUMBER' => 'NUMERIC'
    50          
    50          
620             )
621             : ()
622             ),
623             );
624             }
625              
626             sub import
627             {
628             my ($pack, @opt)= @_;
629             my @super_param= ();
630             my $i=0;
631             while ($i < scalar(@opt)) {
632             my $k= $opt[$i];
633             if ($i+1 < scalar(@opt)) {
634             my $v= $opt[$i+1];
635             if (my $handler= $import_handler_nonref{$k}) {
636             $handler->($v);
637             $i++;
638             }
639             elsif ($v eq '0' || $v eq '1') {
640             if (my $handler= $import_handler_bool{$k}) {
641             $handler->($v);
642             $i++;
643             }
644             else {
645             croak "Error: Unrecognised package option for ".__PACKAGE__.": $k\n";
646             }
647             }
648             elsif (ref($v)) {
649             if (my $handler= $import_handler_ref{$k}) {
650             $handler->($v);
651             $i++;
652             }
653             else {
654             croak "Error: Unrecognised package option for ".__PACKAGE__.": $k\n";
655             }
656             }
657             else {
658             push @super_param, $k;
659             }
660             }
661             else {
662             push @super_param, $k;
663             }
664             $i++;
665             }
666              
667             &Exporter::import($pack,@super_param);
668              
669             %type_spec= type_spec();
670             }
671              
672             ######################################################################
673             # Tools
674              
675             sub my_dumper($)
676             {
677 1     1 0 2 my ($x)= @_;
678              
679 1         10 my $d= Data::Dumper->new([$x],['x']);
680 1         32 $d->Terse(1);
681 1         11 $d->Purity(1);
682 1         6 $d->Indent(1);
683              
684 1         9 my $s= $d->Dump;
685 1 50       188 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 4 my $i= 2;
697 3         6 my @mess= ();
698 3         22 while (my ($pack, $file, $line, $function)= caller($i)) {
699 61         116 push @mess, "\t$file:$line: ${pack}::${function}\n";
700 61         185 $i++;
701             }
702 3         69 return "Call Stack:\n".join('', reverse @mess);
703             }
704              
705             sub my_confess(;$)
706             {
707 3   50 3 0 7 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 487 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 903 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   22 use constant ASSOC_NON => undef;
  5         6  
  5         249  
761 5     5   17 use constant ASSOC_LEFT => -1;
  5         6  
  5         232  
762 5     5   18 use constant ASSOC_RIGHT => +1;
  5         4  
  5         25157  
763              
764             sub make_op($$;%)
765             {
766 454     454 0 456 my ($value, $type, %opt)= @_;
767 454   66     1102 my $read_value= $opt{read_value} || $value;
768 454   66     810 my $read_type= $opt{read_type} || $type;
769             my $result= {
770             read_value => $read_value,
771             value => $value,
772             value2 => $opt{value2}, # for infix3
773             read_type => $read_type, # how to parse?
774             type => $type, # how to print?
775             result0 => $opt{result0}, # for 'infix()' operators invoked with 0 arguments
776             # if undef => error to invoke with 0 arguments
777             prec => $opt{prec},
778             assoc => $opt{assoc},
779             rhs => $opt{rhs} || 'expr',
780             rhs_map => $opt{rhs_map} || {},
781             comparison => $opt{comparison}, # for checking ANY/SOME and ALL
782             dialect => $opt{dialect} || {},
783             accept => $opt{accept},
784             allow_when => $opt{allow_when},
785 454   100     3344 };
      50        
      100        
786 454         669 lock_hash %$result;
787 454         6817 return $result;
788             }
789              
790             sub declare_op($$;%)
791             {
792 380     380 0 451 my ($value, $type, %opt)= @_;
793 380         472 my $result= make_op($value, $type, %opt);
794 380         1034 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 2172 my ($s)= @_;
1130 2824         2383 $s =~ s/([\\\'])/\\$1/g;
1131 2824         7933 return "'$s'";
1132             }
1133              
1134             sub quote_perl($)
1135             {
1136 3185     3185 0 2330 my ($s)= @_;
1137 3185 100       4232 return 'undef' unless defined $s;
1138 2824 50       5405 return ($s =~ /[\x00-\x1f\x7f\']/) ? double_quote_perl($s) : single_quote_perl($s);
1139             }
1140              
1141             sub skip_ws($)
1142             {
1143 3517     3517 0 2546 my ($lx)= @_;
1144 3517         2640 my $s= $lx->{text_p};
1145              
1146 3517         2408 for(;;) {
1147 6215 100       9798 if ($$s =~ /\G\n/gc) { # count lines
1148 427         411 $lx->{line}++;
1149 427         439 next;
1150             }
1151 5788 100       11609 next if $$s =~ /\G[^\n\S]+/gc; # other space but newline
1152 3536 100       4922 next if $$s =~ /\G\#[^\n]*/gc; # comments
1153 3517         3518 last;
1154             }
1155             }
1156              
1157             sub token_new($$;$%)
1158             {
1159 3182     3182 0 5540 my ($lx, $kind, $value, %opt)= @_;
1160 3182 50       4093 my_confess unless $kind;
1161             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         15765 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 3182         3442 };
1175 3182         6525 lock_keys %$t;
1176 3182         18939 return $t;
1177             }
1178              
1179             sub token_describe($)
1180             {
1181 1     1 0 1 my ($t)= @_;
1182              
1183 1         2 my %opt= ();
1184 1         2 for my $key(qw(value str prec)) {
1185 3 100       234 if (defined $t->{$key}) {
1186 1         5 $opt{$key}= $t->{$key};
1187             }
1188             }
1189 1         2 for my $key(qw(perltype type)) {
1190 2 100       4 if ($t->{$key}) {
1191 1         2 $opt{$key}= $t->{$key};
1192             }
1193             }
1194              
1195 1 50       3 if (scalar(keys %opt)) {
1196             return "$t->{kind} (".
1197             join(", ",
1198             map {
1199 1         5 my $k= $_;
  2         2  
1200 2         4 "$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 3 my ($lx, $value, $expl)= @_;
1214 1         3 return token_new ($lx, 'error', $value, str => $expl, error => 1);
1215             }
1216              
1217             sub syn_new($$$)
1218             {
1219 2135     2135 0 2834 my ($lx, $type, $name)= @_;
1220 2135         2772 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 682 my ($lx, $interpol, $value, $type, $perltype)= @_;
1228 517         1020 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 63 my ($lx, $interpol)= @_;
1237 62         78 my $s= $lx->{text_p};
1238              
1239             # Text::Balanced actually honours and updates pos($$s), so we can
1240             # interface directly:
1241 62         172 my ($ex)= Text::Balanced::extract_codeblock($$s, '{}()[]');
1242             return error_new($lx, 'codeblock', $@->{error})
1243 62 50       22193 if $@;
1244              
1245 62         117 $lx->{line}+= ($ex =~ tr/\n//);
1246 62         162 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 217 my ($lx, $interpol, $perltype)= @_;
1253 170         155 my $s= $lx->{text_p};
1254              
1255 170         421 my ($ex)= Text::Balanced::extract_variable($$s);
1256             return error_new($lx, 'variable', $@->{error})
1257 170 50       29968 if $@;
1258              
1259 170         324 $lx->{line}+= ($ex =~ tr/\n//);
1260 170         291 return interpol_new ($lx, $interpol, $ex, 'variable', $perltype);
1261             }
1262              
1263             sub token_scan_delimited($$$)
1264             {
1265 50     50 0 88 my ($lx, $interpol, $delim)= @_;
1266 50         62 my $s= $lx->{text_p};
1267              
1268 50         145 my ($ex)= Text::Balanced::extract_delimited($$s, $delim);
1269             return error_new($lx, 'delimited', $@->{error})
1270 50 50       3516 if $@;
1271              
1272 50         90 $lx->{line}+= ($ex =~ tr/\n//);
1273 50         89 return interpol_new ($lx, $interpol, $ex, 'string', 'scalar');
1274             }
1275              
1276             sub token_num_new($$$)
1277             {
1278 235     235 0 372 my ($lx, $interpol, $value)= @_;
1279 235   50     816 return interpol_new ($lx, $interpol || 'Expr', $value, 'num', 'scalar');
1280             }
1281              
1282             sub ident_new($$)
1283             {
1284 529     529 0 757 my ($lx, $value)= @_;
1285 529         665 return token_new ($lx, 'ident', $value, perltype => 'scalar');
1286             }
1287              
1288             sub keyword_new($$) # either syn or function
1289             {
1290 737     737 0 627 my ($lx, $name)= @_;
1291 737 100       1111 if ($reserved{$name}) {
1292 666         782 return syn_new($lx, 'reserved', $name);
1293             }
1294             else {
1295 71         96 return syn_new($lx, 'keyword', $name);
1296             }
1297             }
1298              
1299             sub replace_synonym($)
1300             {
1301 1251     1251 0 1005 my ($name)= @_;
1302 1251         2321 while (my $syn= $synonym{$name}) {
1303 7         17 $name= $syn;
1304             }
1305 1251         1520 return $name;
1306             }
1307              
1308             sub multi_token_new($$)
1309             {
1310 909     909 0 1494 my ($lx, $name)= @_;
1311 909         727 my $s= $lx->{text_p};
1312              
1313 909         991 $name= replace_synonym($name);
1314 909 100       1497 if (my $tree= $multi_token{$name}) {
1315 172         135 SUB_TOKEN: for (;;) {
1316 327         318 skip_ws($lx);
1317              
1318 327         264 my $p= pos($$s);
1319 327 100       691 last SUB_TOKEN unless $$s =~ /\G ([A-Z][A-Z_0-9]*)\b /gcsx;
1320 187         205 my $sub_name= $1;
1321              
1322 187         190 $sub_name= replace_synonym($sub_name);
1323 187         229 $tree= $tree->{$sub_name};
1324 187 100       278 unless ($tree) {
1325 32         55 pos($$s)= $p; # unscan rejected keyword
1326 32         53 last SUB_TOKEN;
1327             }
1328              
1329 155         207 $name.= " $sub_name";
1330 155         156 $name= replace_synonym($name);
1331             }
1332 172         216 return syn_new ($lx, 'keyword', $name); # never a function, always a keyword
1333             }
1334             else {
1335 737         900 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 2834 my ($lx, $interpol)= @_;
1345 3190         2409 my $s= $lx->{text_p};
1346              
1347 3190         4097 skip_ws($lx);
1348              
1349 3190         3393 $lx->{pos_before}= pos($$s);
1350 3190         2787 $lx->{line_before}= $lx->{line}; # strings may contain \n, so this may change.
1351              
1352             # idents: distinguished by case:
1353 3190 100       6276 return multi_token_new ($lx, $1) if $$s =~ /\G ([A-Z][A-Z_0-9]*)\b /gcsx;
1354 2281 100       4120 return ident_new ($lx, $1) if $$s =~ /\G ([a-z][a-z_0-9]*)\b /gcsx;
1355 1752 50       5939 return ident_new ($lx, $1) if $$s =~ /\G \`([^\n\\\`]+)\` /gcsx;
1356              
1357 1752 100       2538 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         13 my $interpol_new= $1;
1360 8 50       17 return error_new ($lx, $interpol_new, 'unknown type cast')
1361             unless good_interpol_type($interpol_new);
1362              
1363 8 50       14 return error_new ($lx, $interpol_new, 'duplicate type case')
1364             if $interpol;
1365              
1366 8         18 my $tok= token_scan_rec ($lx, $interpol_new);
1367 8 50       24 return $tok if $tok->{error};
1368              
1369             return error_new ($lx, $tok->{kind},
1370             "Expected Perl interpolation after type cast to '$interpol_new'")
1371 8 50       33 unless $tok->{kind} =~ /^interpol/;
1372              
1373 8         14 return $tok;
1374             }
1375              
1376 1744 50       2447 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       2609 return token_num_new ($lx, $interpol, hex($1)) if $$s =~ /\G 0x([0-9a-f_]+)\b /gcsix;
1388 1743 100       2383 return token_num_new ($lx, $interpol, oct($1)) if $$s =~ /\G (0b[0-1_]+)\b /gcsx;
1389 1742 100       2808 return token_num_new ($lx, $interpol, $1) if $$s =~ /\G ([1-9][0-9_]*)\b /gcsx;
1390 1526 100       2003 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       2111 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       2705 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       1986 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       2277 return token_scan_variable ($lx, $interpol, 'scalar') if $$s =~ /\G (?= \$\S ) /gcsx;
1410 1438 100       2003 return token_scan_variable ($lx, $interpol, 'array') if $$s =~ /\G (?= \@\S ) /gcsx;
1411 1367 100       1850 return token_scan_variable ($lx, $interpol, 'hash') if $$s =~ /\G (?= \%[^\s\d] ) /gcsx;
1412 1339 100       1874 return token_scan_codeblock ($lx, $interpol) if $$s =~ /\G (?= \{ ) /gcsx;
1413 1277 100       1881 return token_scan_delimited ($lx, $interpol, $1) if $$s =~ /\G (?= [\'\"] ) /gcsx;
1414              
1415             # symbols:
1416 1227 100       3250 return syn_new ($lx, 'symbol', $1)
1417             if $$s =~ /\G(
1418             == | != | <= | >=
1419             | \&\& | \|\| | \! | \^\^
1420             | \*\* | \^
1421             | [-+*\/;:,.()\[\]{}<=>?\%\&\|]
1422             )/gcsx;
1423              
1424             # specials:
1425 41 100       71 return error_new ($lx, $1, 'Unexpected character') if $$s =~ /\G(.)/gcs;
1426 40         59 return syn_new ($lx, 'special', '');
1427             }
1428              
1429             sub token_scan($)
1430             {
1431 3182     3182 0 2072 my ($lx)= @_;
1432 3182         3728 my $t= token_scan_rec($lx, '');
1433             #print STDERR "DEBUG: scanned: ".token_describe($t)."\n";
1434 3182         3521 return $t;
1435             }
1436              
1437             sub lexer_shift($)
1438             # returns the old token kind
1439             {
1440 3182     3182 0 2450 my ($lx)= @_;
1441 3182         3134 my $r= $lx->{token}{kind};
1442 3182         3405 $lx->{token}= token_scan($lx);
1443 3182         5405 return $r;
1444             }
1445              
1446             sub lexer_new($$$)
1447             {
1448 280     280 0 614 my ($s, $file, $line_start)= \(@_);
1449 280         1365 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         712 lock_keys %$lx;
1461 280         2036 lexer_shift($lx);
1462 280         273 return $lx;
1463             }
1464              
1465             sub flatten($);
1466             sub flatten($)
1467             {
1468 11771     11771 0 7293 my ($x)= @_;
1469 11771 100       19006 return $x
1470             unless ref($x);
1471              
1472 874 50       1686 return map { flatten($_) } @$x
  3632         3374  
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 5204 my ($x)= @_;
1485 8106         7150 return map {$_ => 1} flatten $x;
  10800         17134  
1486             }
1487              
1488             sub looking_at_raw($$)
1489             {
1490 9214     9214 0 6566 my ($lx, $kind)= @_;
1491 9214 100       11833 return unless $kind;
1492              
1493 8106         7569 my %kind= flatten_hash $kind;
1494             return $lx->{token}{kind}
1495 8106 100       15544 if $kind{$lx->{token}{kind}};
1496              
1497 6690         10077 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 7779 my ($lx, $kind, $do_shift)= @_;
1505 9214 100       8516 if (my $x= looking_at_raw($lx,$kind)) {
1506 1416 100       2352 lexer_shift($lx) if $do_shift;
1507 1416         2890 return $x;
1508             }
1509 7798         11801 return;
1510             }
1511              
1512             sub english_or(@)
1513             {
1514 1     1 0 1 my $map= undef;
1515 1 50       4 $map= shift
1516             if ref($_[0]) eq 'CODE';
1517              
1518 1         2 my @l= sort map flatten($_), @_;
1519              
1520 1 50       3 @l= map { $map->($_) } @l
  1         3  
1521             if $map;
1522              
1523 1 50       4 return 'nothing' if scalar(@l) == 0;
1524 1 50       6 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 770 my ($lx, $kind, $do_shift)= @_;
1533 779 100       924 if (my $x= looking_at($lx, $kind, $do_shift)) {
    50          
1534 778         1908 return $x;
1535             }
1536             elsif (my $err= lx_token_error($lx)) {
1537 0         0 $lx->{error}= $err;
1538             }
1539             else {
1540             $lx->{error}= 'Expected '.(english_or \"e_perl, $kind).
1541 1         3 ', but found '.token_describe($lx->{token});
1542             }
1543 1         2 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 3539 my ($lx, $kind, @more)= @_;
1572             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       4391 map { $_ => undef } @more,
  11473         14233  
1585             };
1586 2577         5118 lock_keys %$r;
1587 2577         12419 return $r;
1588             }
1589              
1590             # special creates that occur frequently:
1591             sub create_Expr($)
1592             {
1593 1023     1023 0 814 my ($lx)= @_;
1594 1023         1562 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 1244 my ($result, $lx, $parse_elem, $list_sep, $end)= @_;
1617              
1618 1210         1165 my %pos= ();
1619 1210         826 ELEMENT: {do {
  1210         881  
1620 1475         1025 do {
1621             # check that we have no infinite loop:
1622 1484         1041 my $p= pos(${ $lx->{text_p} });
  1484         1997  
1623 1484 50       3330 die "BUG: pos() not shifted in list" if $pos{$p}++;
1624              
1625             # check for end:
1626 1484 100       1557 last ELEMENT if looking_at($lx, $end);
1627 1475 100       1501 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       1999 my @result1= $parse_elem->($lx);
1635              
1636             # append that element to result:
1637 1463         10608 push @$result, @result1;
1638              
1639             # check whether the list continues:
1640             } while (looking_at($lx, $list_sep, SHIFT))};
1641              
1642 1208         2981 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 148 my ($result, $lx, $parse_elem)= @_;
1654              
1655 154         255 while (my @result1= $parse_elem->($lx)) {
1656 54         129 push @$result, @result1;
1657             }
1658              
1659 154 50       239 return if $lx->{error};
1660 154         459 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 7537 my ($hash, $key)= @_;
1669 9535         6688 my $result= undef;
1670 9535         20335 local $SIG{__DIE__}= \&my_confess;
1671 9535 100       15227 if (exists $hash->{$key}) {
    100          
1672 7487         7164 $result= $hash->{$key}
1673             }
1674             elsif (exists $hash->{-default}) {
1675             $result= $hash->{-default}
1676 1086         1350 }
1677             else {
1678 962         3556 return '';
1679             }
1680              
1681 8573   100     16345 until (ref($result) || !defined $result) { # No infinite loop protection!
1682             die "'$result' key not in hash table"
1683 392 50       592 unless exists $hash->{$result};
1684 392         768 $result= $hash->{$result};
1685             }
1686              
1687 8573         21223 return $result;
1688             }
1689              
1690             sub switch($%) # waiting for Perl 5.10: given/when/default
1691             {
1692 8243     8243 0 43359 my ($value, %case)= @_;
1693 8243 50       9579 if (my $code= find_ref(%case, $value)) {
1694 8243         9240 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       6 if ($lx->{token}{error}) {
1704             return 'Found '.
1705             quote_perl($lx->{token}{value}).': '.
1706 1         3 $lx->{token}{str};
1707             }
1708 1         3 return;
1709             }
1710              
1711             sub parse_choice($%)
1712             {
1713 2266     2266 0 12258 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         7 $lx->{error}= 'In '.(caller(3))[3].": $err";
1718             }
1719             elsif (scalar(keys %opt) > 10) {
1720             $lx->{error}= 'In '.(caller(3))[3].': '.
1721 0         0 ' Unexpected '.token_describe($lx->{token});
1722             }
1723             else {
1724             $lx->{error}= 'In '.(caller(3))[3].': Expected '.
1725             (english_or \"e_perl, \%opt).
1726             ', but found '.
1727 0         0 token_describe($lx->{token});
1728             }
1729 1         13 return;
1730             },
1731 2266         11603 %opt, # may override -default
1732             );
1733             }
1734              
1735             sub parse_plain_ident($)
1736             {
1737 543     543 0 523 my ($lx)= @_;
1738             return parse_choice($lx,
1739             'interpol' => sub {
1740 49     49   51 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       74 if ($r->{type} eq 'num') {
1753             $lx->{error}=
1754 0         0 '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         64 lexer_shift($lx);
1761 49         143 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   477 my $r= $lx->{token};
1776 494         568 lexer_shift($lx);
1777 494         1472 return $r;
1778             },
1779 543         2334 );
1780             }
1781              
1782             sub parse_ident_chain($$)
1783             {
1784 510     510 0 414 my ($lx, $arr)= @_;
1785 510         785 return parse_list($arr, $lx, \&parse_plain_ident, '.');
1786             }
1787              
1788             sub check_column(@)
1789             {
1790 324     324 0 656 while (scalar(@_) < 4) { unshift @_, undef; }
  942         1367  
1791 324         521 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 299 my ($lx, $arr)= @_;
1819 324         410 my $r= create ($lx, 'Column', qw(ident_chain));
1820 324   100     1017 $arr||= [];
1821             return
1822 324 50       463 unless parse_ident_chain($lx, $arr);
1823              
1824 324 50       581 my_confess if scalar(@$arr) < 1;
1825 324 50       458 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         387 check_column(@$arr);
1832              
1833 324         310 $r->{ident_chain}= $arr;
1834              
1835 324         577 lock_keys %$r;
1836 324         1790 return $r;
1837             }
1838              
1839             sub parse_schema_qualified($$)
1840             {
1841 186     186 0 158 my ($lx, $kind)= @_;
1842              
1843 186         245 my $r= create ($lx, $kind, qw(ident_chain));
1844 186         215 my $arr= [];
1845             return
1846 186 50       250 unless parse_ident_chain($lx, $arr);
1847              
1848 186 50       318 my_confess if scalar(@$arr) < 1;
1849 186 50       260 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         173 $r->{ident_chain}= $arr;
1856              
1857 186         301 lock_keys %$r;
1858 186         1100 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 138 my ($lx)= @_;
1876 168         214 return parse_schema_qualified($lx, 'Table');
1877             }
1878              
1879             sub parse_charset($)
1880             {
1881 7     7 0 10 my ($lx)= @_;
1882 7         15 return parse_schema_qualified($lx, 'CharSet');
1883             }
1884              
1885             sub parse_constraint($)
1886             {
1887 7     7 0 7 my ($lx)= @_;
1888 7         10 return parse_schema_qualified($lx, 'Constraint');
1889             }
1890              
1891             sub parse_index($)
1892             {
1893 1     1 0 1 my ($lx)= @_;
1894 1         2 return parse_schema_qualified($lx, 'Index');
1895             }
1896              
1897             sub parse_collate($)
1898             {
1899 1     1 0 1 my ($lx)= @_;
1900 1         3 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         4 return parse_schema_qualified($lx, 'Engine');
1919             }
1920              
1921              
1922             sub parse_column_name($)
1923             {
1924 49     49 0 45 my ($lx)= @_;
1925 49         65 my $r= create ($lx, 'ColumnName', qw(token));
1926              
1927             parse_choice($lx,
1928             'ident' => sub {
1929 44     44   43 $r->{type}= 'ident';
1930 44         43 $r->{token}= $lx->{token};
1931 44         62 lexer_shift($lx);
1932             },
1933              
1934             'interpolColumn' => 'interpol',
1935             'interpol' => sub {
1936 5     5   10 $r->{type}= 'interpol';
1937 5         7 $r->{token}= $lx->{token};
1938 5         8 lexer_shift($lx);
1939             },
1940 49         183 );
1941 49 50       348 return if $lx->{error};
1942              
1943 49         86 lock_keys %$r;
1944 49         293 return $r;
1945             }
1946              
1947             sub parse_column_index($)
1948             {
1949 2     2 0 1 my ($lx)= @_;
1950 2         3 my $r= create ($lx, 'ColumnIndex', qw(name length desc));
1951              
1952             return unless
1953 2 50       4 $r->{name}= parse_column_name($lx);
1954              
1955 2 100       3 if (looking_at($lx, '(', SHIFT)) {
1956             return unless
1957 1 50 33     3 $r->{length}= parse_limit_expr($lx)
1958             and expect ($lx, ')', SHIFT);
1959             }
1960              
1961 2 100       6 if (looking_at($lx, 'DESC', SHIFT)) {
    50          
1962 1         1 $r->{desc}= 1;
1963             }
1964             elsif (looking_at($lx, 'ASC', SHIFT)) {
1965             #ignore
1966             }
1967              
1968 2         6 lock_hash %$r;
1969 2         24 return $r;
1970             }
1971              
1972             sub parse_table_name($)
1973             {
1974 3     3 0 4 my ($lx)= @_;
1975 3         8 my $r= create ($lx, 'TableName', qw(token));
1976              
1977             parse_choice($lx,
1978             'ident' => sub {
1979 3     3   4 $r->{type}= 'ident';
1980 3         5 $r->{token}= $lx->{token};
1981 3         6 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         16 );
1991 3 50       27 return if $lx->{error};
1992              
1993 3         9 lock_keys %$r;
1994 3         18 return $r;
1995             }
1996              
1997             sub parse_table_as($)
1998             {
1999 122     122 0 104 my ($lx)= @_;
2000 122         170 my $r= create ($lx, 'TableAs', qw(table as));
2001              
2002             return unless
2003 122 50       177 $r->{table}= parse_table($lx);
2004              
2005 122 100       157 if (looking_at($lx, 'AS', SHIFT)) {
2006             return unless
2007 3 50       12 $r->{as}= parse_table_name($lx);
2008             }
2009              
2010 122         241 lock_hash %$r;
2011 122         1516 return $r;
2012             }
2013              
2014             sub parse_value_or_column_into($$$)
2015             {
2016 173     173 0 168 my ($lx, $r, $type)= @_;
2017              
2018 173         168 my $token= $lx->{token};
2019 173         239 lexer_shift($lx);
2020              
2021 173 100       250 if (looking_at($lx, '.')) {
2022 9         13 $r->{type}= 'column';
2023 9         23 $r->{arg}= parse_column($lx, [ $token ]);
2024             }
2025             else {
2026 164         170 $r->{type}= $type;
2027 164         317 $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   25 use constant ACTION_AMBIGUOUS => undef;
  5         7  
  5         263  
2037 5     5   17 use constant ACTION_REDUCE => -1;
  5         7  
  5         248  
2038 5     5   17 use constant ACTION_SHIFT => +1;
  5         5  
  5         79154  
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 24 my ($lx)= @_;
2049             return unless
2050 24 50       64 my $limit= parse_limit_num($lx);
2051 24         55 my $r= create_Expr ($lx);
2052 24         31 $r->{type}= 'limit';
2053 24         23 $r->{arg}= $limit;
2054 24         48 lock_hash %$r;
2055 24         362 return $r;
2056             }
2057              
2058             sub parse_char_unit($)
2059             {
2060 1     1 0 1 my ($lx)= @_;
2061 1         2 my $r= create($lx, 'CharUnit', qw(name));
2062 1         3 $r->{name}= expect($lx, ['CHARACTERS', 'CODE_UNITS', 'OCTETS'], SHIFT);
2063 1         3 lock_hash %$r;
2064 1         13 return $r;
2065             }
2066              
2067             sub parse_list_delim($$)
2068             {
2069 57     57 0 59 my ($lx, $func)= @_;
2070             return unless
2071 57 50 33     90 expect($lx, '(', SHIFT)
      33        
2072             and my $list= parse_list([], $lx, $func, ',', ')')
2073             and expect($lx, ')', SHIFT);
2074 57         230 return $list;
2075             }
2076              
2077             sub parse_type_post_inner($)
2078             {
2079 106     106 0 86 my ($lx)= @_;
2080              
2081 106         66 my $functor= undef;
2082 106         102 my @arg= ();
2083             parse_choice ($lx,
2084             -default => sub {
2085 73 100   73   101 if (my $spec= find_ref(%type_spec, $lx->{token}{kind})) {
2086 27 100       44 if ($spec->{value_list}) {
2087 1         2 $functor= 'basewlist',
2088             push @arg, lexer_shift($lx);
2089             return unless
2090 1 50       2 my $value_list= parse_list_delim($lx, \&parse_expr);
2091 1         2 push @arg, @$value_list;
2092             }
2093             else {
2094 26         30 $functor= 'base';
2095 26         32 push @arg, lexer_shift($lx);
2096             }
2097             }
2098             },
2099              
2100             'UNSIGNED' => 'SIGNED',
2101             'SIGNED' => sub {
2102 1     1   2 $functor= 'property';
2103 1         2 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   1 $functor= 'property';
2113 1         3 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   8 lexer_shift($lx);
2138             return unless
2139 4 50       10 my $arg= parse_charset($lx);
2140 4         6 $functor= 'property';
2141 4         9 push @arg, 'charset', $arg;
2142             },
2143             'DROP CHARACTER SET' => sub {
2144 3     3   4 $functor= 'property';
2145 3         3 push @arg, 'charset', '';
2146 3         6 lexer_shift($lx);
2147             },
2148              
2149             'COLLATE' => sub {
2150 1     1   4 lexer_shift($lx);
2151             return unless
2152 1 50       3 my $arg= parse_collate($lx);
2153 1         2 $functor= 'property';
2154 1         2 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   32 lexer_shift($lx);
2177             return unless
2178 23 50       54 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       5 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         1 $functor= 'largelength';
2190 1         3 push @arg, $list->[0];
2191              
2192 1         3 push @arg, lexer_shift($lx);
2193              
2194 1 50       2 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         2 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       45 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         24 $functor= 'length';
2223 22         45 push @arg, @$list;
2224             }
2225 23         162 );
2226 23 50       217 return if $lx->{error};
2227 23 50       34 return unless expect($lx, ')', SHIFT);
2228             },
2229 106         1349 );
2230              
2231 106         1995 return ($functor, \@arg);
2232             }
2233              
2234             sub parse_type_post($$);
2235             sub parse_type_post($$)
2236             {
2237 86     86 0 81 my ($lx, $base)= @_;
2238 86         119 my $r= create($lx, 'TypePost', qw(base functor arg));
2239 86         112 $r->{base}= $base;
2240              
2241 86         113 ($r->{functor}, $r->{arg})= parse_type_post_inner($lx);
2242             return
2243 86 50       242 if $lx->{error};
2244              
2245             return $base
2246 86 100       243 unless defined $r->{functor};
2247              
2248 56         124 return parse_type_post ($lx, $r);
2249             }
2250              
2251             sub parse_type($)
2252             {
2253 30     30 0 32 my ($lx)= @_;
2254 30         50 my $r= create($lx, 'Type', qw(base token));
2255              
2256 30 100       59 if (looking_at($lx, ['interpol', 'interpolType'])) {
2257 8         10 $r->{type}= 'interpol';
2258 8         10 $r->{token}= $lx->{token};
2259 8         13 lexer_shift($lx);
2260             }
2261             else {
2262 22 50       57 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         26 $r->{type}= 'base';
2267 22         31 $r->{base}= $lx->{token}{kind};
2268             }
2269              
2270 30         78 lock_hash %$r;
2271 30         374 return parse_type_post ($lx, $r);
2272             }
2273              
2274             sub parse_type_list($) # without enclosing (...)
2275             {
2276 1     1 0 2 my ($lx)= @_;
2277             return unless
2278 1 50       4 my $arg= parse_list ([], $lx, \&parse_type, ',', ')');
2279              
2280 1         3 my $r= create ($lx, ['TypeList','explicit'], qw(arg));
2281 1         2 $r->{arg}= $arg;
2282 1         3 lock_hash %$r;
2283 1         13 return $r;
2284             }
2285              
2286             sub parse_type_list_delim($) # with enclosing (...)
2287             {
2288 1     1 0 1 my ($lx)= @_;
2289              
2290             return parse_choice($lx,
2291             '(' => sub {
2292 1     1   2 lexer_shift($lx);
2293             return unless
2294 1 50 33     3 my $r= parse_type_list ($lx)
2295             and expect ($lx, ')', SHIFT);
2296 1         11 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         5 );
2307             }
2308              
2309             sub parse_on_action($)
2310             {
2311 1     1 0 1 my ($lx)= @_;
2312 1         4 return looking_at($lx, ['RESTRICT', 'CASCADE', 'SET NULL', 'SET DEFAULT', 'NO ACTION'], SHIFT);
2313             }
2314              
2315             sub parse_references($)
2316             {
2317 3     3 0 4 my ($lx)= @_;
2318 3         6 my $r= create($lx, 'References', qw(table column match on_delete on_update));
2319              
2320 3         6 lexer_shift($lx);
2321              
2322             return unless
2323             $r->{table}= parse_table($lx)
2324 3 50 33     5 and $r->{column}= parse_list_delim($lx, \&parse_column_name);
2325              
2326 3 100       7 if (looking_at($lx, 'MATCH', SHIFT)) {
2327 1         3 $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         6 lexer_shift($lx);
2334             return unless
2335 1 50       3 $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             -default => sub {}
2343 4     4   18 );
2344 3         14 });
2345 3 50       9 return if $lx->{error};
2346              
2347 3         7 lock_hash %$r;
2348 3         48 return $r;
2349             }
2350              
2351             sub parse_column_spec_post_inner($)
2352             {
2353 38     38 0 36 my ($lx)= @_;
2354 38         31 my $functor= undef;
2355 38         34 my @arg= ();
2356              
2357 38         29 my $constraint= undef;
2358 38 100       50 if (looking_at($lx, 'CONSTRAINT', SHIFT)) {
2359             return unless
2360 2 50       6 $constraint= parse_constraint($lx);
2361             }
2362              
2363             parse_choice($lx,
2364             -default => sub {
2365 20 50   20   28 if ($constraint) {
2366 0         0 $lx->{error}= 'Constraint expected';
2367             }
2368             else {
2369 20         56 my ($func, $arg)= parse_type_post_inner($lx); # inherit column type post
2370 20 100       54 if ($func) {
2371 4         6 $functor= "type_$func";
2372 4         14 @arg= @$arg;
2373             }
2374             }
2375             },
2376             'NOT NULL' => sub {
2377 10     10   13 $functor= 'property';
2378 10         21 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   1 $functor= 'property';
2398 1         2 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   8 lexer_shift($lx);
2428             return unless
2429 6 50       10 my $val= parse_expr($lx);
2430 6         7 $functor= 'property';
2431 6         12 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   5 my $ref= parse_references($lx);
2485 1         1 $functor= 'property';
2486 1         3 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       679 );
2494              
2495 38         838 return ($functor, \@arg);
2496             }
2497              
2498             sub parse_column_spec_post($$);
2499             sub parse_column_spec_post($$)
2500             {
2501 38     38 0 37 my ($lx, $base)= @_;
2502              
2503 38         48 my $r= create($lx, 'ColumnSpecPost', qw(base functor arg));
2504 38         44 $r->{base}= $base;
2505 38         45 $r->{arg}= [];
2506              
2507 38         55 ($r->{functor}, $r->{arg})= parse_column_spec_post_inner($lx);
2508             return
2509 38 50       79 if $lx->{error};
2510              
2511             return $base
2512 38 100       137 unless defined $r->{functor};
2513              
2514 22         38 return parse_column_spec_post ($lx, $r);
2515             }
2516              
2517             sub parse_column_spec($)
2518             {
2519 16     16 0 19 my ($lx)= @_;
2520              
2521 16         28 my $r= create($lx, 'ColumnSpec', qw(datatype name token));
2522              
2523             parse_choice($lx,
2524             'interpolColumnSpec' => 'interpol',
2525             'interpol' => sub {
2526 7     7   11 $r->{type}= 'interpol';
2527 7         8 $r->{token}= $lx->{token};
2528 7         12 lexer_shift($lx);
2529             },
2530              
2531             -default => sub {
2532 9     9   17 $r->{type}= 'base';
2533             return unless
2534 9 50       16 $r->{datatype}= parse_type($lx);
2535             }
2536 16         82 );
2537 16 50       113 return if $lx->{error};
2538              
2539 16         36 lock_hash %$r;
2540 16         207 return parse_column_spec_post($lx, $r);
2541              
2542             }
2543              
2544             sub parse_expr_list($) # without enclosing (...)
2545             {
2546 9     9 0 14 my ($lx)= @_;
2547 9 100       27 if (looking_at($lx, [@SELECT_INITIAL,'interpolStmt'])) {
2548             return unless
2549 3 50       7 my $q= parse_select_stmt($lx);
2550              
2551 3         10 my $r= create_Expr ($lx);
2552 3         5 $r->{type}= 'subquery';
2553 3         3 $r->{arg}= $q;
2554 3         13 return $r;
2555             }
2556             else {
2557 6         20 my $r= create ($lx, ['ExprList','explicit'], qw(arg));
2558              
2559             return unless
2560 6 50       23 my $arg= parse_list ([], $lx, \&parse_expr, ',', ')');
2561              
2562 6         12 $r->{arg}= $arg;
2563 6         19 lock_hash %$r;
2564 6         89 return $r;
2565             }
2566             }
2567              
2568             sub parse_expr_list_delim($) # with enclosing (...)
2569             {
2570 15     15 0 17 my ($lx)= @_;
2571              
2572             return parse_choice($lx,
2573             '(' => sub {
2574 9     9   13 lexer_shift($lx);
2575             return unless
2576 9 50 33     22 my $r= parse_expr_list ($lx)
2577             and expect ($lx, ')', SHIFT);
2578 9         82 return $r;
2579             },
2580              
2581             'interpol' => sub { # Perl array reference:
2582 5     5   14 my $r= create ($lx, ['ExprList','interpol'], qw(token));
2583 5         10 $r->{token}= $lx->{token};
2584 5         9 lexer_shift($lx);
2585 5         13 lock_hash %$r;
2586 5         76 return $r;
2587             },
2588 15         66 );
2589             }
2590              
2591             sub get_rhs($$)
2592             {
2593 190     190 0 190 my ($left, $arg_i)= @_;
2594 190   33     678 return $left->{rhs_map}{$arg_i} || $left->{rhs};
2595             }
2596              
2597             sub parse_thing($$;$$)
2598             {
2599 203     203 0 245 my ($lx, $thing_name, $left, $right_mark)= @_;
2600             return switch ($thing_name,
2601             'expr' => sub {
2602 191     191   270 return parse_expr ($lx, $left, $right_mark)
2603             },
2604             'type' => sub {
2605 1     1   3 return parse_type ($lx);
2606             },
2607             'string_expr' => sub {
2608 1     1   2 return parse_expr ($lx, $left, 'string')
2609             },
2610             'expr_list' => sub {
2611 9     9   19 return parse_expr_list_delim($lx);
2612             },
2613             'type_list' => sub {
2614 1     1   5 return parse_type_list_delim($lx);
2615             },
2616 203         1083 );
2617             }
2618              
2619             sub parse_funcsep($$$)
2620             {
2621 8     8 0 9 my ($lx, $r, $pattern)= @_;
2622 8         11 for my $e (@$pattern) {
2623 34 100       78 if (!ref($e)) {
    100          
    50          
2624             return unless
2625 14 50       20 expect($lx, $e, SHIFT);
2626 14         13 push @{ $r->{arg} }, $e; # no ref()
  14         27  
2627             }
2628             elsif (ref($e) eq 'SCALAR') {
2629             return unless
2630 13 50       21 my $arg= parse_thing($lx, $$e); # will return a ref()
2631 13         61 push @{ $r->{arg} }, $arg;
  13         30  
2632             }
2633             elsif (ref($e) eq 'ARRAY') {
2634 7 100       13 if (looking_at($lx, $e->[0])) {
2635             return unless
2636 2 50       5 parse_funcsep($lx, $r, $e);
2637             }
2638             }
2639             else {
2640 0         0 die "Unrecognised pattern piece, ref()=".ref($e);
2641             }
2642             }
2643 8         31 return $r;
2644             }
2645              
2646             sub parse_check($)
2647             {
2648 9     9 0 7 my ($lx)= @_;
2649 9         17 my $r= create ($lx, 'Check', qw(expr));
2650              
2651 9         13 my $cond= parse_expr_post($lx, undef, undef, create($lx, 'ExprEmpty'));
2652 9 50       16 return unless $cond;
2653              
2654 9         10 $r->{expr}= $cond;
2655 9         26 return $r;
2656             }
2657              
2658             sub parse_when_post($)
2659             {
2660 42     42 0 37 my ($lx)= @_;
2661              
2662             return unless
2663 42 100       61 looking_at($lx, 'WHEN', SHIFT); # no error if false (-> parse_try_list)
2664              
2665 20         22 my $cond;
2666              
2667 20         36 my $functor= find_functor(\%functor_suffix, $lx->{token}{kind});
2668 20 100 66     59 if ($functor && $functor->{allow_when}) {
2669 9         16 $cond= parse_expr_post($lx, undef, undef, create($lx, 'ExprEmpty'));
2670             }
2671             else {
2672 11         18 $cond= parse_expr($lx);
2673             }
2674              
2675             return unless
2676 20 50 33     57 $cond
      33        
2677             and expect($lx, 'THEN', SHIFT)
2678             and my $expr= parse_expr($lx);
2679              
2680 20         27 $cond->{maybe_check}= 1; # allow Check interpolation if this is an Expr
2681              
2682 20         63 return [ $cond, $expr ];
2683             }
2684              
2685             sub parse_when($)
2686             {
2687 22     22 0 14 my ($lx)= @_;
2688              
2689             return unless
2690 22 50 66     29 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         55 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 167 my ($left, $right, $right_mark)= @_;
2702              
2703             # hack for 'IN':
2704             return ACTION_REDUCE
2705             if ($right_mark || '') eq 'string' &&
2706 183 100 100     733 $right->{value} eq 'IN';
      66        
2707              
2708             # currently, this is very simple, because we don't use precedences:
2709 182 100       353 return ACTION_SHIFT
2710             unless $left;
2711              
2712             # special rule to allow sequencing even for operators without precedence:
2713             return ACTION_REDUCE
2714             if $left->{value} eq $right->{value} &&
2715 4 100 66     19 $left->{read_type} eq 'infix()';
2716              
2717             # parse with precedences?
2718 3 50       9 if ($do_prec) {
2719             # if both have a precedence:
2720 0 0 0     0 if ($left->{prec} && $right->{prec}) {
2721             return ACTION_REDUCE
2722 0 0       0 if $left->{prec} > $right->{prec};
2723              
2724             return ACTION_SHIFT
2725 0 0       0 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       9 if (defined $left->{value2}) {
2744 3         5 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 185 my ($lx, $left, $right, $right_mark)= @_;
2755 183         256 my $result= shift_or_reduce_pure ($left, $right, $right_mark);
2756 183 50       284 unless ($result) {
2757 0         0 $lx->{error}= "Use of operators '$left->{value}' vs. '$right->{value}' ".
2758             "requires parentheses.";
2759             }
2760 183         301 return $result;
2761             }
2762              
2763             sub find_functor($$)
2764             {
2765 1111     1111 0 861 my ($map, $kind)= @_;
2766              
2767             return unless
2768 1111 100       1374 my $functor= find_ref(%$map, $kind);
2769              
2770 262 100       533 if (my $accept= $functor->{accept}) {
2771 3         7 for my $a (@$accept) {
2772 3 50       8 if ($read_dialect{$a}) {
2773 3         11 return $functor;
2774             }
2775             }
2776 0         0 return;
2777             }
2778              
2779 259         402 return $functor;
2780             }
2781              
2782             sub set_expr_functor($$@)
2783             {
2784 257     257 0 282 my ($r, $functor, @arg)= @_;
2785 257 50       406 my_confess if $r->{arg};
2786              
2787 257         301 $r->{type}= $functor->{type};
2788 257         225 $r->{functor}= $functor;
2789 257         361 $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 934 my ($lx, $left, $right_mark, $arg1)= @_;
2796              
2797             # infix:
2798 1012         1006 my $kind= $lx->{token}{kind};
2799              
2800 1012 100       1261 if (my $right= find_functor(\%functor_suffix, $kind)) {
2801             return unless
2802 183 50       277 my $action= shift_or_reduce($lx, $left, $right, $right_mark);
2803              
2804 183 100       303 if ($action == ACTION_SHIFT) {
2805 178         214 lexer_shift ($lx);
2806              
2807 178         256 my $r= create_Expr ($lx);
2808 178         280 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   153 my $arg2= parse_thing ($lx, get_rhs($right,0), $right, 1);
2815 90         433 push @{ $r->{arg} }, $arg2;
  90         209  
2816             },
2817             'infix()' => sub {
2818             # parse sequence:
2819 63     63   57 my $i=0;
2820 63         51 do {
2821             return unless
2822 64 50       116 my $argi= parse_thing ($lx, get_rhs($right,$i++), $right, 1);
2823 64         317 push @{ $r->{arg} }, $argi;
  64         182  
2824             } while (looking_at($lx, $kind, SHIFT)); # same operator?
2825             },
2826             'infix23' => sub {
2827             # parse second arg:
2828             return unless
2829 2 50   2   5 my $arg2= parse_thing ($lx, get_rhs($right,0), $right, 1);
2830 2         20 push @{ $r->{arg} }, $arg2;
  2         7  
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             my $arg2= parse_thing ($lx, get_rhs($right,0), $right, 1)
2843 2 50 33 2   6 and expect ($lx, $right->{value2}, SHIFT)
      33        
2844             and my $arg3= parse_thing ($lx, get_rhs($right,1), $right, 1); # descend
2845              
2846 2         14 push @{ $r->{arg} }, $arg2, $arg3;
  2         5  
2847             },
2848       20     'suffix' => sub {
2849             # nothing more to do
2850             }
2851 178         1248 );
2852 178 100       1627 return if $lx->{error};
2853              
2854 177         342 lock_keys %$r; # {maybe_check} may be modified if we parse WHEN clauses.
2855              
2856 177         883 return parse_expr_post ($lx, $left, $right_mark, $r); # descend
2857             }
2858             }
2859              
2860 834         2572 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 697 my ($lx, $functor, $right_mark)= @_;
2872 817         907 my $r= create_Expr ($lx);
2873              
2874             parse_choice($lx,
2875             '.' => sub {
2876 23     23   31 lexer_shift($lx);
2877 23         30 $r->{type}= 'column';
2878 23         34 $r->{arg}= parse_column ($lx);
2879             },
2880              
2881             'interpolColumn' => 'ident',
2882             'interpolTable' => 'ident',
2883             '*' => 'ident',
2884             'ident' => sub {
2885 283     283   277 $r->{type}= 'column';
2886 283         375 $r->{arg}= parse_column ($lx);
2887             },
2888              
2889             'interpolExpr' => sub {
2890 206     206   208 $r->{type}= 'interpol';
2891 206         225 $r->{token}= $lx->{token};
2892 206         255 lexer_shift($lx);
2893             },
2894              
2895             'interpol' => sub {
2896 173     173   292 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   9 $r->{type}= 'interpol';
2906 7         8 $r->{token}= $lx->{token};
2907 7         8 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     13 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     2 my $arg= parse_column_name($lx)
2919             and expect ($lx, ')', SHIFT);
2920 1         3 set_expr_functor ($r, $functor, $arg);
2921             }
2922 1         5 );
2923             }
2924             },
2925              
2926             'CASE' => sub {
2927 34     34   47 lexer_shift($lx);
2928 34         36 $r->{type}= 'case';
2929 34 100       67 if (looking_at($lx, ['WHEN','ELSE','END'])) { # without 'switchval'
2930             return unless
2931 12 50       22 $r->{arg}= parse_try_list([], $lx, \&parse_when);
2932             }
2933             else { # with switchval
2934             return unless
2935             $r->{switchval}= parse_expr($lx)
2936 22 50 33     36 and $r->{arg}= parse_try_list([], $lx, \&parse_when_post);
2937             }
2938              
2939 34 100       65 if (looking_at($lx, 'ELSE', SHIFT)) {
2940             return unless
2941 24 50       35 $r->{otherwise}= parse_expr($lx);
2942             }
2943              
2944             return unless
2945 34 50       58 expect($lx, 'END', SHIFT);
2946             },
2947              
2948             'ALL' => 'SOME',
2949             'ANY' => 'SOME',
2950             'SOME' => sub {
2951 1 50 33 1   9 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         3 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         3 lexer_shift($lx);
2961              
2962             return unless
2963 1 50 33     2 expect($lx, '(', SHIFT)
      33        
2964             and my $q= parse_select_stmt ($lx)
2965             and expect($lx, ')', SHIFT);
2966              
2967 1         4 my $r2= create_Expr($lx);
2968 1         2 $r2->{type}= 'subquery';
2969 1         1 $r2->{arg}= $q;
2970              
2971 1         2 set_expr_functor ($r, $functor2, $r2);
2972             },
2973              
2974             '(' => sub {
2975 13     13   23 lexer_shift($lx);
2976 13 100       32 if (looking_at($lx, [@SELECT_INITIAL,'interpolStmt'])) {
2977             return unless
2978 2 50       7 my $q= parse_select_stmt ($lx);
2979 2         6 $r->{type}= 'subquery';
2980 2         4 $r->{arg}= $q;
2981             }
2982             else {
2983             return unless
2984 11 50       20 my $arg= parse_expr($lx);
2985 11         16 $r->{type}= '()';
2986 11         12 $r->{arg}= $arg;
2987             }
2988             return unless
2989 13 50       24 expect($lx, ')', SHIFT);
2990             },
2991              
2992             -default => sub {
2993 77     77   140 my $functor2= find_functor(\%functor_prefix, $lx->{token}{kind});
2994 77 100 66     240 if (!$functor2 && $lx->{token}{type} eq 'keyword') { # generic funcall
2995 9         23 $functor2= make_op($lx->{token}{kind}, 'funcall');
2996             }
2997              
2998             # prefix / funcall:
2999 77 50       113 if ($functor2) {
    0          
3000 77         92 set_expr_functor ($r, $functor2);
3001 77         103 lexer_shift($lx);
3002              
3003             switch ($functor2->{read_type},
3004             'prefix' => sub {
3005 45         36 my $arg;
3006 45 100       62 if (looking_at($lx, '(', NO_SHIFT)) {
3007             return unless
3008 16 50       36 $r->{arg}= parse_list_delim ($lx, \&parse_expr);
3009             }
3010             else {
3011             return unless
3012 29 50       49 my $arg= parse_thing ($lx, get_rhs($functor2,0), $functor2, 0);
3013 29         151 $r->{arg}= [ $arg ];
3014             }
3015             },
3016             'funcall' => sub {
3017             return unless
3018 25 50       48 $r->{arg}= parse_list_delim ($lx, \&parse_expr);
3019             },
3020             'funcall1col' => sub {
3021             return unless
3022 1 50 33     3 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             expect ($lx, '(', SHIFT)
3030 6 50 33     11 and parse_funcsep ($lx, $r, $functor2->{rhs});
3031             },
3032 77         455 );
3033 77 50       593 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         107 return;
3041             },
3042 817         8948 );
3043 817 50       17353 return if $lx->{error};
3044              
3045 817 50       1204 die unless $r;
3046 817         1465 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         4212 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 34 my ($lx)= @_;
3056             return parse_choice($lx,
3057             'interpolExpr' => 'interpol',
3058             '?' => 'interpol',
3059             'interpol' => sub {
3060 32     32   31 my $r= $lx->{token};
3061 32         47 lexer_shift($lx);
3062 32         229 return $r;
3063             },
3064 32         104 );
3065             }
3066              
3067             sub parse_expr_as($)
3068             {
3069 226     226 0 174 my ($lx)= @_;
3070 226         309 my $r= create ($lx, 'ExprAs', qw(expr as));
3071              
3072             return unless
3073 226 100       348 $r->{expr}= parse_expr($lx);
3074              
3075 225 100       332 if (looking_at($lx, 'AS', SHIFT)) {
3076             return unless
3077 4 50       13 $r->{as}= parse_column_name($lx);
3078             }
3079              
3080 225         480 lock_hash %$r;
3081 225         3112 return $r;
3082             }
3083              
3084             sub parse_order($)
3085             {
3086 35     35 0 32 my ($lx)= @_;
3087 35         55 my $r= create ($lx, 'Order', qw(type expr token desc));
3088 35         41 $r->{desc}= 0;
3089              
3090             parse_choice($lx,
3091             -default => sub {
3092 14     14   15 $r->{type}= 'expr';
3093             return unless
3094 14 50       25 $r->{expr}= parse_expr($lx);
3095             },
3096              
3097             'interpolOrder' => 'interpol',
3098             'interpol' => sub {
3099 21 100   21   41 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         3 $r->{type}= 'expr';
3104             return unless
3105 3 50       7 $r->{expr}= parse_expr($lx);
3106             }
3107             else {
3108 18         20 $r->{type}= 'interpol';
3109 18         19 $r->{token}= $lx->{token};
3110 18         26 lexer_shift($lx);
3111             }
3112             },
3113 35         161 );
3114 35 50       276 return if $lx->{error};
3115              
3116             parse_choice($lx,
3117       23     -default => sub {}, # no error
3118 2     2   4 'ASC' => sub { lexer_shift($lx); $r->{desc}= 0; },
  2         4  
3119 10     10   16 'DESC' => sub { lexer_shift($lx); $r->{desc}= 1; },
  10         20  
3120 35         189 );
3121              
3122 35         228 lock_hash %$r;
3123 35         474 return $r;
3124             }
3125              
3126             sub parse_join($)
3127             {
3128 127     127 0 103 my ($lx)= @_;
3129 127         288 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             $r->{type}= 'interpol',
3136 5     5   8 $r->{token}= $lx->{token};
3137 5         7 lexer_shift($lx);
3138             },
3139              
3140             -default => sub {
3141 122     122   119 my $shifted= 0;
3142              
3143 122         91 my $want_condition= 1;
3144 122 100       155 if (looking_at($lx, 'NATURAL', SHIFT)) {
3145 3         5 $r->{natural}= 1;
3146 3         4 $shifted= 1;
3147 3         5 $want_condition= 0;
3148             }
3149              
3150             parse_choice($lx,
3151             -default => sub {
3152 113         195 $r->{type}= 'INNER';
3153             },
3154              
3155             'INNER' => sub{
3156 4         6 $r->{type}= 'INNER';
3157 4         9 lexer_shift($lx);
3158 4         7 $shifted= 1;
3159             },
3160              
3161             'UNION' => 'CROSS',
3162             'CROSS' => sub {
3163 2 50       4 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         3 $r->{type}= lexer_shift($lx);
3169 2         1 $want_condition= 0;
3170 2         5 $shifted= 1;
3171             },
3172              
3173             'LEFT' => 'FULL',
3174             'RIGHT' => 'FULL',
3175             'FULL' => sub {
3176 3         6 $r->{type}= lexer_shift($lx);
3177 3         8 looking_at($lx, 'OUTER', SHIFT);
3178 3         6 $shifted= 1;
3179             },
3180 122         664 );
3181 122 50       1079 return if $lx->{error};
3182              
3183 122 100       175 unless (looking_at ($lx, 'JOIN', SHIFT)) {
3184 112 50       162 if ($shifted) {
3185 0         0 $lx->{error}= "Expected JOIN, but found ".token_describe($lx->{token});
3186             }
3187 112         108 $r= undef;
3188 112         275 return;
3189             }
3190              
3191             return unless
3192 10 50       22 $r->{table}= parse_list([], $lx, \&parse_table_as, ',');
3193              
3194 10 100       33 if ($want_condition) {
3195             parse_choice($lx,
3196             'ON' => sub {
3197 3         6 lexer_shift($lx);
3198 3         8 $r->{on}= parse_expr($lx);
3199             },
3200             'USING' => sub {
3201 2         4 lexer_shift($lx);
3202             return unless
3203 2 50       7 $r->{using}= parse_list_delim ($lx, \&parse_column_name);
3204             },
3205 5         21 );
3206             }
3207             }
3208 127         619 );
3209 127 50       1169 return if $lx->{error};
3210 127 100       351 return unless $r;
3211              
3212 15         35 lock_hash %$r;
3213 15         243 return $r;
3214             }
3215              
3216             sub push_option($$$)
3217             {
3218 488     488 0 384 my ($lx, $list, $words)= @_;
3219 488 100       525 if (my $x= looking_at($lx, $words, SHIFT)) {
3220 5         8 push @$list, $x;
3221 5         13 return $x;
3222             }
3223 483         749 return 0;
3224             }
3225              
3226             sub push_option_list($$$)
3227             {
3228 300     300 0 298 my ($lx, $list, $words)= @_;
3229 300         417 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 60 my ($lx)= @_;
3235             # FIXME: MISSING:
3236             # - WHERE CURRENT OF (i.e., cursor support)
3237 56         100 return parse_expr($lx);
3238             }
3239              
3240             sub parse_select($)
3241             {
3242 180     180 0 148 my ($lx)= @_;
3243 180         386 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       327 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         277 $r->{opt_front}= [];
3277             push_option ($lx, $r->{opt_front}, [
3278             'DISTINCT', 'ALL',
3279             ($read_dialect{mysql} ?
3280 180 50       555 ('DISTINCTROW')
3281             : ()
3282             )
3283             ]);
3284              
3285             push_option_list ($lx, $r->{opt_front}, [
3286             ($read_dialect{mysql} ?
3287             (
3288 180 50       585 '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       401 $r->{expr_list}= parse_list([], $lx, \&parse_expr_as, ',');
3298              
3299 179 100       241 if (looking_at($lx, 'FROM', SHIFT)) {
3300             return unless
3301             $r->{from}= parse_list([], $lx, \&parse_table_as, ',')
3302 94 50 33     189 and $r->{join}= parse_try_list([], $lx, \&parse_join);
3303              
3304 94 100       156 if (looking_at($lx, 'WHERE', SHIFT)) {
3305             return unless
3306 44 50       86 $r->{where}= parse_where ($lx);
3307             }
3308 94 100       158 if (looking_at($lx, 'GROUP BY', SHIFT)) {
3309             return unless
3310 6 50       18 $r->{group_by}= parse_list([], $lx, \&parse_order, ',');
3311              
3312 6         10 $r->{group_by_with_rollup}= looking_at($lx, 'WITH ROLLUP', SHIFT);
3313             }
3314 94 100       120 if (looking_at($lx, 'HAVING', SHIFT)) {
3315             return unless
3316 1 50       3 $r->{having}= parse_expr ($lx);
3317             }
3318 94 100       130 if (looking_at($lx, 'ORDER BY', SHIFT)) {
3319             return unless
3320 8 50       21 $r->{order_by}= parse_list([], $lx, \&parse_order, ',');
3321             }
3322              
3323 94 100       132 if (looking_at($lx, 'LIMIT', SHIFT)) {
3324 4 50       9 unless (looking_at($lx, 'ALL', SHIFT)) {
3325 4         12 my $first_num= parse_limit_num ($lx);
3326 4 100       14 if (looking_at($lx, ',', SHIFT)) {
3327 2         3 $r->{limit_offset}= $first_num;
3328 2         4 $r->{limit_cnt}= parse_limit_num($lx);
3329             }
3330             else {
3331 2         5 $r->{limit_cnt}= $first_num;
3332             }
3333             }
3334             }
3335 94 100 100     238 if (!$r->{limit_offset} &&
3336             looking_at ($lx, 'OFFSET', SHIFT))
3337             {
3338 1         3 $r->{limit_offset}= parse_limit_num ($lx);
3339             }
3340              
3341 94         119 $r->{opt_back}= [];
3342             push_option_list ($lx, $r->{opt_back}, [
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 94 50 33     483 'FOR SHARE', # FIXME: normalise: MySQL: LOCK IN SHARE MODE
    50          
    50          
3356             'NOWAIT'
3357             )
3358             : ()
3359             ),
3360             ]);
3361             }
3362              
3363 179         386 lock_hash %$r;
3364 179         3631 return $r;
3365             }
3366              
3367             sub parse_insert($)
3368             {
3369 13     13 0 17 my ($lx)= @_;
3370 13         41 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       35 return unless expect($lx, 'INSERT', SHIFT);
3385              
3386             # PostgreSQL:
3387             # - RETURNING ...
3388              
3389 13         25 $r->{opt_front}= [];
3390             push_option_list ($lx, $r->{opt_front}, [
3391             ($read_dialect{mysql} ?
3392             (
3393 13 50       58 'IGNORE',
3394             'LOW_PRIORITY',
3395             'HIGH_PRIORITY',
3396             'DELAYED',
3397             )
3398             : ()
3399             )
3400             ]);
3401              
3402 13         23 looking_at($lx, 'INTO', SHIFT); # optional in MySQL
3403              
3404             return unless
3405 13 50       30 $r->{into}= parse_table($lx);
3406              
3407 13 100       26 if (looking_at($lx, '(')) {
3408             return unless
3409 5 50       16 $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   10 lexer_shift($lx);
3421 5         16 $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   17 if ($r->{column}) {
3428 0         0 $lx->{error}= "Either column list or 'SET' expected, but found both.";
3429 0         0 return;
3430             }
3431 8         13 lexer_shift($lx);
3432 8         19 $r->{set}= parse_list([], $lx, \&parse_expr, ',');
3433             },
3434              
3435 13         63 (map { $_ => 'interpolStmt' } @SELECT_INITIAL),
3436             'interpol' => 'interpolStmt',
3437             'interpolStmt' => sub {
3438 0     0   0 $r->{select}= parse_select_stmt($lx);
3439             },
3440 13         65 );
3441 13 50       151 return if $lx->{error};
3442              
3443 13 100 66     43 if ($read_dialect{mysql} &&
3444             looking_at ($lx, 'ON DUPLICATE KEY UPDATE', SHIFT))
3445             {
3446             return unless
3447 1 50       4 $r->{duplicate_update}= parse_list([], $lx, \&parse_expr, ',');
3448             }
3449              
3450 13         38 lock_hash %$r;
3451 13         263 return $r;
3452             }
3453              
3454             sub parse_update($)
3455             {
3456 9     9 0 9 my ($lx)= @_;
3457 9         28 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       20 return unless expect($lx, 'UPDATE', SHIFT);
3472              
3473             # PostgreSQL:
3474             # - RETURNING ...
3475              
3476 9         16 $r->{opt_front}= [];
3477             push_option_list ($lx, $r->{opt_front}, [
3478             ($read_dialect{mysql} ?
3479             (
3480             'IGNORE',
3481             'LOW_PRIORITY',
3482             )
3483             : ()
3484             ),
3485             ($read_dialect{postgresql} ?
3486             (
3487 9 50       40 'ONLY',
    50          
3488             )
3489             : ()
3490             )
3491             ]);
3492              
3493             return unless
3494             $r->{table}= parse_list([], $lx, \&parse_table_as, ',')
3495             and expect($lx, 'SET', SHIFT)
3496 9 50 33     25 and $r->{set}= parse_list([], $lx, \&parse_expr, ',');
      33        
3497              
3498 9 100       23 if (looking_at($lx, 'FROM', SHIFT)) {
3499             return unless
3500 1 50       3 $r->{from}= parse_list([], $lx, \&parse_table_as, ',');
3501             }
3502             return unless
3503 9 50       22 $r->{join}= parse_try_list([], $lx, \&parse_join);
3504              
3505 9 50       17 if (looking_at($lx, 'WHERE', SHIFT)) {
3506             return unless
3507 9 50       16 $r->{where}= parse_where ($lx);
3508             }
3509 9 100       18 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       14 if (looking_at($lx, 'LIMIT', SHIFT)) {
3514 1         3 $r->{limit_cnt}= parse_limit_num($lx);
3515             }
3516              
3517 9         23 lock_hash %$r;
3518 9         167 return $r;
3519             }
3520              
3521             sub parse_delete($)
3522             {
3523 4     4 0 5 my ($lx)= @_;
3524 4         17 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       11 return unless expect($lx, 'DELETE', SHIFT);
3539              
3540             # PostgreSQL:
3541             # - RETURNING ...
3542              
3543 4         10 $r->{opt_front}= [];
3544             push_option_list ($lx, $r->{opt_front}, [
3545             ($read_dialect{mysql} ?
3546             (
3547 4 50       19 'IGNORE',
3548             'LOW_PRIORITY',
3549             'QUICK'
3550             )
3551             : ()
3552             )
3553             ]);
3554              
3555 4 50       11 return unless expect($lx, 'FROM', SHIFT);
3556              
3557 4         8 $r->{from_opt_front}= [];
3558             push_option ($lx, $r->{from_opt_front}, [
3559             ($read_dialect{postgresql} ?
3560 4 50       16 ('ONLY')
3561             : ()
3562             )
3563             ]);
3564              
3565             return unless
3566 4 50       13 $r->{from}= parse_list([], $lx, \&parse_table_as, ',');
3567              
3568 4 100       9 if (looking_at($lx, 'USING', SHIFT)) {
3569             return unless
3570 2 50       7 $r->{using}= parse_list([], $lx, \&parse_table_as, ',');
3571             }
3572              
3573             return unless
3574 4 50       12 $r->{join}= parse_try_list([], $lx, \&parse_join);
3575              
3576 4 100       8 if (looking_at($lx, 'WHERE', SHIFT)) {
3577             return unless
3578 3 50       7 $r->{where}= parse_where ($lx);
3579             }
3580 4 50       10 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       11 if (looking_at($lx, 'LIMIT', SHIFT)) {
3585 0         0 $r->{limit_cnt}= parse_limit_num($lx);
3586             }
3587              
3588 4         11 lock_hash %$r;
3589 4         74 return $r;
3590             }
3591              
3592             sub keyword($$)
3593             {
3594 1     1 0 3 my ($lx, $keyword)= @_;
3595             return
3596 1 50       4 unless $keyword;
3597              
3598 1 50       3 return $keyword
3599             if ref($keyword);
3600            
3601 1         1 my $r= create($lx, 'Keyword', qw(keyword));
3602 1         2 $r->{keyword}= $keyword;
3603 1         3 lock_hash %$r;
3604 1         11 return $r;
3605             }
3606              
3607             sub parse_index_option($)
3608             {
3609 1     1 0 1 my ($lx)= @_;
3610 1         3 my $r= create($lx, 'IndexOption', qw(arg));
3611              
3612             parse_choice($lx,
3613             -default => sub {
3614 1     1   2 $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         5 );
3627 1 50       9 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 2 my ($lx)= @_;
3637 3 100       6 if (looking_at($lx, 'USING', SHIFT)) {
3638 1         3 return expect($lx, ['BTREE','HASH','RTREE'], SHIFT);
3639             }
3640 2         2 return;
3641             }
3642              
3643             sub parse_table_constraint($)
3644             {
3645 3     3 0 4 my ($lx)= @_;
3646 3         4 my $r= create($lx, "TableConstraint", qw(constraint index_type column index_option reference));
3647 3         5 $r->{index_option}= [];
3648              
3649 3 50       4 if (looking_at($lx, 'CONSTRAINT', SHIFT)) {
3650             return unless
3651 3 50       5 $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             $r->{column}= parse_list_delim($lx, \&parse_column_index)
3661 0 0 0     0 and $r->{index_option}= parse_try_list([], $lx, \&parse_index_option);
3662             },
3663             'UNIQUE' => sub {
3664 1     1   2 lexer_shift($lx);
3665 1         2 $r->{type}= 'unique';
3666 1         3 $r->{index_type}= parse_index_type($lx);
3667             return unless
3668             $r->{column}= parse_list_delim($lx, \&parse_column_index)
3669 1 50 33     4 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             $r->{column}= parse_list_delim($lx, \&parse_column_index)
3677 0 0 0     0 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             $r->{column}= parse_list_delim($lx, \&parse_column_index)
3685 0 0 0     0 and $r->{index_option}= parse_try_list([], $lx, \&parse_index_option);
3686             },
3687             'FOREIGN KEY' => sub {
3688 2     2   4 lexer_shift($lx);
3689 2         2 $r->{type}= 'foreign_key';
3690 2         3 $r->{index_type}= parse_index_type($lx);
3691             return unless
3692             $r->{column}= parse_list_delim($lx, \&parse_column_name)
3693 2 50 33     5 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       27 : ()
3709             ),
3710             );
3711 3 50       45 return if $lx->{error};
3712              
3713 3         7 lock_hash %$r;
3714 3         45 return $r;
3715             }
3716              
3717             sub parse_table_option1($$$$)
3718             {
3719 6     6 0 8 my ($lx, $r, $name, $parse)= @_;
3720 6         9 $r->{type}= 'literal';
3721 6         7 $r->{name}= $name;
3722 6         10 lexer_shift($lx);
3723 6         11 looking_at($lx, '=', SHIFT); # optional =
3724             return unless
3725 6 50       14 $r->{value}= $parse->($lx);
3726 6         11 while (looking_at($lx, ',', SHIFT)) {} # optional ,
3727 6         12 return $r;
3728             }
3729              
3730             sub parse_on_commit_action($)
3731             {
3732 1     1 0 3 my ($lx)= @_;
3733             return keyword ($lx,
3734             expect($lx,
3735             [
3736             'PRESERVE ROWS',
3737             'DELETE ROWS',
3738             ($read_dialect{postgresql} ?
3739             (
3740 1 50       6 'DROP'
3741             )
3742             : ()
3743             )
3744             ],
3745             SHIFT
3746             )
3747             );
3748             }
3749              
3750             sub parse_table_option($)
3751             {
3752 12     12 0 10 my ($lx)= @_;
3753 12         23 my $r= create($lx, 'TableOption', qw(name value token));
3754              
3755             parse_choice($lx,
3756             -default => sub {
3757 4     4   7 $r= undef;
3758             },
3759              
3760             ($read_dialect{mysql} ?
3761             (
3762             'ENGINE' => sub {
3763 2     2   7 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   4 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   3 return parse_table_option1($lx, $r, 'COMMENT', \&parse_expr);
3782             },
3783             )
3784             : ()
3785             ),
3786              
3787             'ON COMMIT' => sub {
3788 1     1   3 return parse_table_option1($lx, $r, 'ON COMMIT', \&parse_on_commit_action);
3789             },
3790              
3791             'interpolTableOption' => 'interpol',
3792             'interpol' => sub {
3793 2     2   5 $r->{type}= 'interpol';
3794 2         2 $r->{token}= $lx->{token};
3795 2         4 lexer_shift($lx);
3796 2         4 while (looking_at($lx, ',', SHIFT)) {} # optional ,
3797 2         2 return $r;
3798             },
3799 12 50       118 );
3800 12 100       153 return unless $r;
3801 8 50       15 return if $lx->{error};
3802 8         16 lock_hash %$r;
3803 8         114 return $r;
3804             }
3805              
3806             sub parse_column_def($)
3807             {
3808 6     6 0 4 my ($lx)= @_;
3809 6         12 my $r= create($lx, 'ColumnDef', qw(name column_spec));
3810             return unless
3811             $r->{name}= parse_column_name($lx)
3812 6 50 33     13 and $r->{column_spec}= parse_column_spec($lx);
3813 6         16 lock_hash %$r;
3814 6         89 return $r;
3815             }
3816              
3817             sub parse_column_def_or_option($)
3818             {
3819 6     6 0 8 my ($lx)= @_;
3820             return parse_choice($lx,
3821             'interpol' => 'ident',
3822             'ident' => sub {
3823 4     4   10 return parse_column_def($lx);
3824             },
3825             -default => sub {
3826 2     2   5 return parse_table_constraint($lx);
3827             },
3828 6         28 );
3829             }
3830              
3831             sub parse_create_table($)
3832             {
3833 2     2 0 3 my ($lx)= @_;
3834             return unless
3835 2 50       5 expect($lx, \@CREATE_TABLE_INITIAL);
3836              
3837 2         8 my $r= create($lx, ['Stmt','CreateTable'],
3838             qw(subtype if_not_exists table column_def tabconstr tableopt select));
3839 2         5 $r->{subtype}= lexer_shift($lx);
3840              
3841 2 100 66     11 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       5 $r->{table}= parse_table($lx);
3849              
3850 2         3 $r->{column_def}= [];
3851 2         4 $r->{tabconstr}= [];
3852 2 50       4 if (looking_at($lx, '(')) {
3853             return unless
3854 2 50       6 my $spec= parse_list_delim($lx, \&parse_column_def_or_option);
3855              
3856 2         5 $r->{column_def}= [ grep { $_->{kind} eq 'ColumnDef' } @$spec ];
  6         12  
3857 2         6 $r->{tabconstr}= [ grep { $_->{kind} ne 'ColumnDef' } @$spec ];
  6         8  
3858             }
3859              
3860             return unless
3861 2 50       8 $r->{tableopt}= parse_try_list([], $lx, \&parse_table_option);
3862              
3863 2 100 66     5 if (looking_at($lx, 'AS', SHIFT) ||
3864             looking_at($lx, \@SELECT_INITIAL))
3865             {
3866             return unless
3867 1 50       4 $r->{select}= parse_select($lx);
3868             }
3869              
3870 2 0 33     3 unless (scalar(@{ $r->{column_def} }) || $r->{select}) {
  2         7  
3871 0         0 $lx->{error}= 'Either query or at least one column expected';
3872 0         0 return;
3873             }
3874              
3875 2         6 lock_hash %$r;
3876 2         32 return $r;
3877             }
3878              
3879             sub parse_drop_table($)
3880             {
3881 1     1 0 2 my ($lx)= @_;
3882             return unless
3883 1 50       3 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     5 if ($read_dialect{mysql} &&
3890             looking_at($lx, 'IF EXISTS', SHIFT))
3891             {
3892 1         1 $r->{if_exists}= 1;
3893             }
3894              
3895             return unless
3896 1 50       5 $r->{table}= parse_list([], $lx, \&parse_table, ',');
3897              
3898 1         4 $r->{cascade}= looking_at($lx, ['RESTRICT','CASCADE'], SHIFT);
3899              
3900 1         3 lock_hash %$r;
3901 1         14 return $r;
3902             }
3903              
3904             sub parse_column_pos_perhaps($)
3905             {
3906 3     3 0 3 my ($lx)= @_;
3907             return parse_choice($lx,
3908             -default => sub {
3909 1     1   3 return;
3910             },
3911             'FIRST' => sub {
3912 1     1   2 return lexer_shift($lx);
3913             },
3914             'AFTER' => sub {
3915 1     1   3 lexer_shift($lx);
3916 1         2 return ('AFTER', parse_column_name($lx));
3917             },
3918 3         16 );
3919             }
3920              
3921             sub parse_alter_table($)
3922             {
3923 20     20 0 18 my ($lx)= @_;
3924             return unless
3925 20 50       30 expect($lx, \@ALTER_TABLE_INITIAL);
3926              
3927 20         43 my $r= create($lx, ['Stmt','AlterTable'],
3928             qw(subtype functor subfunctor arg online ignore table only));
3929 20         30 $r->{subtype}= lexer_shift($lx);
3930 20         22 $r->{arg}= [];
3931              
3932             return unless
3933 20 50       27 $r->{table}= parse_table($lx);
3934              
3935 20         25 $r->{only}= looking_at($lx, 'ONLY', SHIFT);
3936              
3937             parse_choice($lx,
3938             'DROP CONSTRAINT' => sub {
3939 1     1   2 $r->{functor}= lexer_shift($lx);
3940             return unless
3941 1 50       2 my $constraint= parse_constraint($lx);
3942 1         1 push @{ $r->{arg} }, $constraint, looking_at($lx, ['RESTRICT','CASCADE'], SHIFT);
  1         3  
3943             },
3944              
3945             'DROP COLUMN' => sub {
3946 3     3   5 $r->{functor}= lexer_shift($lx);
3947             return unless
3948 3 50       6 my $column= parse_column_name($lx);
3949 3         3 push @{ $r->{arg} }, $column, looking_at($lx, ['RESTRICT','CASCADE'], SHIFT);
  3         7  
3950             },
3951              
3952             'RENAME COLUMN' => sub {
3953 1     1   2 $r->{functor}= lexer_shift($lx);
3954              
3955             return unless
3956 1 50 33     2 my $column= parse_column_name($lx)
      33        
3957             and expect($lx, 'TO', SHIFT)
3958             and my $column2= parse_column_name($lx);
3959              
3960 1         2 push @{ $r->{arg} }, $column, 'TO', $column2;
  1         3  
3961             },
3962              
3963             'DROP PRIMARY KEY' => sub {
3964 1     1   2 $r->{functor}= lexer_shift($lx);
3965             },
3966              
3967             'ALTER COLUMN' => sub {
3968 6     6   9 $r->{functor}= lexer_shift($lx);
3969 6         5 push @{ $r->{arg} }, parse_column_name($lx);
  6         12  
3970 6 50       10 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         2 push @{ $r->{arg} }, lexer_shift($lx);
  3         7  
3977             },
3978              
3979             'SET DEFAULT' => sub {
3980 1         2 push @{ $r->{arg} }, lexer_shift($lx);
  1         3  
3981 1         2 push @{ $r->{arg} }, parse_expr($lx);
  1         3  
3982             },
3983              
3984             ($read_dialect{postgresql} ?
3985             (
3986             'TYPE' => sub {
3987 2         2 push @{ $r->{arg} }, lexer_shift($lx);
  2         4  
3988 2         2 push @{ $r->{arg} }, parse_type($lx);
  2         7  
3989 2 50       5 return if $lx->{error};
3990 2 100       3 if (my $x= looking_at($lx, 'USING', SHIFT)) {
3991 1         1 push @{ $r->{arg} }, $x, parse_expr($lx);
  1         3  
3992             }
3993             }
3994             )
3995 6 50       34 : ()
3996             ),
3997             );
3998             },
3999              
4000             'RENAME TO' => sub {
4001 1     1   2 $r->{functor}= lexer_shift($lx);
4002 1         1 push @{ $r->{arg} }, parse_table($lx);
  1         4  
4003             },
4004              
4005             'ADD COLUMN' => sub {
4006 2     2   4 $r->{functor}= lexer_shift($lx);
4007 2 100       3 if (looking_at($lx, '(', SHIFT)) {
4008 1         1 push @{ $r->{arg} }, parse_list([], $lx, \&parse_column_def, ',');
  1         4  
4009 1 50       3 return if $lx->{error};
4010 1         3 expect($lx, ')', SHIFT);
4011             }
4012             else {
4013             return unless
4014 1 50 33     2 my $col1= parse_column_name($lx)
4015             and my $spec= parse_column_spec($lx);
4016 1         1 push @{ $r->{arg} }, $col1, $spec, parse_column_pos_perhaps($lx);
  1         4  
4017             }
4018             },
4019              
4020             'ADD' => sub {
4021 1     1   3 $r->{functor}= lexer_shift($lx);
4022 1         2 push @{ $r->{arg} }, parse_table_constraint($lx);
  1         3  
4023             },
4024              
4025             ($read_dialect{mysql} ?
4026             (
4027             'MODIFY COLUMN' => sub {
4028 1     1   2 $r->{functor}= lexer_shift($lx);
4029             return unless
4030 1 50 33     3 my $col1= parse_column_name($lx)
4031             and my $spec= parse_column_spec($lx);
4032 1         1 push @{ $r->{arg} }, $col1, $spec, parse_column_pos_perhaps($lx);
  1         3  
4033             },
4034             'CHANGE COLUMN' => sub {
4035 1     1   3 $r->{functor}= lexer_shift($lx);
4036             return unless
4037 1 50 33     2 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         2 push @{ $r->{arg} }, $col1, $col2, $spec, parse_column_pos_perhaps($lx);
  1         3  
4041             },
4042             'DROP FOREIGN KEY' => sub { # standard SQL: DROP CONSTRAINT
4043 1     1   2 $r->{functor}= lexer_shift($lx);
4044             return unless
4045 1 50       2 my $constraint= parse_constraint($lx);
4046 1         1 push @{ $r->{arg} }, $constraint;
  1         4  
4047             },
4048             'DROP INDEX' => sub {
4049 1     1   2 $r->{functor}= lexer_shift($lx);
4050             return unless
4051 1 50       3 my $index= parse_index($lx);
4052 1         2 push @{ $r->{arg} }, $index;
  1         3  
4053             },
4054             )
4055 20 50       247 : ()
4056             ),
4057             );
4058 20 50       431 return if $lx->{error};
4059              
4060 20         41 lock_hash %$r;
4061 20         369 return $r;
4062             }
4063              
4064             sub parse_stmt_interpol($)
4065             {
4066 3     3 0 5 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     18 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       9 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       8 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         7 my $r= create ($lx, ['Stmt','Interpol'], qw(token));
4089 3         6 $r->{token}= $lx->{token};
4090 3         10 lexer_shift($lx);
4091              
4092 3         8 lock_hash %$r;
4093 3         42 return $r;
4094             }
4095              
4096             sub parse_select_stmt($)
4097             {
4098 6     6 0 8 my ($lx)= @_;
4099             return parse_choice($lx,
4100 6     6   11 'SELECT' => sub { parse_select ($lx) },
4101              
4102             'interpolStmt' => 'interpol',
4103 0     0   0 'interpol' => sub { parse_stmt_interpol ($lx) },
4104 6         25 );
4105             }
4106              
4107             sub parse_stmt($)
4108             {
4109 225     225 0 206 my ($lx)= @_;
4110             return parse_choice($lx,
4111 173     173   284 'SELECT' => sub { parse_select ($lx) },
4112 13     13   30 'INSERT' => sub { parse_insert ($lx) },
4113 9     9   23 'UPDATE' => sub { parse_update ($lx) },
4114 4     4   12 'DELETE' => sub { parse_delete ($lx) },
4115              
4116 1350         1408 (map { $_ => 'CREATE TABLE' } @CREATE_TABLE_INITIAL),
4117 2     2   6 'CREATE TABLE' => sub { parse_create_table($lx) },
4118              
4119 450         647 (map { $_ => 'DROP TABLE' } @DROP_TABLE_INITIAL),
4120 1     1   4 'DROP TABLE' => sub { parse_drop_table($lx) },
4121              
4122 1350         1636 (map { $_ => 'ALTER TABLE' } @ALTER_TABLE_INITIAL),
4123 20     20   27 'ALTER TABLE' => sub { parse_alter_table($lx) },
4124              
4125             'interpolStmt' => 'interpol',
4126 3     3   6 'interpol' => sub { parse_stmt_interpol ($lx) },
4127 225         1038 );
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 228 my ($line_start)= @_;
4206 278         257 my $text= [];
4207 278         788 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         446 lock_keys %$s; # poor-man's bless()
4216 278         1220 return $s;
4217             }
4218              
4219             sub str_append_raw($$)
4220             {
4221 11636     11636 0 8372 my ($s, $text)= @_;
4222 11636         9210 $s->{buff}.= $text;
4223 11636         11355 $s->{line_is}+= ($text =~ tr/\n//);
4224             }
4225              
4226             sub str_sync_line($)
4227             {
4228 5808     5808 0 3892 my ($s)= @_;
4229 5808         8640 while ($s->{line_is} < $s->{line_target}) {
4230 309         349 str_append_raw ($s, "\n");
4231             }
4232             }
4233             sub str_target_line($$)
4234             {
4235 2834     2834 0 2155 my ($s, $n)= @_;
4236 2834 50       3541 my_confess "undefined line number" unless defined $n;
4237 2834         2569 $s->{line_target}= $n;
4238             }
4239              
4240             sub str_append_comma($)
4241             {
4242 7388     7388 0 4702 my ($s)= @_;
4243 7388 100       10358 if ($s->{need_comma}) {
4244 3792         4183 str_append_raw ($s, COMMA_STR);
4245 3792         3382 $s->{need_comma}= 0;
4246             }
4247             }
4248              
4249             sub str_append_perl($$)
4250             {
4251 4347     4347 0 3954 my ($s, $perl)= @_;
4252 4347 50       5876 if ($perl ne '') {
4253 4347         4107 str_append_comma($s);
4254 4347         4082 str_sync_line ($s);
4255 4347         3920 str_append_raw ($s, $perl);
4256 4347         38232 $s->{need_comma}= 1;
4257             }
4258             }
4259              
4260             sub str_append_str($$)
4261             {
4262 2628     2628 0 1968 my ($s, $contents)= @_;
4263 2628         2669 str_append_perl ($s, quote_perl($contents));
4264             }
4265              
4266             sub str_append_join($%)
4267             {
4268 764     764 0 1423 my ($s, %opt)= @_;
4269 764   100     1705 $opt{prefix}||= '';
4270 764   100     1701 $opt{suffix}||= '';
4271 764   100     1284 $opt{sep}||= '';
4272              
4273 764         820 str_append_comma($s);
4274 764         809 str_sync_line ($s);
4275 764 100 66     3284 if ($opt{joinfunc}) {
    100 66        
    100 66        
      66        
      66        
4276             # special case: ignore all other settings
4277 94         209 str_append_raw ($s, "$opt{joinfunc}(");
4278 94         98 $s->{need_comma}= 0;
4279 94         75 push @{ $s->{end_str} }, undef;
  94         191  
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         315 str_append_raw ($s, 'join(');
4290 254         386 str_append_str ($s, $opt{sep});
4291 254         235 $s->{need_comma}= 1;
4292 254         199 push @{ $s->{end_str} }, undef;
  254         445  
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         31 str_append_raw ($s, 'join(');
4302 21         27 str_append_str ($s, '');
4303              
4304 21 50       40 if($opt{prefix} ne '') {
4305 21         25 str_append_str ($s, $opt{prefix});
4306             }
4307              
4308 21   100     21 push @{ $s->{end_str} }, $opt{suffix} || undef;
  21         68  
4309             }
4310             else {
4311             # complex case:
4312 395         545 str_append_raw ($s, __PACKAGE__.'::joinlist(');
4313 395         678 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         385 str_append_comma($s);
4319 395         518 str_append_str ($s, $opt{result0});
4320 395         456 str_append_comma($s);
4321 395         431 str_append_str ($s, $opt{prefix});
4322 395         434 str_append_comma($s);
4323 395         431 str_append_str ($s, $opt{sep});
4324 395         397 str_append_comma($s);
4325 395         431 str_append_str ($s, $opt{suffix});
4326 395         344 $s->{need_comma}= 1;
4327 395         282 push @{ $s->{end_str} }, undef;
  395         713  
4328             }
4329             }
4330              
4331             sub str_append_map($$)
4332             {
4333 173     173 0 170 my ($s,$code)= @_;
4334 173         217 str_append_comma($s);
4335 173         198 str_sync_line ($s);
4336 173         305 str_append_raw ($s, "(map{ $code } ");
4337 173         199 $s->{need_comma}= 0;
4338 173         137 push @{ $s->{end_str} }, undef;
  173         281  
4339             }
4340              
4341             sub str_append_funcall_begin($$$)
4342             {
4343 523     523 0 448 my ($s, $func, $in_list)= @_;
4344 523         592 str_append_comma($s);
4345 523         688 str_sync_line ($s);
4346 523 100       597 if ($in_list) {
4347 277         489 str_append_raw ($s, "(map { $func(");
4348             }
4349             else {
4350 246         419 str_append_raw ($s, "$func(");
4351             }
4352 523         510 $s->{need_comma}= 0;
4353 523         419 push @{ $s->{end_str} }, undef;
  523         749  
4354             }
4355              
4356             sub str_append_funcall_end($$)
4357             {
4358 523     523 0 397 my ($s, $in_list)= @_;
4359 523 100       822 if ($in_list) {
4360 277         338 str_append_perl ($s, '$_');
4361 277         257 str_append_raw ($s, ') }');
4362 277         289 $s->{need_comma}= 0;
4363             }
4364             }
4365              
4366             sub str_append_funcall($$$)
4367             {
4368 445     445 0 492 my ($s, $code, $in_list)= @_;
4369 445         631 str_append_funcall_begin ($s, $code, $in_list);
4370 445         589 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 1315 my ($s)= @_;
4380 1450         976 my $end_str= pop @{ $s->{end_str} };
  1450         1795  
4381 1450 100       2176 if (defined $end_str) {
4382 18         24 str_append_str($s, $end_str);
4383             }
4384 1450         1608 str_append_raw ($s, ')');
4385 1450         35615 $s->{need_comma}= 1;
4386             }
4387              
4388             sub str_get_string($)
4389             {
4390 275     275 0 383 my ($s)= @_;
4391 275 50       490 return '()' if $s->{buff} eq '';
4392 275         494 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 539 my ($str, $list, $parens, %opt)= @_;
4404 354         915 local $SIG{__DIE__}= \&my_confess;
4405              
4406             # set line to first element (if any):
4407 354 50       552 if (scalar(@$list)) {
4408 354         533 str_target_line ($str, $list->[0]{line});
4409             }
4410              
4411             # joining, delimiters, result if empty:
4412             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 354 100       1096 );
4418              
4419             # map?
4420 354 100       733 if (my $x= $opt{map}) {
4421 1         2 str_append_comma ($str);
4422 1         124 str_sync_line ($str);
4423 1         3 str_append_raw ($str, "map{$x} ");
4424 1         2 $str->{need_comma}= 0;
4425             };
4426              
4427             # the list:
4428 354         424 for my $l (@$list) {
4429 428         588 str_append_thing ($str, $l, IN_LIST, $parens);
4430             }
4431              
4432             # end:
4433 352         560 str_append_end($str);
4434             }
4435              
4436             sub interpol_set_context ($$);
4437              
4438             sub perl_val($$$)
4439             {
4440 1056     1056 0 1088 my ($token, $ctxt, $allow)= @_;
4441              
4442             my_confess "Expected ".(english_or \"e_perl, $allow).", but found '$token->{kind}'"
4443             if $allow &&
4444 1056 50 66     1874 scalar(grep { $token->{kind} eq $_ } flatten($allow)) == 0;
  96         188  
4445              
4446             return switch($token->{kind},
4447 526     526   803 'ident' => sub { quote_perl($token->{value}) },
4448 9     9   22 '*' => sub { __PACKAGE__.'::ASTERISK' },
4449 2     2   4 '?' => sub { __PACKAGE__.'::QUESTION' },
4450 2     2   5 'NULL' => sub { __PACKAGE__.'::NULL' },
4451 1     1   4 'TRUE' => sub { __PACKAGE__.'::TRUE' },
4452 0     0   0 'FALSE' => sub { __PACKAGE__.'::FALSE' },
4453 0     0   0 'UNKNOWN' => sub { __PACKAGE__.'::UNKNOWN' },
4454 1     1   3 'DEFAULT' => sub { __PACKAGE__.'::DEFAULT' },
4455             -default => sub {
4456 515 50   515   1694 if ($token->{kind} =~ /^interpol/) {
4457 515         745 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         8177 );
4464             }
4465              
4466             sub perl_val_list($$$)
4467             {
4468 43     43 0 43 my ($token, $ctxt, $allow)= @_;
4469 43         57 my $s= perl_val($token, $ctxt, $allow);
4470              
4471 43 100       281 if ($token->{perltype} eq 'hash') {
4472 9         26 return "sort keys $s";
4473             }
4474             else {
4475 34         86 return $s;
4476             }
4477             }
4478              
4479             sub token_pos($)
4480             {
4481 3     3 0 4 my ($token)= @_;
4482 3         40 return "$token->{lx}{file}:".($token->{line} + $token->{lx}{line_start});
4483             }
4484              
4485             sub lx_pos($)
4486             {
4487 2     2 0 2 my ($lx)= @_;
4488 2         23 return "$lx->{file}:".($lx->{line} + $lx->{line_start});
4489             }
4490              
4491             sub croak_unless_scalar($)
4492             {
4493 861     861 0 727 my ($token)= @_;
4494             die token_pos($token).": ".
4495             "Error: Scalar context, embedded Perl must not be syntactic array or hash.\n"
4496 861 100 66     5370 if $token->{perltype} eq 'array' || $token->{perltype} eq 'hash';
4497             }
4498              
4499             sub str_append_typed($$$$$%)
4500             {
4501 474     474 0 701 my ($str, $callback, $ctxt, $thing, $in_list, %opt)= @_;
4502 474         772 my $q_val= perl_val ($thing->{token}, $ctxt, undef);
4503              
4504 474 100 100     3751 if (!$in_list ||
    100          
4505             $thing->{token}{perltype} eq 'scalar')
4506             {
4507 358         476 croak_unless_scalar ($thing->{token});
4508 356         878 str_append_perl ($str, __PACKAGE__."::${callback}($q_val)");
4509             }
4510             elsif ($thing->{token}{perltype} eq 'hash') {
4511 19 100       43 if ($opt{hash}) {
    50          
4512 16         49 str_append_perl ($str, __PACKAGE__."::${callback}_hash($q_val)");
4513             }
4514             elsif ($opt{hashkeys}) {
4515 3         11 str_append_map ($str, __PACKAGE__."::${callback}(\$_)");
4516 3         9 str_append_perl ($str, "sort keys $q_val");
4517 3         6 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         267 str_append_map ($str, __PACKAGE__."::${callback}(\$_)");
4525 97         143 str_append_perl ($str, $q_val);
4526 97         120 str_append_end ($str);
4527             }
4528             }
4529              
4530             sub is_multicol($);
4531             sub is_multicol($)
4532             {
4533 510     510 0 392 my ($thing) = @_;
4534             return switch ($thing->{kind},
4535             'ExprAs' => sub{
4536 165     165   222 return is_multicol($thing->{expr});
4537             },
4538             'Expr' => sub {
4539 165 100   165   277 if ($thing->{type} eq 'column') {
4540 90         115 return is_multicol($thing->{arg});
4541             }
4542 75         519 return 0;
4543             },
4544             'Column' => sub {
4545 90     90   143 return is_multicol($thing->{ident_chain}[-1]);
4546             },
4547             '*' => sub {
4548 4     4   57 return 1;
4549             },
4550             'interpol' => sub {
4551 21     21   273 return $thing->{perltype} ne 'scalar';
4552             },
4553             -default => sub {
4554 65     65   825 return 0;
4555             },
4556 510         2840 );
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 651 my ($str, $in_list, $family, @token)= @_;
4570 509         543 my $func= lc($family);
4571              
4572 509   66     1580 my $ctxt= $ident_context{$family}{scalar @token} ||
4573             (scalar(@token) == 1 ?
4574             [ $family ]
4575             : [ map 'none', 1..scalar(@token) ]
4576             );
4577              
4578 509         567 my $n= scalar(@token);
4579 509         839 my @non_scalar_i= grep { $token[$_]{perltype} ne 'scalar' } 0..$n-1;
  551         1287  
4580              
4581 509 100 100     1551 if (!$in_list ||
    100          
4582             scalar(@non_scalar_i) == 0)
4583             {
4584 470         560 for my $a (@token) { croak_unless_scalar ($a); }
  503         611  
4585             my $q_vals= join(",",
4586             map
4587 469         566 { perl_val($token[$_], $ctxt->[$_], undef) }
  502         822  
4588             0..$n-1
4589             );
4590 469         1218 str_append_perl ($str, __PACKAGE__."::${func}${n}($q_vals)");
4591             }
4592             elsif (scalar(@non_scalar_i) == 1) {
4593             str_append_map ($str,
4594             __PACKAGE__."::${func}${n}(".
4595             join(",",
4596             map {
4597 35 100       102 ($token[$_]{perltype} eq 'scalar' ?
  40         146  
4598             perl_val($token[$_], $ctxt->[$_], undef)
4599             : '$_'
4600             )
4601             }
4602             0..$n-1
4603             ).
4604             ")"
4605             );
4606 35         43 my ($i)= @non_scalar_i;
4607 35         79 str_append_perl ($str, perl_val_list($token[$i], $ctxt->[$i], undef));
4608 35         50 str_append_end ($str);
4609             }
4610             else {
4611 4 50       12 my $f_ident= "${func}${n}_".join('', map{ $_->{perltype} eq 'scalar' ? 1 : 'n' } @token);
  8         25  
4612             str_append_perl ($str,
4613             __PACKAGE__."::$f_ident(".
4614             join(",",
4615             map {
4616 4 50       14 ($token[$_]{perltype} eq 'scalar' ?
  8         21  
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 124 my ($str, $limit_cnt, $limit_offset)= @_;
4631              
4632 106 100 66     380 if (defined $limit_cnt || defined $limit_offset) {
4633 5         8 my $limit_cnt_str= 'undef';
4634 5 50       11 if ($limit_cnt) {
4635 5         15 $limit_cnt_str= perl_val($limit_cnt, 'Expr', ['interpol', 'interpolExpr', '?']);
4636             }
4637              
4638 5         29 my $limit_offset_str= 'undef';
4639 5 100       15 if ($limit_offset) {
4640 3         11 $limit_offset_str= perl_val($limit_offset, 'Expr', ['interpol', 'interpolExpr', '?']);
4641             }
4642              
4643 5         32 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 21 my ($str, $thing, $in_list)= @_;
4650 18 50       26 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         26 str_append_join ($str, prefix => '(', suffix => ')', never_empty => 1);
4657 18         29 str_append_thing ($str, $thing, $in_list, NO_PARENS);
4658 18         1037 str_append_end ($str);
4659             }
4660             }
4661              
4662             sub str_append_table_key($$$)
4663             {
4664 3     3 0 5 my ($str, $thing, $type)= @_;
4665 3         4 str_append_join ($str, sep => ' ');
4666 3 50       8 if (my $x= $thing->{constraint}) {
4667 3         4 str_append_str ($str, 'CONSTRAINT');
4668 3         7 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4669             }
4670 3         95 str_append_str ($str, $type);
4671 3 100       6 if (my $x= $thing->{index_type}) {
4672 1         252 str_append_str ($str, "USING $x");
4673             }
4674 3         8 str_append_list ($str, $thing->{column}, NO_PARENS, prefix=>'(', suffix=>')');
4675 3         3 for my $o (@{ $thing->{index_option} }) {
  3         7  
4676 0         0 str_append_thing ($str, $o, IN_LIST, NO_PARENS);
4677             }
4678 3 100       8 if (my $x= $thing->{reference}) {
4679 2         4 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4680             }
4681 3         53 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 2829 my ($str, $thing, $in_list, $parens)= @_;
4708 2820         6297 local $SIG{__DIE__}= \&my_confess;
4709              
4710             # simple things to append:
4711 2820 100       4072 unless (defined $thing) {
4712 16         22 str_append_perl ($str, 'undef');
4713 16         31 return;
4714             }
4715 2804 100       3863 unless (ref $thing) {
4716 380         413 str_append_str ($str, $thing);
4717 380         681 return;
4718             }
4719 2424 100       3395 if (ref($thing) eq 'ARRAY') {
4720 1         2 str_append_list ($str, $thing, NO_PARENS, prefix => '(', suffix => ')');
4721 1         3 return;
4722             }
4723              
4724             # normal structure:
4725 2423         3185 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         165 my $type_name = 'SelectStmt';
4733 178 100       144 if (scalar(@{ $thing->{expr_list} }) == 1) {
  178         354  
4734 165 100       275 unless (is_multicol($thing->{expr_list}[0])) {
4735 149         153 $type_name = 'SelectStmtSingle';
4736             }
4737             }
4738              
4739             # generate:
4740 178         937 str_append_funcall ($str, __PACKAGE__.'::'.$type_name.'->obj', $in_list);
4741 178         235 str_append_join ($str, never_empty => 1);
4742              
4743             str_append_list ($str, $thing->{expr_list}, NO_PARENS,
4744             prefix => join(' ', 'SELECT',
4745 178         218 @{ $thing->{opt_front} }
  178         509  
4746             ).' '
4747             );
4748              
4749 176 100       364 if (my $x= $thing->{from}) {
4750 94         128 str_append_list ($str, $x, NO_PARENS, prefix => ' FROM ');
4751              
4752 94 50       193 if (my $x= $thing->{join}) {
4753 94 100       150 if (@$x) {
4754 4         10 str_append_map ($str, '" $_" ');
4755 4         5 for my $xi (@$x) {
4756 5         11 str_append_thing ($str, $xi, IN_LIST, NO_PARENS);
4757             }
4758 4         7 str_append_end ($str);
4759             }
4760             }
4761 94 100       159 if (my $x= $thing->{where}) {
4762 44         69 str_target_line ($str, $x->{line});
4763 44         67 str_append_str ($str, ' WHERE ');
4764 44         81 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4765             }
4766 93 100       2057 if (my $x= $thing->{group_by}) {
4767 6         8 my $suffix= '';
4768 6 100       13 if ($thing->{group_by_with_rollup}) {
4769 1         2 $suffix= ' WITH ROLLUP';
4770             }
4771 6         11 str_append_list ($str, $x, NO_PARENS,
4772             prefix => ' GROUP BY ',
4773             suffix => $suffix,
4774             result0 => '',
4775             );
4776             }
4777 93 100       173 if (my $x= $thing->{having}) {
4778 1         3 str_target_line ($str, $x->{line});
4779 1         2 str_append_str ($str, ' HAVING ');
4780 1         4 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4781             }
4782 93 100       204 if (my $x= $thing->{order_by}) {
4783 8         17 str_append_list ($str, $x, NO_PARENS,
4784             prefix => ' ORDER BY ',
4785             result0 => ''
4786             );
4787             }
4788 93         168 str_append_limit ($str, $thing->{limit_cnt}, $thing->{limit_offset});
4789              
4790 93         78 str_append_str ($str, join('', map " $_", @{ $thing->{opt_back} }));
  93         292  
4791             }
4792              
4793 175         244 str_append_end ($str);
4794 175         207 str_append_end ($str);
4795             },
4796             'Delete' => sub {
4797 4         11 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
4798 4         9 str_append_join ($str, never_empty => 1);
4799              
4800             str_append_list ($str, $thing->{from}, NO_PARENS,
4801             prefix =>
4802             join(' ',
4803             'DELETE',
4804 4         5 @{ $thing->{opt_front} },
4805             'FROM',
4806 4         8 @{ $thing->{from_opt_front} },
  4         22  
4807             ).' '
4808             );
4809              
4810 4 100       11 if (my $x= $thing->{using}) {
4811 2         4 str_append_list ($str, $x, NO_PARENS,
4812             prefix => ' USING ',
4813             result0 => ''
4814             );
4815             }
4816              
4817 4 50       12 if (my $x= $thing->{join}) {
4818 4 100       9 if (@$x) {
4819 2         7 str_append_map ($str, '" $_" ');
4820 2         4 for my $xi (@$x) {
4821 3         6 str_append_thing ($str, $xi, IN_LIST, NO_PARENS);
4822             }
4823 2         4 str_append_end ($str);
4824             }
4825             }
4826 4 100       11 if (my $x= $thing->{where}) {
4827 3         8 str_target_line ($str, $x->{line});
4828 3         5 str_append_str ($str, ' WHERE ');
4829 3         8 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4830             }
4831 4 50       157 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         12 str_append_limit ($str, $thing->{limit_cnt}, $thing->{limit_offset});
4838              
4839 4         8 str_append_end ($str);
4840 4         5 str_append_end ($str);
4841             },
4842             'Insert' => sub {
4843 13         28 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
4844 13         23 str_append_join ($str, never_empty => 1);
4845              
4846             str_append_str ($str,
4847             join(' ',
4848             'INSERT',
4849 13         15 @{ $thing->{opt_front} },
  13         45  
4850             'INTO',
4851             ).' '
4852             );
4853              
4854 13         25 str_append_thing ($str, $thing->{into}, NOT_IN_LIST, NO_PARENS);
4855              
4856 13 100       435 if (my $col= $thing->{column}) {
4857 5         12 str_append_list ($str, $col, NO_PARENS, prefix => ' (', suffix => ')');
4858             }
4859            
4860 13 100       42 if (my $val= $thing->{value}) {
    50          
    0          
    0          
4861 5         10 str_append_str ($str, ' VALUES ');
4862 5         8 str_append_list ($str, $val, NO_PARENS);
4863             }
4864             elsif (my $set= $thing->{set}) {
4865 8         18 str_append_funcall ($str, __PACKAGE__."::set2values", NOT_IN_LIST);
4866 8         14 for my $l (@$set) {
4867 16         28 str_append_thing ($str, $l, IN_LIST, NO_PARENS);
4868             }
4869 8         16 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       38 if (my $x= $thing->{duplicate_update}) {
4883 1         2 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         20 str_append_end ($str);
4888 13         18 str_append_end ($str);
4889             },
4890             'Update' => sub {
4891 9         19 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
4892 9         18 str_append_join ($str, never_empty => 1);
4893              
4894             str_append_list ($str, $thing->{table}, NO_PARENS,
4895             prefix => join(' ', 'UPDATE',
4896 9         14 @{ $thing->{opt_front} }
  9         27  
4897             ).' '
4898             );
4899              
4900 9 100       23 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       22 if (my $x= $thing->{join}) {
4907 9 50       15 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       21 if (my $x= $thing->{set}) {
4916 9         15 str_append_list ($str, $x, NO_PARENS,
4917             prefix => ' SET ',
4918             result0 => '' # this is an error.
4919             );
4920             }
4921 9 50       26 if (my $x= $thing->{where}) {
4922 9         15 str_target_line ($str, $x->{line});
4923 9         13 str_append_str ($str, ' WHERE ');
4924 9         15 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4925             }
4926 9 100       384 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         18 str_append_limit ($str, $thing->{limit_cnt}, $thing->{limit_offset});
4933              
4934 9         11 str_append_end ($str);
4935 9         13 str_append_end ($str);
4936             },
4937             'CreateTable' => sub {
4938 2         5 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
4939 2         4 str_append_join ($str, never_empty => 1);
4940              
4941 2         7 str_append_str ($str, "$thing->{subtype} ");
4942 2 100       6 if ($thing->{if_not_exists}) {
4943 1         2 str_append_str ($str, 'IF NOT EXISTS ');
4944             }
4945 2         4 str_append_thing ($str, $thing->{table}, NOT_IN_LIST, NO_PARENS);
4946              
4947             my @tabspec= (
4948 2         5 @{ $thing->{column_def} },
4949 2         55 @{ $thing->{tabconstr} }
  2         5  
4950             );
4951 2         4 str_append_list ($str, \@tabspec, NO_PARENS,
4952             result0 => '',
4953             prefix => ' (',
4954             suffix => ')'
4955             );
4956              
4957 2         5 str_append_list ($str, $thing->{tableopt}, NO_PARENS,
4958             result0 => '',
4959             prefix => ' ',
4960             sep => ' ',
4961             );
4962              
4963 2 100       5 if (my $x= $thing->{select}) {
4964 1         2 str_append_str ($str, ' AS ');
4965 1         3 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
4966             }
4967              
4968 2         85 str_append_end ($str);
4969 2         3 str_append_end ($str);
4970             },
4971             'DropTable' => sub {
4972 1         3 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
4973 1         3 str_append_join ($str, never_empty => 1);
4974              
4975 1         3 str_append_str ($str, "$thing->{subtype} ");
4976 1 50       3 if ($thing->{if_exists}) {
4977 1         2 str_append_str ($str, 'IF EXISTS ');
4978             }
4979 1         3 str_append_list ($str, $thing->{table}, NO_PARENS);
4980              
4981 1 50       3 if (my $x= $thing->{cascade}) {
4982 1         3 str_append_str ($str, " $x");
4983             }
4984 1         2 str_append_end ($str);
4985 1         2 str_append_end ($str);
4986             },
4987             'AlterTable' => sub {
4988 20         36 str_append_funcall ($str, __PACKAGE__.'::Stmt->obj', $in_list);
4989 20         31 str_append_join ($str, never_empty => 1);
4990              
4991 20         44 str_append_str ($str, "$thing->{subtype} ");
4992 20 50       39 if ($thing->{only}) {
4993 0         0 str_append_str ($str, 'ONLY ');
4994             }
4995 20         32 str_append_thing ($str, $thing->{table}, NOT_IN_LIST, NO_PARENS);
4996              
4997 20         541 str_append_join ($str, sep => ' ', prefix => ' ');
4998 20         23 for my $l ($thing->{functor}, @{ $thing->{arg} }) {
  20         32  
4999 61         81 str_append_thing ($str, $l, NOT_IN_LIST, NO_PARENS);
5000             }
5001 20         30 str_append_end ($str);
5002              
5003 20         22 str_append_end ($str);
5004 20         22 str_append_end ($str);
5005             },
5006             'Interpol' => sub {
5007 3         8 str_append_typed ($str, 'stmt', 'Stmt', $thing, $in_list);
5008             },
5009 230     141   2451 );
5010             },
5011              
5012             'TableOption' => sub {
5013             switch ($thing->{type},
5014             'interpol' => sub {
5015 2         5 str_append_typed ($str, 'tableopt', 'TableOption', $thing, $in_list);
5016             },
5017             'literal' => sub {
5018 6         11 str_append_join ($str, sep => ' ');
5019 6         10 str_append_str ($str, $thing->{name});
5020 6         12 str_append_thing ($str, $thing->{value}, NOT_IN_LIST, NO_PARENS);
5021 6         186 str_append_end ($str);
5022             }
5023 8     3   30 );
5024             },
5025              
5026             'Keyword' => sub {
5027 1     0   2 str_append_str ($str, $thing->{keyword});
5028             },
5029              
5030             'Join' => sub {
5031 15 100   9   26 if ($thing->{type} eq 'interpol') {
5032 5         10 str_append_typed ($str, 'joinclause', 'Join', $thing, $in_list);
5033             }
5034             else {
5035 10         19 str_append_join ($str, result0 => '');
5036              
5037 10 100       47 if ($thing->{natural}) {
5038 3 50       6 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         17 str_append_str ($str, "$thing->{type} JOIN ");
5047             }
5048              
5049 10         18 str_append_list ($str, $thing->{table}, NO_PARENS);
5050              
5051 10 100       29 if (my $on= $thing->{on}) {
    100          
5052 3         7 str_append_str ($str, ' ON ');
5053 3         6 str_append_thing ($str, $on, NOT_IN_LIST, NO_PARENS);
5054             }
5055             elsif (my $using= $thing->{using}) {
5056 2         4 str_append_str ($str, ' USING (');
5057 2         4 str_append_list ($str, $using, NO_PARENS);
5058 2         3 str_append_str ($str, ')');
5059             };
5060              
5061 10         151 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   442 str_append_ident_chain ($str, $in_list, $thing->{kind}, @{ $thing->{ident_chain} });
  509         1025  
5075             },
5076              
5077             'TableAs' => sub {
5078 122 100   102   196 if (my $x= $thing->{as}) {
5079             # Oracle does not allows AS in table aliases. But this module
5080             # does not allow leaving it out. To avoid generating what
5081             # this module cannot read back in the default case, check for
5082             # the write dialect.
5083 3 50       8 if ($write_dialect eq 'oracle') {
5084 0         0 str_append_join ($str, sep => ' ', never_empty => 1);
5085             }
5086             else {
5087 3         6 str_append_join ($str, sep => ' AS ', never_empty => 1);
5088             }
5089 3         9 str_append_thing ($str, $thing->{table}, NOT_IN_LIST, NO_PARENS);
5090 3         90 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
5091 3         79 str_append_end ($str);
5092             }
5093             else {
5094 119         178 str_append_thing ($str, $thing->{table}, $in_list, NO_PARENS);
5095             }
5096             },
5097              
5098             'ExprAs' => sub {
5099 224 100   123   335 if (my $x= $thing->{as}) {
5100 4         9 str_append_join ($str, sep => ' AS ', never_empty => 1);
5101 4         8 str_append_thing ($str, $thing->{expr}, NOT_IN_LIST, NO_PARENS);
5102 3         211 str_append_thing ($str, $x, NOT_IN_LIST, NO_PARENS);
5103 3         80 str_append_end ($str);
5104             }
5105             else {
5106 220         322 str_append_thing ($str, $thing->{expr}, $in_list, $parens);
5107             }
5108             },
5109             'Order' => sub {
5110             switch($thing->{type},
5111             'interpol' => sub {
5112 18 100       33 if ($thing->{desc}) {
5113 5         9 str_append_typed ($str, 'desc', 'Order', $thing, $in_list, hashkeys => 1);
5114             }
5115             else {
5116 13         22 str_append_typed ($str, 'asc', 'Order', $thing, $in_list, hashkeys => 1);
5117             }
5118             },
5119             'expr' => sub {
5120 17 100       34 if ($thing->{desc}) {
5121 5         11 str_append_map ($str, __PACKAGE__.'::desc($_)');
5122 5         10 str_append_thing ($str, $thing->{expr}, $in_list, NO_PARENS);
5123 5         379 str_append_end ($str);
5124             }
5125             else {
5126 12         22 str_append_thing ($str, $thing->{expr}, $in_list, NO_PARENS);
5127             }
5128             },
5129 35     17   132 );
5130             },
5131             'TypeList' => sub {
5132             switch($thing->{type},
5133             'interpol' => sub {
5134 0         0 str_append_typed ($str, 'typelist', 'Type', $thing, $in_list);
5135             },
5136              
5137             'explicit' => sub {
5138 1         4 str_append_list ($str, $thing->{arg}, NO_PARENS, prefix => '(', suffix => ')');
5139             # may not be empty!
5140             },
5141 1     0   5 );
5142             },
5143             'Type' => sub {
5144             switch ($thing->{type},
5145             'interpol' => sub {
5146 8         12 str_append_typed ($str, 'type', 'Type', $thing, $in_list);
5147             },
5148             'base' => sub {
5149 22         38 str_append_perl ($str, __PACKAGE__.'::Type->new()');
5150             },
5151 30     15   99 );
5152             },
5153             'TypePost' => sub {
5154 56 50   23   80 return str_append_parens ($str, $thing, NOT_IN_LIST)
5155             if $parens;
5156              
5157 56         136 str_append_funcall_begin ($str, __PACKAGE__.'::type_'.$thing->{functor}, $in_list);
5158 56         43 for my $arg (@{ $thing->{arg} }) {
  56         99  
5159 68         108 str_append_thing ($str, $arg, NOT_IN_LIST, NO_PARENS);
5160             }
5161 56         81 str_append_funcall_end ($str, $in_list);
5162 56         80 str_append_thing ($str, $thing->{base}, $in_list, NO_PARENS);
5163 56         1375 str_append_end ($str);
5164             },
5165             'ColumnDef' => sub {
5166 6     1   11 str_append_join ($str, sep => ' ');
5167 6         12 str_append_thing ($str, $thing->{name}, NOT_IN_LIST, NO_PARENS);
5168 6         164 str_append_thing ($str, $thing->{column_spec}, NOT_IN_LIST, NO_PARENS);
5169 6         145 str_append_end ($str);
5170             },
5171              
5172             'ColumnSpec' => sub {
5173             switch ($thing->{type},
5174             'interpol' => sub {
5175 7         11 str_append_typed ($str, 'colspec', 'ColumnSpec', $thing, $in_list);
5176             },
5177             'base' => sub {
5178 9         20 str_append_funcall ($str, __PACKAGE__.'::ColumnSpec->new', $in_list);
5179 9         17 str_append_thing ($str, $thing->{datatype}, $in_list, NO_PARENS);
5180 9         210 str_append_end ($str);
5181             }
5182 16     4   65 );
5183             },
5184             'ColumnSpecPost' => sub {
5185 22 50   4   31 return str_append_parens ($str, $thing, NOT_IN_LIST)
5186             if $parens;
5187              
5188 22         60 str_append_funcall_begin ($str, __PACKAGE__.'::colspec_'.$thing->{functor}, $in_list);
5189 22         16 for my $arg (@{ $thing->{arg} }) {
  22         42  
5190 61         76 str_append_thing ($str, $arg, NOT_IN_LIST, NO_PARENS);
5191             }
5192 22         33 str_append_funcall_end ($str, $in_list);
5193 22         29 str_append_thing ($str, $thing->{base}, $in_list, NO_PARENS);
5194 22         514 str_append_end ($str);
5195             },
5196              
5197             'TableConstraint' => sub {
5198             switch($thing->{type},
5199             'primary_key' => sub {
5200 0         0 str_append_table_key ($str, $thing, 'PRIMARY KEY');
5201             },
5202             'unique' => sub {
5203 1         4 str_append_table_key ($str, $thing, 'UNIQUE');
5204             },
5205             'fulltext' => sub {
5206 0         0 str_append_table_key ($str, $thing, 'FULLTEXT');
5207             },
5208             'spatial' => sub {
5209 0         0 str_append_table_key ($str, $thing, 'SPATIAL');
5210             },
5211             'index' => sub {
5212 0         0 str_append_table_key ($str, $thing, 'INDEX');
5213             },
5214             'foreign_key' => sub {
5215 2         3 str_append_table_key ($str, $thing, 'FOREIGN KEY');
5216             },
5217 3     0   19 );
5218             },
5219              
5220             'IndexOption' => sub {
5221             switch($thing->{type},
5222             'using' => sub {
5223 0         0 str_append_str ($str, "USING $thing->{arg}");
5224             }
5225 0     0   0 );
5226             },
5227              
5228             'References' => sub {
5229             # table column match on_delete on_update));
5230             str_append_join ($str, sep => ' ',
5231             prefix => 'REFERENCES ',
5232             suffix =>
5233 4         8 join('', map { " $_" }
5234             ($thing->{match} ?
5235             ('MATCH', $thing->{match})
5236             : ()
5237             ),
5238             ($thing->{on_delete} ?
5239             ('ON DELETE', $thing->{on_delete})
5240             : ()
5241             ),
5242             ($thing->{on_update} ?
5243             ('ON UPDATE', $thing->{on_update})
5244 3 100   0   15 : ()
    100          
    50          
5245             ),
5246             )
5247             );
5248 3         7 str_append_thing ($str, $thing->{table}, NOT_IN_LIST, NO_PARENS);
5249 3         79 str_append_list ($str, $thing->{column}, NO_PARENS,
5250             prefix => '(', suffix => ')', result0 => '');
5251 3         5 str_append_end ($str);
5252             },
5253              
5254             'CharUnit' => sub {
5255 1     0   2 str_append_str ($str, $thing->{name});
5256             },
5257              
5258             'ExprList' => sub {
5259             switch($thing->{type},
5260             'interpol' => sub {
5261 5         12 str_append_typed ($str, 'exprlist', 'Expr', $thing, $in_list);
5262             },
5263              
5264             'explicit' => sub {
5265 6         17 str_append_list ($str, $thing->{arg}, NO_PARENS, prefix => '(', suffix => ')');
5266             # may not be empty!
5267             },
5268 11     8   35 );
5269             },
5270             'ExprEmpty' => sub {
5271             # Append an empty string. Must have an operand here, otherwise
5272             # parameters might get mixed up.
5273 18     10   30 str_append_str($str, '');
5274             },
5275             'Check' => sub {
5276 9     9   17 str_append_join ($str, joinfunc => __PACKAGE__.'::Check->obj');
5277 9         20 str_append_thing ($str, $thing->{expr}, NOT_IN_LIST, NO_PARENS);
5278 9         416 str_append_end ($str);
5279             },
5280             'Expr' => sub {
5281             switch($thing->{type},
5282             'limit' => sub {
5283 24         58 my $limit_cnt_str= perl_val($thing->{arg}, 'Expr',
5284             ['interpol', 'interpolExpr', '?']);
5285 24         194 str_append_perl ($str, __PACKAGE__."::limit_number($limit_cnt_str)");
5286             },
5287             'interpol' => sub {
5288             my $func= $thing->{maybe_check} ?
5289             'expr_or_check'
5290             : ($thing->{token}{type} eq 'num' ||
5291 375 100 100     1733 $thing->{token}{type} eq 'string' ||
    100          
5292             !$parens) ?
5293             'expr'
5294             : 'exprparen';
5295 375         690 str_append_typed ($str, $func, 'Expr', $thing, $in_list, hash => 1);
5296             },
5297             'column' => sub {
5298 314         502 str_append_thing ($str, $thing->{arg}, $in_list, NO_PARENS);
5299             },
5300             '()' => sub {
5301 11         22 str_append_thing ($str, $thing->{arg}, $in_list, PARENS);
5302             },
5303             'subquery' => sub {
5304 6         10 str_append_funcall ($str, __PACKAGE__.'::subquery', $in_list);
5305 6         10 str_append_thing ($str, $thing->{arg}, $in_list, NO_PARENS);
5306 6         317 str_append_end ($str);
5307             },
5308             'prefix1' => sub {
5309 2         4 $in_list= NOT_IN_LIST; # just to be sure
5310 2 50       5 return str_append_parens ($str, $thing, NOT_IN_LIST)
5311             if $parens;
5312 2 50       2 die 'Expected exactly 1 argument' unless scalar(@{ $thing->{arg} }) == 1;
  2         6  
5313              
5314 2         8 str_append_join ($str,
5315             prefix => "$thing->{functor}{value} ",
5316             never_empty => 1
5317             );
5318 2         6 str_append_thing ($str, $thing->{arg}[0], NOT_IN_LIST, PARENS);
5319 1         54 str_append_end ($str);
5320             },
5321             'prefixn' => sub {
5322 1         2 $parens= NO_PARENS; # just to be sure
5323 1 50       1 die 'Expected exactly 1 argument' unless scalar(@{ $thing->{arg} }) == 1;
  1         4  
5324              
5325 1 50       2 if ($in_list) {
5326 0         0 str_append_map ($str, "'$thing->{functor}{value} '.(\$_)");
5327             }
5328             else {
5329 1         3 str_append_join ($str,
5330             prefix => "$thing->{functor}{value} ",
5331             never_empty => 1
5332             );
5333             }
5334 1         4 str_append_thing ($str, $thing->{arg}[0], $in_list, PARENS);
5335 1         41 str_append_end ($str);
5336             },
5337              
5338             'infix2' => sub {
5339 89         80 $in_list= NOT_IN_LIST; # just to be sure
5340 89 100       152 return str_append_parens ($str, $thing, NOT_IN_LIST)
5341             if $parens;
5342              
5343 85         91 my $f= $thing->{functor};
5344 85         174 str_append_join ($str, joinfunc => __PACKAGE__.'::Infix->obj');
5345 85         225 str_append_str ($str, $thing->{functor}{value});
5346 85         182 str_append_thing ($str, $thing->{arg}[0], NOT_IN_LIST, PARENS);
5347 85         5586 str_append_thing ($str, $thing->{arg}[1], NOT_IN_LIST, PARENS);
5348 85         3916 str_append_end ($str);
5349             },
5350              
5351             'infix23' => 'infix3',
5352             'infix3' => sub {
5353 5         7 $in_list= NOT_IN_LIST; # just to be sure
5354 5 100       14 return str_append_parens ($str, $thing, NOT_IN_LIST)
5355             if $parens;
5356              
5357 4         8 my $f= $thing->{functor};
5358 4         8 str_append_join ($str, never_empty => 1);
5359 4         13 str_append_thing ($str, $thing->{arg}[0], NOT_IN_LIST, PARENS);
5360 4         355 str_append_str ($str, " $thing->{functor}{value} ");
5361 4         59 str_append_thing ($str, $thing->{arg}[1], NOT_IN_LIST, PARENS);
5362 4 100       265 if (scalar(@{ $thing->{arg} }) == 3) {
  4         17  
5363 2         6 str_append_str ($str, " $thing->{functor}{value2} ");
5364 2         4 str_append_thing ($str, $thing->{arg}[2], NOT_IN_LIST, PARENS);
5365             }
5366 4         129 str_append_end ($str);
5367             },
5368              
5369             # prefix and suffix allow bitwise application:
5370             # Currently not supported via _prefix() and _suffix() helper
5371             # functions, but may be later. (Needs only a little rewrite
5372             # here. The helper functions don't need to be changed.)
5373             'prefix()' => 'prefix',
5374             'suffix' => 'prefix',
5375             'prefix' => sub {
5376 63 100       112 if ($thing->{type} eq 'prefix()') { # for AND() and OR() as functors
5377 20         21 $in_list = NOT_IN_LIST;
5378             }
5379 63         68 my $f= $thing->{functor};
5380 63 50 50     213 my $fk= $functor_kind{$f->{type} || ''}
5381             or die "Expected $thing->{type} to be mapped by \%functor_kind";
5382 63 100       76 if ($in_list) {
5383 27         43 my $qt= quote_perl($f->{value});
5384 27 100       101 str_append_map ($str, __PACKAGE__."::_".$fk."($qt,".($parens?1:0).",\$_)");
5385 27         24 for my $l (@{ $thing->{arg} }) {
  27         44  
5386 35         63 str_append_thing ($str, $l, IN_LIST, PARENS);
5387             }
5388 27         49 str_append_end ($str);
5389             }
5390             else {
5391 36         78 str_append_funcall($str, __PACKAGE__."::_".$fk, NOT_IN_LIST);
5392 36         59 str_append_thing ($str, $f->{value}, IN_LIST, NO_PARENS);
5393 36 100       64 str_append_thing ($str, $parens?1:0, IN_LIST, NO_PARENS);
5394 36         27 for my $l (@{ $thing->{arg} }) {
  36         66  
5395 46         65 str_append_thing ($str, $l, IN_LIST, PARENS);
5396             }
5397 36         65 str_append_end ($str);
5398             }
5399             },
5400              
5401             # funcall and infix use args inline if they are in list context.
5402             # They are handled by _prefix() and _suffix() helper functions in order
5403             # to allow dialect conversion:
5404             'funcall' => 'infix()',
5405             'infix()' => sub {
5406 95         87 $in_list= NOT_IN_LIST; # just to be sure
5407 95         98 my $f= $thing->{functor};
5408 95 50 50     292 my $fk= $functor_kind{$f->{type} || ''}
5409             or die 'Expected $thing->{type} to be mapped by %functor_kind';
5410              
5411 95         216 str_append_funcall($str, __PACKAGE__."::_".$fk, NOT_IN_LIST);
5412 95         175 str_append_thing ($str, $f->{value}, IN_LIST, NO_PARENS);
5413 95 100       177 str_append_thing ($str, $parens?1:0, IN_LIST, NO_PARENS);
5414 95         61 for my $l (@{ $thing->{arg} }) {
  95         186  
5415 185         280 str_append_thing ($str, $l, IN_LIST, PARENS);
5416             }
5417 95         186 str_append_end ($str);
5418             },
5419              
5420             'funcsep' => sub {
5421 6         6 $in_list= NOT_IN_LIST; # just to be sure
5422 6         9 str_append_join ($str, never_empty => 1, sep => ' ');
5423 6         16 str_append_str ($str, "$thing->{functor}{value}(");
5424 6         5 for my $t (@{ $thing->{arg} }) {
  6         12  
5425 27         42 str_append_thing ($str, $t, NOT_IN_LIST, NO_PARENS);
5426             }
5427 6         8 str_append_end ($str);
5428             },
5429              
5430             'case' => sub {
5431 47         41 $in_list= NOT_IN_LIST; # just to be sure
5432              
5433             # FIXME (maybe): we add parens here, so if there are no
5434             # when-then pairs at all and only the else part is printed,
5435             # it will get parens, too, no matter what. That's ok,
5436             # since it's a non-standard, marginal special case.
5437 47 100       118 return str_append_parens ($str, $thing, NOT_IN_LIST)
5438             if $parens;
5439              
5440 34         37 my $sw= $thing->{switchval};
5441 34 100       72 if ($sw) {
5442 22         69 str_append_funcall ($str, __PACKAGE__."::caseswitch", NOT_IN_LIST);
5443 22         37 str_append_thing ($str, $sw, NOT_IN_LIST, NO_PARENS);
5444             }
5445             else {
5446 12         18 str_append_funcall ($str, __PACKAGE__."::casecond", NOT_IN_LIST);
5447             }
5448              
5449 34 100       1633 if (my $e= $thing->{otherwise}) {
5450 24         39 str_append_thing ($str, $e, NOT_IN_LIST, NO_PARENS);
5451             }
5452             else {
5453 10         15 str_append_str ($str, 'NULL');
5454             }
5455              
5456 34         1074 for my $wh (@{ $thing->{arg} }) {
  34         72  
5457 30 50       64 if (ref($wh) eq 'ARRAY') {
5458 30         47 my ($when,$expr)= @$wh;
5459 30         56 str_append_funcall ($str, __PACKAGE__.'::whenthen', NOT_IN_LIST);
5460 30         48 str_append_thing ($str, $when, NOT_IN_LIST, NO_PARENS);
5461 30         1281 str_append_thing ($str, $expr, NOT_IN_LIST, NO_PARENS);
5462 30         1349 str_append_end ($str);
5463             }
5464             else {
5465 0         0 die 'expected array';
5466             }
5467             }
5468              
5469 34         53 str_append_end ($str);
5470             },
5471 1038     618   13038 );
5472             },
5473              
5474             'ColumnName' => sub {
5475             switch ($thing->{type},
5476             'interpol' => 'ident',
5477             'ident' => sub {
5478 48         78 str_append_typed ($str, 'colname', 'none', $thing, $in_list, hashkeys => 1);
5479             }
5480 48     11   126 );
5481             },
5482              
5483             'ColumnIndex' => sub {
5484 2 50 66 0   7 if (defined $thing->{length} || $thing->{desc}) {
5485 2         4 str_append_join ($str, sep => ' ');
5486 2         757 str_append_thing ($str, $thing->{name}, NOT_IN_LIST, NO_PARENS);
5487 2 100       63 if (defined $thing->{length}) {
5488 1         2 str_append_join ($str, prefix => '(', suffix => ')');
5489 1         2 str_append_thing ($str, $thing->{length}, NOT_IN_LIST, NO_PARENS);
5490 1         51 str_append_end ($str);
5491             }
5492 2 100       4 if ($thing->{desc}) {
5493 1         3 str_append_str ($str, 'DESC');
5494             }
5495 2         3 str_append_end ($str);
5496             }
5497             else {
5498 0         0 str_append_thing ($str, $thing->{name}, $in_list, $parens);
5499             }
5500             },
5501              
5502             'TableName' => sub {
5503             switch ($thing->{type},
5504             'interpol' => 'ident',
5505             'ident' => sub {
5506 3         7 str_append_typed ($str, 'tabname', 'none', $thing, $in_list, hashkeys => 1);
5507             }
5508 3     1   10 );
5509             },
5510              
5511             'Fetch' => 'Do',
5512             'Do' => sub {
5513 12     12   15 str_append_thing ($str, $thing->{stmt}, $in_list, $parens);
5514             },
5515 2423         49852 );
5516             }
5517              
5518             sub to_perl($$\@)
5519             {
5520 278     278 0 283 my ($line_start, $kind, $things)= @_;
5521 278         344 my $str= str_new($line_start);
5522 278         384 for my $thing (@$things) {
5523 383         598 str_append_thing ($str, $thing, IN_LIST, NO_PARENS);
5524             }
5525 275         455 my $text= str_get_string($str);
5526 275         1238 return "do{".__PACKAGE__."::_max1_if_scalar map{".__PACKAGE__."::${kind}->obj(\$_)} $text}",
5527             }
5528              
5529             ######################################################################
5530             # Top-level parser interface:
5531              
5532             sub lx_die_perhaps($;$)
5533             {
5534 559     559 0 477 my $lx= shift;
5535              
5536             # if a test value is given, check that it is defined:
5537 559 100       923 if (scalar(@_)) {
5538 280         222 my ($check_val)= @_;
5539 280 100       427 unless (defined $check_val) {
5540 1   50     3 $lx->{error}||= 'Unknown error';
5541             }
5542             }
5543              
5544             # if an error is set, then die:
5545 559 100       842 if ($lx->{error}) {
5546 2         7 die lx_pos($lx).": Error: $lx->{error}\n";
5547             }
5548             }
5549              
5550              
5551             sub parse_1_or_list($$$;$)
5552             {
5553 273     273 0 293 my ($lx, $parse_elem, $list_sep, $end)= @_;
5554 273         434 my $r= parse_list([], $lx, $parse_elem, $list_sep, $end);
5555 273         478 lx_die_perhaps($lx, $r);
5556 272         538 return @$r;
5557             }
5558              
5559             sub parse_0_try_list($$)
5560             {
5561 7     7 0 7 my ($lx, $parse_elem)= @_;
5562 7         18 my $r= parse_try_list([], $lx, $parse_elem);
5563 7         15 lx_die_perhaps($lx, $r);
5564 7         15 return @$r;
5565             }
5566              
5567             sub parse_stmt_list($)
5568             {
5569 137     137 0 370 parse_1_or_list ($_[0], \&parse_stmt, ';', ['}',')',']']);
5570             }
5571              
5572             sub parse_do_stmt($)
5573             {
5574 2     2 0 2 my ($lx) = @_;
5575             map {
5576 2         4 my $stmt = $_;
  2         2  
5577 2         4 my $r = create($lx, 'Do', qw(stmt));
5578 2         3 $r->{stmt} = $stmt;
5579 2         5 $r;
5580             }
5581             parse_stmt_list($lx);
5582             }
5583              
5584             sub parse_fetch_stmt($)
5585             {
5586 10     10 0 10 my ($lx) = @_;
5587             map {
5588 10         12 my $stmt = $_;
  10         9  
5589 10         15 my $r = create($lx, 'Fetch', qw(stmt));
5590 10         11 $r->{stmt} = $stmt;
5591 10         24 $r;
5592             }
5593             parse_stmt_list($lx);
5594             }
5595              
5596             my %top_parse= (
5597             # pure parse actions:
5598             'Stmt' => \&parse_stmt_list,
5599              
5600             'Join' => sub { parse_0_try_list($_[0], \&parse_join) },
5601             'TableOption' => sub { parse_0_try_list($_[0], \&parse_table_option) },
5602              
5603             'Expr' => sub { parse_1_or_list ($_[0], \&parse_expr, ',') },
5604             'Check' => sub { parse_1_or_list ($_[0], \&parse_check, ',') },
5605             'Type' => sub { parse_1_or_list ($_[0], \&parse_type, ',') },
5606             'Column' => sub { parse_1_or_list ($_[0], \&parse_column, ',') },
5607             'Table' => sub { parse_1_or_list ($_[0], \&parse_table, ',') },
5608             'Index' => sub { parse_1_or_list ($_[0], \&parse_index, ',') },
5609             'CharSet' => sub { parse_1_or_list ($_[0], \&parse_charset, ',') },
5610             'Collate' => sub { parse_1_or_list ($_[0], \&parse_collate, ',') },
5611             'Constraint' => sub { parse_1_or_list ($_[0], \&parse_constraint, ',') },
5612             'Transliteration' => sub { parse_1_or_list ($_[0], \&parse_transliteration, ',') },
5613             'Transcoding' => sub { parse_1_or_list ($_[0], \&parse_transcoding, ',') },
5614             'Order' => sub { parse_1_or_list ($_[0], \&parse_order, ',') },
5615             'ColumnSpec' => sub { parse_1_or_list ($_[0], \&parse_column_spec, ',') },
5616              
5617             # parse & execute actions:
5618             'Do' => sub { parse_do_stmt ($_[0]) },
5619             'Fetch' => sub { parse_fetch_stmt($_[0]) },
5620             );
5621             my $top_parse_re= '(?:'.join('|', sort { length($b) <=> length($a) } '', keys %top_parse).')';
5622             my $top_parse_re2= '(?:'.join('|', sort { length($b) <=> length($a) } 'none', keys %top_parse).')';
5623              
5624             sub interpol_set_context ($$)
5625             {
5626 515     515 0 533 my ($text, $ctxt)= @_;
5627 515         424 $text=~ s/(\Q${\SQL_MARK}\E$top_parse_re)(?::$top_parse_re2)?(\s*\{)/$1:$ctxt$2/gs;
  515         2638  
5628 515         1489 return $text;
5629             }
5630              
5631             sub good_interpol_type($)
5632             {
5633 8     8 0 10 my ($type)= @_;
5634 8         28 return !!$top_parse{$type};
5635             }
5636              
5637             sub mark_sql()
5638             {
5639             # Step 1:
5640             # This function will get the text without comments, strings, etc.,
5641             # and replace the initial SQL marking the start of SQL syntax by
5642             # our special SQL_MARK. Then, the unprocessed text will be
5643             # processed by replace_sql().
5644 5     5 0 303 s/\b\Q$sql_marker\E($top_parse_re\s*\{)/${\SQL_MARK}$1/gs;
  259         490  
5645              
5646             # Step 2:
5647             # Unmark false positives. The above finds false matches in
5648             # variables:
5649             #
5650             # $sql{...}
5651             #
5652             # We cannot(?) do this in one go, as we'd need a variable-width
5653             # negative look-behind regexp, which Perl does not have. This
5654             # is because there can be arbitrary white space between $ and
5655             # a variable name.
5656 5         10 s/([\$\@\%]\s*)\Q${\SQL_MARK}\E/$1$sql_marker/gs;
  5         343  
5657              
5658             # Note that there are still false positives, which are really hard
5659             # to find unless we start parsing Perl completely:
5660             #
5661             # ${ sql{blah} }
5662             }
5663              
5664             sub parse($$)
5665             {
5666 22     22 0 10217 my ($kind, $str)= @_;
5667 22         52 my $lx= lexer_new ($str, "", 0);
5668 22         33 my $func= $top_parse{$kind};
5669 22 50       38 return undef unless $func;
5670 22 50       43 return () if looking_at($lx, '');
5671 22         39 my @thing= $func->($lx);
5672 21         52 expect($lx, '', SHIFT);
5673 21         30 lx_die_perhaps ($lx);
5674 20         37 return to_perl(1, $kind, @thing);
5675             }
5676              
5677             sub replace_sql()
5678             {
5679 5     5 0 7232 my ($module, $file, $line)= caller(4); # find our from where we were invoked
5680              
5681 5         14 mark_sql();
5682             #print STDERR "DEBUG: BEFORE: $_\n";
5683              
5684 5         12 pos($_)= 0;
5685 5         9 REPLACEMENT: while (/(\Q${\SQL_MARK}\E($top_parse_re)(?::($top_parse_re2))?\s*\{)/gs) {
  263         6714  
5686             # prepare lexer:
5687 258   100     1096 my $ctxt= $3 || 'Stmt';
5688 258         303 my $speckind= $2;
5689 258   66     511 my $kind= $speckind || $ctxt;
5690 258         368 my $start= pos($_) - length($1);
5691 258         1543 my $prefix= substr($_, 0, $start);
5692 258         5418 my $line_rel= ($prefix =~ tr/\n//);
5693 258         602 my $lx= lexer_new ($_, $file, $line + $line_rel);
5694              
5695             # select parser:
5696 258         310 my $func= $top_parse{$kind};
5697 258 50       390 unless ($func) {
5698 0         0 die "$file:".($line+$line_rel+1).
5699             ": Error: Plain ${sql_marker}${speckind}{...} is illegal, because the ".
5700             "surrounding block must not return an object.\n\tPlease use ".
5701             (english_or map "${sql_marker}${_}{...}", keys %top_parse)." to disambiguate.\n";
5702 0         0 last REPLACEMENT;
5703             }
5704              
5705             # parse (including closing brace):
5706 258         379 my @thing= $func->($lx);
5707 258         402 expect ($lx, '}', SHIFT);
5708 258         351 lx_die_perhaps ($lx);
5709              
5710 258         287 my $end= $lx->{token}{pos};
5711 258 50 33     912 my_confess unless defined $end && $start < $end;
5712              
5713             # Make Perl code:
5714             # Represent the parse result as a list in Perl (if it's only
5715             # one element, the parens don't hurt). Each thing is
5716             # handled individually by to_perl():
5717 258         421 my $perl= to_perl($line + $line_rel, $kind, @thing);
5718              
5719             # replace:
5720 258 50       427 print STDERR "$file:".($line+$line_rel+1).': DEBUG: '.__PACKAGE__." replacement: $perl\n"
5721             if $debug;
5722              
5723 258         11517 my $old_text= substr($_, $start, $end-$start, $perl); # extract and replace text
5724             # pos($_) is now undef, which is ok, we will
5725             # rescan the text anyway.
5726              
5727             # Insert newlines at the end that have been dropped so that the line
5728             # count does not change and Perl's error messages are useful:
5729 258         361 my $line_cnt_old= ($old_text =~ tr/\n//);
5730 258         333 my $line_cnt_new= ($perl =~ tr/\n//);
5731 258 50       443 my_confess "More newlines than before" #.": \n###\n$old_text\n###$perl\n###\n"
5732             if $line_cnt_new > $line_cnt_old;
5733              
5734 258 100       532 if (my $line_cnt_less= $line_cnt_old - $line_cnt_new) {
5735 101         1383 substr($_, $start + length($perl), 0, "\n" x $line_cnt_less);
5736             }
5737              
5738             # rescan everything in order to recurse into embedded sql{...}:
5739 258         3878 pos($_)= 0;
5740             }
5741 5         21 pos($_)= undef;
5742              
5743             #print STDERR "DEBUG: AFTER: $_\n";
5744             };
5745              
5746             FILTER_ONLY
5747             # code_no_comments => \&mark_sql, # This is way to slow.
5748             all => \&replace_sql;
5749              
5750             ######################################################################
5751             # Functions used in generated code:
5752              
5753             # Obj:
5754             {
5755             package SQL::Yapp::Obj;
5756              
5757 5     5   40 use strict;
  5         7  
  5         131  
5758 5     5   16 use warnings;
  5         8  
  5         164  
5759 5     5   20 use Carp qw(croak);
  5         6  
  5         475  
5760              
5761 0     0   0 sub op($) { return ''; }
5762              
5763             ######################################################################
5764             # stringify: simply return second entry in array, the string:
5765             use overload '""' => 'value',
5766 5     5   22 cmp => sub { "$_[0]" cmp "$_[1]" };
  5     173   9  
  5         56  
  173         17317  
5767              
5768             sub type_error($$)
5769             {
5770 3     3   4 my ($x, $want)= @_;
5771 3         6 my $r= ref($x);
5772 3         11 $r=~ s/^SQL::Yapp:://;
5773 3         368 croak "Error: Expected $want, but found ".$r;
5774             }
5775              
5776 0     0   0 sub asc($) { $_[0]->type_error('Asc'); }
5777 0     0   0 sub assign($) { $_[0]->type_error('assignment'); }
5778 0     0   0 sub charset($) { $_[0]->type_error('CharSet'); }
5779 0     0   0 sub constraint($) { $_[0]->type_error('Constraint'); }
5780 0     0   0 sub charset1($) { $_[0]->type_error('CharSet'); }
5781 0     0   0 sub collate1($) { $_[0]->type_error('Collate'); }
5782 0     0   0 sub colname($) { $_[0]->type_error('ColumnName'); }
5783 0     0   0 sub colspec($) { $_[0]->type_error('ColumnSpec'); }
5784 0     0   0 sub column1($) { $_[0]->type_error('Column'); }
5785 1     1   6 sub column1_single($) { $_[0]->type_error('Column'); }
5786 0     0   0 sub constraint1($) { $_[0]->type_error('Constraint'); }
5787 0     0   0 sub desc($) { $_[0]->type_error('Desc'); }
5788 0     0   0 sub engine1($) { $_[0]->type_error('Engine'); }
5789 0     0   0 sub expr($) { $_[0]->type_error('Expr'); }
5790 0     0   0 sub expr_or_check($) { $_[0]->type_error('Expr or Check'); }
5791 0     0   0 sub check($) { $_[0]->type_error('Check'); }
5792 1     1   5 sub exprparen($) { $_[0]->type_error('Expr'); }
5793 0     0   0 sub index1($) { $_[0]->type_error('Index'); }
5794 0     0   0 sub joinclause($) { $_[0]->type_error('JOIN clause'); }
5795 0     0   0 sub limit_number($) { $_[0]->type_error('number or ?'); }
5796 0     0   0 sub stmt($) { $_[0]->type_error('Stmt'); }
5797 0     0   0 sub subquery($) { $_[0]->type_error('subquery'); }
5798 0     0   0 sub table1($) { $_[0]->type_error('Table'); }
5799 0     0   0 sub tabname($) { $_[0]->type_error('TableName'); }
5800 0     0   0 sub tableopt($) { $_[0]->type_error('TableOption'); }
5801 0     0   0 sub transcoding($) { $_[0]->type_error('Transcoding'); }
5802 0     0   0 sub transliteration1($) { $_[0]->type_error('Transliteration'); }
5803 0     0   0 sub type($) { $_[0]->type_error('Type'); }
5804              
5805 0     0   0 sub do($) { $_[0]->type_error('Do'); }
5806 0     0   0 sub fetch($) { $_[0]->type_error('Fetch'); }
5807             }
5808              
5809             # Obj1:
5810             {
5811             package SQL::Yapp::Obj1;
5812              
5813 5     5   3004 use strict;
  5         6  
  5         111  
5814 5     5   16 use warnings;
  5         69  
  5         152  
5815 5     5   19 use base qw(SQL::Yapp::Obj);
  5         6  
  5         1630  
5816 5     5   22 use Scalar::Util qw(blessed);
  5         6  
  5         664  
5817              
5818             sub obj($$)
5819             {
5820 890     890   3239 my ($class,$x)= @_;
5821 890 100 66     2536 return $x
5822             if blessed($x) && $x->isa(__PACKAGE__);
5823 731         1989 return bless([$x], $class);
5824             }
5825              
5826 814     814   3401 sub value($) { return $_[0][0]; }
5827             }
5828              
5829             ###############
5830             # Asterisk:
5831             {
5832             package SQL::Yapp::Asterisk;
5833              
5834 5     5   21 use strict;
  5         6  
  5         98  
5835 5     5   18 use warnings;
  5         19  
  5         143  
5836 5     5   19 use base qw(SQL::Yapp::Obj);
  5         6  
  5         1765  
5837              
5838             sub obj($)
5839             {
5840 2     2   2 my ($class)= @_;
5841 2         6 return bless([], $class);
5842             }
5843              
5844 2     2   9 sub value($) { return '*'; }
5845              
5846 2     2   8 sub column1($) { return $_[0]; }
5847 0     0   0 sub column1_single($) { return $_[0]; }
5848 0     0   0 sub expr($) { return $_[0]; }
5849 0     0   0 sub expr_or_check($) { return $_[0]; }
5850              
5851 0     0   0 sub asterisk($) { return $_[0]; }
5852             }
5853              
5854             # Question:
5855             {
5856             package SQL::Yapp::Question;
5857              
5858 5     5   21 use strict;
  5         8  
  5         87  
5859 5     5   14 use warnings;
  5         4  
  5         106  
5860 5     5   15 use base qw(SQL::Yapp::Obj);
  5         3  
  5         1894  
5861              
5862             sub obj($)
5863             {
5864 1     1   2 my ($class)= @_;
5865 1         3 return bless([], $class);
5866             }
5867              
5868 1     1   3 sub value($) { return '?' }
5869              
5870 0     0   0 sub limit_number($) { return $_[0]; }
5871 1     1   3 sub exprparen($) { return $_[0]; }
5872 0     0   0 sub expr($) { return $_[0]; }
5873 0     0   0 sub expr_or_check($) { return $_[0]; }
5874 0     0   0 sub asc($) { return $_[0]; }
5875 0     0   0 sub desc($) { return SQL::Yapp::Desc->obj($_[0]); }
5876             }
5877              
5878             # ExprSpecial:
5879             {
5880             package SQL::Yapp::ExprSpecial;
5881              
5882 5     5   21 use strict;
  5         5  
  5         83  
5883 5     5   13 use warnings;
  5         6  
  5         123  
5884 5     5   16 use base qw(SQL::Yapp::Obj1);
  5         7  
  5         1724  
5885              
5886 0     0   0 sub exprparen($) { return $_[0]; }
5887 0     0   0 sub expr($) { return $_[0]; }
5888 0     0   0 sub expr_or_check($) { return $_[0]; }
5889 0     0   0 sub asc($) { return $_[0]; }
5890 0     0   0 sub desc($) { return SQL::Yapp::Desc->obj($_[0]); }
5891             }
5892              
5893             # Stmt:
5894             {
5895             package SQL::Yapp::Stmt;
5896              
5897 5     5   21 use strict;
  5         6  
  5         96  
5898 5     5   21 use warnings;
  5         5  
  5         123  
5899 5     5   17 use Carp qw(croak);
  5         5  
  5         208  
5900 5     5   17 use base qw(SQL::Yapp::Obj1);
  5         4  
  5         1767  
5901              
5902 1     1   9 sub subquery($) { $_[0]->type_error('SELECT statement'); }
5903 0     0   0 sub exprparen($) { $_[0]->subquery(); }
5904 2     2   7 sub expr($) { $_[0]->subquery(); }
5905 0     0   0 sub expr_or_check($) { $_[0]->subquery(); }
5906              
5907 3     3   6 sub stmt($) { return $_[0]; }
5908              
5909             sub do($)
5910             {
5911 0     0   0 my ($stmt) = @_;
5912 0         0 my $dbh = SQL::Yapp::get_dbh();
5913 0         0 $dbh->do($stmt);
5914 0         0 return; # return no statements so that _max1_if_scalar is ok with void context
5915             }
5916             }
5917              
5918             # SelectStmt:
5919             {
5920             package SQL::Yapp::SelectStmt;
5921              
5922 5     5   20 use strict;
  5         5  
  5         102  
5923 5     5   15 use warnings;
  5         5  
  5         121  
5924 5     5   14 use Carp qw(croak);
  5         6  
  5         242  
5925 5     5   21 use base qw(SQL::Yapp::Stmt);
  5         4  
  5         1577  
5926              
5927 2     2   7 sub subquery($) { return '('.($_[0]->value).')'; }
5928              
5929             sub fetch($)
5930             {
5931 0     0   0 my ($stmt) = @_;
5932 0         0 my $dbh = SQL::Yapp::get_dbh();
5933 0         0 my $sth = $dbh->prepare($stmt);
5934 0         0 my $aref = $dbh->selectall_arrayref($sth, { Slice => {} });
5935 0 0       0 return unless $aref;
5936 0         0 return @$aref;
5937             }
5938             }
5939              
5940             # SelectStmtSingle:
5941             {
5942             package SQL::Yapp::SelectStmtSingle;
5943              
5944 5     5   22 use strict;
  5         5  
  5         79  
5945 5     5   15 use warnings;
  5         12  
  5         123  
5946 5     5   18 use Carp qw(croak);
  5         10  
  5         182  
5947 5     5   15 use base qw(SQL::Yapp::SelectStmt);
  5         5  
  5         1595  
5948              
5949             sub fetch($)
5950             {
5951 0     0   0 my ($stmt) = @_;
5952 0         0 my $dbh = SQL::Yapp::get_dbh();
5953 0         0 my $sth = $dbh->prepare($stmt);
5954 0 0       0 return unless $sth->execute;
5955 0         0 my @r= ();
5956 0         0 while (my $a= $sth->fetchrow_arrayref) {
5957 0 0       0 die unless scalar(@$a) == 1;
5958 0         0 push @r, $a->[0];
5959             }
5960 0         0 return @r;
5961             }
5962             }
5963              
5964             # Do:
5965             # This is a bit different, since the obj() method will actually execute the statement.
5966             {
5967             package SQL::Yapp::Do;
5968              
5969 5     5   19 use strict;
  5         5  
  5         79  
5970 5     5   12 use warnings;
  5         5  
  5         131  
5971 5     5   15 use Carp qw(confess);
  5         4  
  5         353  
5972              
5973             sub obj($$)
5974             {
5975 0     0   0 my ($class, $stmt) = @_;
5976 0         0 return $stmt->do;
5977             }
5978             }
5979              
5980             # Fetch:
5981             # This is a bit different, since the obj() method will actually execute the statement.
5982             {
5983             package SQL::Yapp::Fetch;
5984              
5985 5     5   18 use strict;
  5         8  
  5         75  
5986 5     5   11 use warnings;
  5         7  
  5         273  
5987              
5988             sub obj($$)
5989             {
5990 0     0   0 my ($class, $stmt) = @_;
5991 0         0 return $stmt->fetch;
5992             }
5993             }
5994              
5995             # ColumnName:
5996             {
5997             package SQL::Yapp::ColumnName;
5998              
5999 5     5   17 use strict;
  5         3  
  5         118  
6000 5     5   18 use warnings;
  5         4  
  5         116  
6001 5     5   15 use base qw(SQL::Yapp::Obj1);
  5         5  
  5         1223  
6002              
6003 0     0   0 sub colname($) { return $_[0]; }
6004             }
6005              
6006             # TableName:
6007             {
6008             package SQL::Yapp::TableName;
6009              
6010 5     5   18 use strict;
  5         5  
  5         77  
6011 5     5   13 use warnings;
  5         4  
  5         100  
6012 5     5   13 use base qw(SQL::Yapp::Obj1);
  5         5  
  5         1117  
6013              
6014 0     0   0 sub tabname($) { return $_[0]; }
6015             }
6016              
6017             # Column:
6018             {
6019             package SQL::Yapp::Column;
6020              
6021 5     5   18 use strict;
  5         7  
  5         79  
6022 5     5   13 use warnings;
  5         6  
  5         95  
6023 5     5   16 use base qw(SQL::Yapp::Obj1);
  5         5  
  5         1452  
6024              
6025 2     2   6 sub column1($) { return $_[0]; }
6026 3     3   8 sub exprparen($) { return $_[0]; }
6027 1     1   3 sub expr($) { return $_[0]; }
6028 0     0   0 sub expr_or_check($) { return $_[0]; }
6029 2     2   6 sub asc($) { return $_[0]; }
6030 5     5   13 sub desc($) { return SQL::Yapp::Desc->obj($_[0]); }
6031             }
6032              
6033             # Table:
6034             {
6035             package SQL::Yapp::Table;
6036 5     5   23 use strict;
  5         3  
  5         79  
6037 5     5   14 use warnings;
  5         5  
  5         187  
6038 5     5   16 use base qw(SQL::Yapp::Obj1);
  5         5  
  5         1067  
6039 5     5   10 sub table1($) { return $_[0]; }
6040             }
6041              
6042             # CharSet:
6043             {
6044             package SQL::Yapp::CharSet;
6045 5     5   20 use strict;
  5         5  
  5         89  
6046 5     5   22 use warnings;
  5         6  
  5         118  
6047 5     5   107 use base qw(SQL::Yapp::Obj1);
  5         7  
  5         1308  
6048 0     0   0 sub charset1($) { return $_[0]; }
6049             }
6050              
6051             # Collate:
6052             {
6053             package SQL::Yapp::Collate;
6054 5     5   21 use strict;
  5         7  
  5         87  
6055 5     5   18 use warnings;
  5         4  
  5         119  
6056 5     5   14 use base qw(SQL::Yapp::Obj1);
  5         10  
  5         1233  
6057 0     0   0 sub collate1($) { return $_[0]; }
6058             }
6059              
6060             # Constraint:
6061             {
6062             package SQL::Yapp::Constraint;
6063 5     5   46 use strict;
  5         5  
  5         82  
6064 5     5   13 use warnings;
  5         6  
  5         112  
6065 5     5   16 use base qw(SQL::Yapp::Obj1);
  5         4  
  5         1253  
6066 0     0   0 sub constraint1($) { return $_[0]; }
6067             }
6068              
6069             # Index:
6070             {
6071             package SQL::Yapp::Index;
6072 5     5   29 use strict;
  5         6  
  5         97  
6073 5     5   15 use warnings;
  5         4  
  5         124  
6074 5     5   14 use base qw(SQL::Yapp::Obj1);
  5         5  
  5         1186  
6075 0     0   0 sub index1($) { return $_[0]; }
6076             }
6077              
6078             # Transliteration:
6079             {
6080             package SQL::Yapp::Transliteration;
6081 5     5   24 use strict;
  5         6  
  5         100  
6082 5     5   20 use warnings;
  5         5  
  5         120  
6083 5     5   14 use base qw(SQL::Yapp::Obj1);
  5         5  
  5         1220  
6084 0     0   0 sub transliteration($) { return $_[0]; }
6085             }
6086              
6087             # Transcoding:
6088             {
6089             package SQL::Yapp::Transcoding;
6090 5     5   24 use strict;
  5         7  
  5         85  
6091 5     5   14 use warnings;
  5         4  
  5         122  
6092 5     5   20 use base qw(SQL::Yapp::Obj1);
  5         9  
  5         1196  
6093 0     0   0 sub transcoding($) { return $_[0]; }
6094             }
6095              
6096             # TableOption:
6097             {
6098             package SQL::Yapp::TableOption;
6099 5     5   27 use strict;
  5         9  
  5         89  
6100 5     5   13 use warnings;
  5         6  
  5         140  
6101 5     5   16 use base qw(SQL::Yapp::Obj1);
  5         4  
  5         1231  
6102 2     2   4 sub tableopt($) { return $_[0]; }
6103             }
6104              
6105             # Engine:
6106             {
6107             package SQL::Yapp::Engine;
6108 5     5   19 use strict;
  5         5  
  5         98  
6109 5     5   14 use warnings;
  5         7  
  5         136  
6110 5     5   15 use base qw(SQL::Yapp::Obj1);
  5         5  
  5         1238  
6111 0     0   0 sub engine1($) { return $_[0]; }
6112             }
6113              
6114              
6115             # Join:
6116             {
6117             package SQL::Yapp::Join;
6118              
6119 5     5   30 use strict;
  5         5  
  5         111  
6120 5     5   15 use warnings;
  5         5  
  5         122  
6121 5     5   16 use base qw(SQL::Yapp::Obj1);
  5         5  
  5         1196  
6122              
6123 4     4   7 sub joinclause($) { return $_[0]; }
6124             }
6125              
6126             # Check:
6127             {
6128             package SQL::Yapp::Check;
6129              
6130 5     5   21 use strict;
  5         10  
  5         85  
6131 5     5   15 use warnings;
  5         5  
  5         157  
6132 5     5   15 use base qw(SQL::Yapp::Obj1);
  5         5  
  5         1615  
6133              
6134 3     3   21 sub check($) { return $_[0]; }
6135 3     3   7 sub expr_or_check($) { return $_[0]; }
6136              
6137             sub obj($$)
6138             {
6139 18 100   18   49 if (ref($_[1]) eq $_[0]) {
    100          
6140 9         53 return $_[1];
6141             }
6142             elsif (ref($_[1])) {
6143 7         15 bless($_[1], $_[0]);
6144             }
6145             else {
6146 2         8 $_[0]->SUPER::obj($_[1]);
6147             }
6148             }
6149             }
6150              
6151             # Expr:
6152             {
6153             package SQL::Yapp::Expr;
6154              
6155 5     5   23 use strict;
  5         5  
  5         107  
6156 5     5   19 use warnings;
  5         4  
  5         119  
6157 5     5   15 use base qw(SQL::Yapp::Obj1);
  5         5  
  5         1584  
6158              
6159 0     0   0 sub exprparen($) { return '('.($_[0]->value).')'; }
6160 18     18   41 sub expr($) { return $_[0]; }
6161 0     0   0 sub expr_or_check($) { return $_[0]; }
6162 0     0   0 sub asc($) { return $_[0]; }
6163 0     0   0 sub desc($) { return SQL::Yapp::Desc->obj($_[0]); }
6164             }
6165              
6166             # Infix:
6167             {
6168             package SQL::Yapp::Infix;
6169              
6170 5     5   29 use strict;
  5         6  
  5         89  
6171 5     5   14 use warnings;
  5         5  
  5         130  
6172 5     5   16 use base qw(SQL::Yapp::Expr);
  5         6  
  5         1251  
6173 5     5   21 use Carp qw(croak);
  5         13  
  5         1045  
6174              
6175             sub obj($$$$)
6176             {
6177 56     56   70 my ($class, $op, $a1, $a2)= @_;
6178 56         128 return bless(["$a1 $op $a2", $op, $a1, $a2], $class);
6179             }
6180              
6181 14     14   25 sub op($) { return $_[0][1]; }
6182 14     14   28 sub arg1($) { return $_[0][2]; }
6183 14     14   19 sub arg2($) { return $_[0][3]; }
6184              
6185             sub assign($)
6186             {
6187 14     14   11 my ($self)= @_;
6188 14 50       16 if ($self->op() eq '=') { # we're not checking everything, just whether it's an assignment
6189 14         21 return $self;
6190             }
6191 0         0 croak "Assignment expected, but found top-level operator '".($self->op)."'.";
6192             }
6193             }
6194              
6195             # Order:
6196             {
6197             package SQL::Yapp::Order;
6198              
6199 5     5   22 use strict;
  5         6  
  5         103  
6200 5     5   16 use warnings;
  5         7  
  5         124  
6201 5     5   16 use base qw(SQL::Yapp::Obj1);
  5         8  
  5         1274  
6202 5     5   24 use Scalar::Util qw(blessed);
  5         6  
  5         523  
6203              
6204             sub obj($$)
6205             {
6206 7     7   22 my ($class,$x)= @_;
6207 7 50 33     58 return $x
6208             if blessed($x) && $x->isa('SQL::Yapp::Obj');
6209 0         0 return bless([$x], 'SQL::Yapp::Asc'); # not Order, but Asc.
6210             }
6211             }
6212              
6213             # Asc:
6214             {
6215             package SQL::Yapp::Asc;
6216              
6217 5     5   19 use strict;
  5         5  
  5         84  
6218 5     5   20 use warnings;
  5         9  
  5         122  
6219 5     5   15 use base qw(SQL::Yapp::Order);
  5         5  
  5         1587  
6220              
6221 0     0   0 sub asc($) { return $_[0]; }
6222 0     0   0 sub desc($) { return SQL::Yapp::Desc->obj($_[0]); }
6223             }
6224              
6225             # Desc:
6226             {
6227             package SQL::Yapp::Desc;
6228              
6229 5     5   22 use strict;
  5         4  
  5         91  
6230 5     5   16 use warnings;
  5         5  
  5         139  
6231 5     5   16 use base qw(SQL::Yapp::Order);
  5         4  
  5         1610  
6232              
6233             sub obj($$)
6234             {
6235 9     9   8 my ($class, $orig)= @_;
6236 9         14 return bless(["$orig DESC",$orig],$class);
6237             }
6238              
6239 1     1   4 sub orig($) { return $_[0][1]; }
6240              
6241 2     2   3 sub asc($) { return $_[0]; }
6242 1     1   2 sub desc($) { return &orig; }
6243             }
6244              
6245             # Type:
6246             {
6247             package SQL::Yapp::Type;
6248              
6249 5     5   24 use strict;
  5         11  
  5         124  
6250 5     5   28 use warnings;
  5         5  
  5         170  
6251 5     5   16 use base qw(SQL::Yapp::Obj);
  5         5  
  5         1258  
6252 5     5   20 use Hash::Util qw(lock_keys);
  5         6  
  5         39  
6253 5     5   282 use Carp qw(croak);
  5         6  
  5         3039  
6254              
6255             sub set_base($$$)
6256             {
6257 11     11   12 my ($self, $base, $spec)= @_;
6258              
6259             # set new spec:
6260 11         14 $self->{base}= $base;
6261 11         11 $self->{spec}= $spec;
6262              
6263             # filter options by new spec:
6264 11         7 for my $o (keys %{ $self->{option} }) {
  11         27  
6265 3 100       8 unless ($spec->{$o}) {
6266 1         2 delete $self->{option}{$o};
6267             }
6268             }
6269              
6270 11         77 return $self;
6271             }
6272              
6273             sub set_property($$$)
6274             {
6275 13     13   15 my ($self, $key, $value)= @_;
6276 13         30 my %a= %$self;
6277 13 50       29 croak "No $key for $self->{base} allowed." unless $self->{spec}{$key};
6278 13         15 $self->{option}{$key}= $value;
6279 13         15 return $self;
6280             }
6281              
6282             sub new($)
6283             {
6284 9     9   1543 my $r= bless({ base => undef, spec => undef, option => {} }, $_[0]);
6285 9         88 lock_keys %$r;
6286 9         77 return $r;
6287             }
6288              
6289             sub obj($$)
6290             {
6291 13     13   41 return $_[1];
6292             }
6293              
6294             sub clone($)
6295             {
6296 11     11   11 my ($self)= @_;
6297             my $r= bless({
6298             %$self,
6299             # no need to make a deep copy of 'spec', because it is never changed.
6300 11         18 option => { %{ $self->{option} } },
  11         42  
6301             }, __PACKAGE__);
6302 11         26 lock_keys %$r;
6303 11         76 return $r;
6304             }
6305              
6306             sub type($)
6307             {
6308 7     7   10 return $_[0]->clone(); # make a copy before trying to modify this
6309             }
6310              
6311             sub colspec($)
6312             {
6313 1     1   6 return SQL::Yapp::ColumnSpec->new($_[0]); # make a copy producing a ColumnSpec
6314             }
6315              
6316             sub value($)
6317             {
6318 38     38   44 my ($self)= @_;
6319 38 100       73 return '' unless $self->{base};
6320 20         33 my @r= ($self->{base});
6321 20 100 66     79 if ($self->{spec}{prec1} && defined $self->{option}{prec1}) {
6322 19         18 my $len_str= '';
6323 19         21 $len_str.= $self->{option}{prec1};
6324 19 50 66     43 if ($self->{spec}{prec2} && defined $self->{option}{prec2}) {
6325 0         0 $len_str.= ', '.$self->{option}{prec2};
6326             }
6327             else {
6328 19 0 33     31 if ($self->{spec}{prec_mul} && $self->{option}{prec_mul}) {
6329 0         0 $len_str.= ' '.$self->{option}{prec_mul};
6330             }
6331 19 0 33     28 if ($self->{spec}{prec_unit} && $self->{option}{prec_unit}) {
6332 0         0 $len_str.= ' '.$self->{option}{prec_unit};
6333             }
6334             }
6335 19         28 push @r, '('.$len_str.')';
6336             }
6337 20 50 33     48 if (my $value_list= $self->{spec}{value_list} && $self->{option}{value_list}) {
6338 0         0 push @r, '('.join(', ',@$value_list).')';
6339             }
6340 20 100 66     54 if (my $x= $self->{spec}{charset} && $self->{option}{charset}) {
6341 3         4 push @r, 'CHARACTER SET', $x;
6342             }
6343 20 50 66     46 if (my $x= $self->{spec}{collate} && $self->{option}{collate}) {
6344 0         0 push @r, 'COLLATE', $x;
6345             }
6346 20         23 for my $key ('sign', 'zerofill', 'timezone') {
6347 60 50 66     127 if (my $x= $self->{spec}{$key} && $self->{option}{$key}) {
6348 0         0 push @r, $x;
6349             }
6350             }
6351              
6352 20         113 return join(' ', @r);
6353             }
6354             }
6355              
6356              
6357             # ColumnSpec:
6358             {
6359             package SQL::Yapp::ColumnSpec;
6360              
6361 5     5   23 use strict;
  5         6  
  5         91  
6362 5     5   25 use warnings;
  5         5  
  5         117  
6363 5     5   17 use base qw(SQL::Yapp::Obj);
  5         11  
  5         1117  
6364 5     5   23 use Hash::Util qw(lock_keys);
  5         7  
  5         16  
6365 5     5   233 use Carp qw(croak);
  5         9  
  5         28910  
6366              
6367             sub new($$)
6368             {
6369 3     3   9 my ($class, $type)= @_;
6370 3         7 my $r= bless({ datatype => $type->clone(), name => {}, option => {} }, $class);
6371 3         33 lock_keys %$r;
6372 3         22 return $r;
6373             }
6374              
6375             sub obj($$)
6376             {
6377 3     3   11 return $_[1];
6378             }
6379              
6380             sub clone($)
6381             {
6382 1     1   2 my ($self)= @_;
6383             my $r= bless({
6384             datatype => $self->{datatype}->clone(),
6385 1         3 name => { %{ $self->{name} } },
6386 1         3 option => { %{ $self->{option} } },
  1         4  
6387             }, __PACKAGE__);
6388 1         4 lock_keys %$r;
6389 1         6 return $r;
6390             }
6391              
6392             sub colspec($)
6393             {
6394 1     1   3 return $_[0]->clone(); # make a copy before trying to modify this
6395             }
6396              
6397             sub name($$)
6398             {
6399 6     6   7 my ($self, $key)= @_;
6400 6 50       11 if (my $x= $self->{name}{$key}) {
6401 0         0 return ('CONSTRAINT', $x);
6402             }
6403 6         10 return;
6404             }
6405              
6406             sub value($)
6407             {
6408 4     4   6 my ($self)= @_;
6409 4         8 my @r= ($self->{datatype});
6410              
6411 4         7 for my $key ('notnull', 'autoinc', 'unique', 'primary', 'key') {
6412 20 100       34 if (my $x= $self->{option}{$key}) {
6413 4         7 push @r, $self->name($key), $x;
6414             }
6415             }
6416              
6417 4         6 for my $key ('default', 'column_format', 'storage') {
6418 12 100       21 if (my $x= $self->{option}{$key}) {
6419 2         3 push @r, $self->name($key), uc($key), $x;
6420             }
6421             }
6422              
6423 4         7 for my $key ('check') {
6424 4 50       10 if (my $x= $self->{option}{$key}) {
6425 0         0 push @r, $self->name($key), uc($key), '('.$x.')';
6426             }
6427             }
6428              
6429 4         5 for my $key ('references') {
6430 4 50       7 if (my $x= $self->{option}{$key}) {
6431 0         0 push @r, $self->name($key), $x;
6432             }
6433             }
6434              
6435 4         8 return join(' ', @r);
6436             }
6437             }
6438              
6439              
6440             # Special Constants:
6441 2     2 0 308 sub ASTERISK { SQL::Yapp::Asterisk->obj(); }
6442 1     1 0 6 sub QUESTION { SQL::Yapp::Question->obj(); }
6443 0     0 0 0 sub NULL { SQL::Yapp::ExprSpecial->obj('NULL'); }
6444 0     0 0 0 sub TRUE { SQL::Yapp::ExprSpecial->obj('TRUE'); }
6445 0     0 0 0 sub FALSE { SQL::Yapp::ExprSpecial->obj('FALSE'); }
6446 0     0 0 0 sub UNKNOWN { SQL::Yapp::ExprSpecial->obj('UNKNOWN'); }
6447 0     0 0 0 sub DEFAULT { SQL::Yapp::ExprSpecial->obj('DEFAULT'); }
6448              
6449              
6450             # Wrapped DBI methods:
6451             sub croak_no_ref($)
6452             {
6453 1     1 0 2 my ($self)= @_;
6454 1         3 croak "Error: Wrong type argument from interpolated code:\n".
6455             "\tExpected scalar, but found ".my_dumper($self);
6456             }
6457              
6458             ########################################
6459             # Generators:
6460              
6461             # These functions are used to typecheck interpolated Perl code's
6462             # result values and to generate objects on the fly if that's possible.
6463             # Usually on-the-fly generation coerces basic Perl types to a blessed
6464             # object, but it would also be feasible to coerce objects to objects.
6465             # Some 'generator' functions don't generate at all, but simply type
6466             # check.
6467             #
6468             # Note: often these functions are invoked in string context, which
6469             # means that directly after their invocation, the string cast operator
6470             # is invoked. However, there's no easy way to prevent object creation
6471             # in that case, because there is no such thing as 'wantstring'
6472             # (would-be analog to 'wantarray'). So these functions must always
6473             # return a blessed reference.
6474              
6475             sub _functor($$@)
6476             {
6477 106     106   137 my ($functor, $parens, @arg)= @_;
6478              
6479             # possibly translate the functor to a different SQL dialect:
6480 106 100       211 if (my $dialect= $functor->{dialect}) {
6481 97 100       152 if (my $f2= find_ref(%$dialect, $write_dialect)) {
6482 28         27 $functor= $f2;
6483             }
6484             }
6485              
6486             # print it:
6487 106         126 my $name= $functor->{value};
6488              
6489             # prefix and suffix are not handled here, because they behave
6490             # differently: they assume exactly one argument are applied
6491             # point-wise. They cannot be switched (ok, we might switch
6492             # between prefix and suffix, but that's not supported yet).
6493             my $s= switch ($functor->{type},
6494             'infix()' => sub {
6495             (scalar(@arg) ?
6496             join(" $name ", @arg)
6497             : defined($functor->{result0}) ?
6498             get_quote_val->($functor->{result0})
6499 52 50   52   187 : die "Error: Functor $functor->{value} used with 0 args, but requires at least one."
    100          
6500             );
6501             },
6502             'funcall' => sub {
6503 21     21   19 $parens= 0;
6504 21         49 "$name(".join(", ", @arg).")";
6505             },
6506             'prefix' => sub {
6507 26 100   26   43 die "Error: Exactly one argument expected for operator $functor->{value},\n".
6508             "\tfound (".join(",", @arg).")"
6509             unless scalar(@arg) == 1;
6510 25         52 "$name $arg[0]"
6511             },
6512             'suffix' => sub {
6513 7 50   7   15 die "Error: exactly one argument expected, found @arg" unless scalar(@arg) == 1;
6514 7         12 "$arg[0] $name"
6515             },
6516 106         616 );
6517 105 100       830 return $parens ? "($s)" : $s;
6518             }
6519              
6520             sub _prefix($$@)
6521             {
6522 67     67   141 my ($name, $parens)= splice @_,0,2;
6523 67   100     226 return _functor($functor_prefix{$name} || { value => $name, type => 'funcall' } , $parens, @_);
6524             }
6525              
6526             sub _suffix($$@)
6527             {
6528 39     39   343 my ($name, $parens)= splice @_,0,2;
6529 39         78 return _functor($functor_suffix{$name}, $parens, @_);
6530             }
6531              
6532             sub _max1_if_scalar(@)
6533             {
6534             # void context:
6535 219 100   219   322 unless (defined wantarray) {
6536 1 50       3 return if scalar(@_) == 0; # allow void context with no params (e.g. after Do)
6537 1         134 croak 'Error: NYI: void context is currently not supported for SQL blocks.';
6538             }
6539              
6540             # list context:
6541 218 100       320 return @_ if wantarray;
6542              
6543             # scalar context:
6544 191 100       366 croak 'Error: Multiple results cannot be assigned to scalar'
6545             if scalar(@_) > 1;
6546 190         359 return $_[0];
6547             }
6548              
6549             sub min1(@)
6550             {
6551 0 0   0 0 0 croak 'Error: Expected at least one element, but found an empty list'
6552             if scalar(@_) == 0;
6553 0         0 return @_;
6554             }
6555              
6556             sub min1default($@)
6557             {
6558 0 0   0 0 0 return @_ if scalar(@_) == 1;
6559 0         0 shift;
6560 0         0 return @_;
6561             }
6562              
6563             sub joinlist($$$$$@)
6564             {
6565 213 100   213 0 337 if (scalar(@_) == 5) {
6566 1 50       5 return $_[1] if defined $_[1];
6567 0         0 my ($module, $file, $line)= caller;
6568 0         0 croak "$file:$_[0]: Error: Expected at least one element, but found an empty list";
6569             }
6570 212         594 return $_[2].join ($_[3], @_[5..$#_]).$_[4];
6571             }
6572              
6573             sub assign($) # check that the result is an assignment, i.e.:`a` =
6574             {
6575 14     14 0 12 my ($x)= @_;
6576 14 50       17 if (ref($x)) {
6577 14         17 return $x->assign();
6578             }
6579             else {
6580 0         0 croak "Assignment expected, but found non-reference.";
6581             }
6582             }
6583              
6584             sub set2values(@)
6585             {
6586 7 50   7 0 14 croak "At least one value expected" if scalar(@_) == 0;
6587             return
6588             ' ('.
6589 14         15 join(',', map { assign($_)->arg1() } @_).
6590             ') VALUES ('.
6591 7         10 join(',', map { $_->arg2() } @_).
  14         17  
6592             ')';
6593             }
6594              
6595             sub exprlist($)
6596             {
6597 7     7 0 13 my ($x)= @_;
6598 7 50       16 croak "Array reference expected for expression list"
6599             unless ref($x) eq 'ARRAY';
6600 7 50       12 croak "At least one element expected in expression list"
6601             unless scalar(@$x) >= 1;
6602 7         9 return '('.join(', ', map { expr($_) } @$x).')';
  14         15  
6603             }
6604              
6605             ####################
6606             # Type
6607              
6608             sub type($)
6609             {
6610 7     7 0 1395 my ($x)= @_;
6611 7 50       14 if (ref($x)) {
6612 7         14 return $x->type();
6613             }
6614             else {
6615 0         0 croak "Type expected, but found non-reference (user types are not supported yet).";
6616             }
6617             }
6618              
6619             # These have $self at the end because it's easier to generate code like that.
6620             sub type_base($$)
6621             {
6622 11     11 0 39 my $self= pop @_;
6623 11         16 my ($base)= @_;
6624 11 50       20 croak "Unrecognised base type '$base'" unless
6625             my $spec= find_ref(%type_spec, $base);
6626 11 50       23 die unless $self;
6627 11         24 return $self->set_base($base, $spec);
6628             }
6629              
6630             sub type_basewlist($@)
6631             {
6632 0     0 0 0 my $self= pop @_;
6633 0         0 my ($base, @value)= @_;
6634 0 0       0 croak "Unrecognised base type '$base'" unless
6635             my $spec= find_ref(%type_spec, $base);
6636 0 0       0 die unless $self;
6637 0         0 $self->set_base($base, $spec);
6638 0         0 $self->set_property('value_list', \@value);
6639 0         0 return $self;
6640             }
6641              
6642             sub type_length($$;$)
6643             {
6644 11     11 0 12 my $self= pop @_;
6645 11         9 my ($prec1, $prec2)= @_;
6646 11         16 $self->set_property('prec1', $prec1);
6647 11 50       16 $self->set_property('prec2', $prec2) if defined $prec2;
6648 11         32 return $self;
6649             }
6650              
6651             sub type_largelength($$$;$)
6652             {
6653 0     0 0 0 my $self= pop @_;
6654 0         0 my ($coeff, $mul, $unit)= @_;
6655 0         0 $self->set_property('prec1', $coeff);
6656 0 0       0 $self->set_property('prec_mul', $mul) if defined $mul;
6657 0 0       0 $self->set_property('prec_unit', $unit) if defined $unit;
6658 0         0 return $self;
6659             }
6660              
6661             sub type_property($$$)
6662             {
6663 2     2 0 4 my $self= pop @_;
6664 2         3 my ($key,$value)= @_;
6665 2         4 $self->set_property($key,$value);
6666 2         4 return $self;
6667             }
6668              
6669             ####################
6670             # ColumnSpec
6671              
6672             sub colspec($)
6673             {
6674 2     2 0 280 my ($x)= @_;
6675 2 50       6 if (ref($x)) {
6676 2         6 return $x->colspec();
6677             }
6678             else {
6679 0         0 croak "ColumnSpec expected, but found non-reference (user types are not supported yet).";
6680             }
6681             }
6682              
6683             sub colspec_property($$$$)
6684             {
6685 4     4 0 10 my $self= pop @_;
6686 4         6 my ($name, $key, $value)= @_;
6687 4         6 $self->{name}{$key}= $name;
6688 4         5 $self->{option}{$key}= $value;
6689 4         15 return $self;
6690             }
6691              
6692             sub colspec_type_base($$)
6693             {
6694 0     0 0 0 my $self= pop @_;
6695 0         0 my ($base)= @_;
6696 0         0 type_base($base, $self->{datatype});
6697 0         0 return $self;
6698             }
6699              
6700             sub colspec_type_property($$$)
6701             {
6702 0     0 0 0 my $self= pop @_;
6703 0         0 my ($key, $value)= @_;
6704 0         0 type_property($key, $value, $self->{datatype});
6705 0         0 return $self;
6706             }
6707              
6708             sub colspec_type_basewlist($@)
6709             {
6710 0     0 0 0 my $self= pop @_;
6711 0         0 my ($base, @value)= @_;
6712 0         0 type_basewlist($base, @value, $self->{datatype});
6713 0         0 return $self;
6714             }
6715              
6716             sub colspec_type_length($$;$)
6717             {
6718 0     0 0 0 my $self= pop @_;
6719 0         0 my ($prec1, $prec2)= @_;
6720 0         0 type_length($prec1, $prec2, $self->{datatype});
6721 0         0 return $self;
6722             }
6723              
6724             sub colspec_type_largelength($$$;$)
6725             {
6726 0     0 0 0 my $self= pop @_;
6727 0         0 my ($coeff, $mul, $unit)= @_;
6728 0         0 type_largelength($coeff, $mul, $unit, $self->{datatype});
6729 0         0 return $self;
6730             }
6731              
6732             ####################
6733             # identifier interpolation, column and table:
6734              
6735             sub tabname($)
6736             {
6737 1     1 0 2 my ($x)= @_;
6738 1 50       5 if (ref($x)) {
    50          
6739 0         0 return $x->tabname;
6740             }
6741             elsif (defined $x) {
6742 1         2 return SQL::Yapp::TableName->obj(get_quote_id->($xlat_table->($x)));
6743             }
6744             else {
6745 0         0 croak "Error: Cannot use undef/NULL as a table name";
6746             }
6747             }
6748              
6749             # Schema-qualified names:
6750             sub schemaname1($$$)
6751             {
6752 122     122 0 96 my ($class,$xlat,$x)= @_;
6753 122 50       151 if (defined $x) {
6754 122         160 return $class->obj(get_quote_id->($xlat->($x)));
6755             }
6756             else {
6757 0         0 croak "Error: Cannot use undef/NULL as a table name";
6758             }
6759             }
6760              
6761             sub schemaname2($$$$)
6762             {
6763 2     2 0 4 my ($class,$xlat,$x,$y)= @_;
6764              
6765 2 100       6 if (ref($x)) { croak_no_ref($x); }
  1         3  
6766 1 50       3 if (ref($y)) { croak_no_ref($y); }
  0         0  
6767 1 50       3 croak "Error: Cannot use undef/NULL as an identifier"
6768             unless defined $y;
6769              
6770 1 50       5 return $class->obj(
6771             get_quote_id->(
6772             undef,
6773             (defined $x ? $xlat_schema->($x) : undef),
6774             $xlat->($y)));
6775             }
6776              
6777             sub schemaname3($$$$$)
6778             {
6779 3     3 0 4 my ($class,$xlat,$x,$y,$z)= @_;
6780 3 50       8 if (ref($x)) { croak_no_ref($x); }
  0         0  
6781 3 50       4 if (ref($y)) { croak_no_ref($y); }
  0         0  
6782 3 50       5 if (ref($z)) { croak_no_ref($z); }
  0         0  
6783 3 50       5 croak "Error: Cannot use undef/NULL as an identifier"
6784             unless defined $z;
6785              
6786 3 50       8 return $class->obj(
    50          
6787             get_quote_id->(
6788             (defined $x ? $xlat_catalog->($x) : undef),
6789             (defined $y ? $xlat_schema->($y) : undef),
6790             $xlat->($z)));
6791             }
6792              
6793              
6794             # Table:
6795             sub table1($)
6796             {
6797 124     124 0 3632 my ($x)= @_;
6798 124 100       263 return ref($x) ? $x->table1 : schemaname1('SQL::Yapp::Table', $xlat_table, $x);
6799             }
6800              
6801             sub table2($$)
6802             {
6803 2     2 0 533 my ($x,$y)= @_;
6804 2         6 return schemaname2('SQL::Yapp::Table', $xlat_table, $x, $y);
6805             }
6806              
6807             sub table3($$$)
6808             {
6809 3     3 0 284 my ($x,$y,$z)= @_;
6810 3         6 return schemaname3('SQL::Yapp::Table', $xlat_table, $x, $y, $z);
6811             }
6812              
6813              
6814             # Index:
6815             sub index1($)
6816             {
6817 0     0 0 0 my ($x)= @_;
6818 0 0       0 return ref($x) ? $x->index1 : schemaname1('SQL::Yapp::Index', $xlat_index, $x);
6819             }
6820              
6821             sub index2($$)
6822             {
6823 0     0 0 0 my ($x,$y)= @_;
6824 0         0 return schemaname2('SQL::Yapp::Index', $xlat_index, $x, $y);
6825             }
6826              
6827             sub index3($$$)
6828             {
6829 0     0 0 0 my ($x,$y,$z)= @_;
6830 0         0 return schemaname3('SQL::Yapp::Index', $xlat_index, $x, $y, $z);
6831             }
6832              
6833              
6834             # CharSet:
6835             sub charset1($)
6836             {
6837 2     2 0 5 my ($x)= @_;
6838 2 50       79 return ref($x) ? $x->charset1 : schemaname1('SQL::Yapp::CharSet', $xlat_charset, $x);
6839             }
6840              
6841             sub charset2($$)
6842             {
6843 0     0 0 0 my ($x,$y)= @_;
6844 0         0 return schemaname2('SQL::Yapp::CharSet', $xlat_charset, $x, $y);
6845             }
6846              
6847             sub charset3($$$)
6848             {
6849 0     0 0 0 my ($x,$y,$z)= @_;
6850 0         0 return schemaname3('SQL::Yapp::CharSet', $xlat_charset, $x, $y, $z);
6851             }
6852              
6853              
6854             # Collate:
6855             sub collate1($)
6856             {
6857 0     0 0 0 my ($x)= @_;
6858 0 0       0 return ref($x) ? $x->collate1 : schemaname1('SQL::Yapp::Collate', $xlat_collate, $x);
6859             }
6860              
6861             sub collate2($$)
6862             {
6863 0     0 0 0 my ($x,$y)= @_;
6864 0         0 return schemaname2('SQL::Yapp::Collate', $xlat_collate, $x, $y);
6865             }
6866              
6867             sub collate3($$$)
6868             {
6869 0     0 0 0 my ($x,$y,$z)= @_;
6870 0         0 return schemaname3('SQL::Yapp::Collate', $xlat_collate, $x, $y, $z);
6871             }
6872              
6873              
6874             # Constraint:
6875             sub constraint1($)
6876             {
6877 0     0 0 0 my ($x)= @_;
6878 0 0       0 return ref($x) ? $x->constraint1 : schemaname1('SQL::Yapp::Constraint', $xlat_constraint, $x);
6879             }
6880              
6881             sub constraint2($$)
6882             {
6883 0     0 0 0 my ($x,$y)= @_;
6884 0         0 return schemaname2('SQL::Yapp::Constraint', $xlat_constraint, $x, $y);
6885             }
6886              
6887             sub constraint3($$$)
6888             {
6889 0     0 0 0 my ($x,$y,$z)= @_;
6890 0         0 return schemaname3('SQL::Yapp::Constraint', $xlat_constraint, $x, $y, $z);
6891             }
6892              
6893              
6894             # Transliteration:
6895             sub transliteration1($)
6896             {
6897 0     0 0 0 my ($x)= @_;
6898 0 0       0 return ref($x) ? $x->transliteration1 : schemaname1('SQL::Yapp::Transliteration', $xlat_transliteration, $x);
6899             }
6900              
6901             sub transliteration2($$)
6902             {
6903 0     0 0 0 my ($x,$y)= @_;
6904 0         0 return schemaname2('SQL::Yapp::Transliteration', $xlat_transliteration, $x, $y);
6905             }
6906              
6907             sub transliteration3($$$)
6908             {
6909 0     0 0 0 my ($x,$y,$z)= @_;
6910 0         0 return schemaname3('SQL::Yapp::Transliteration', $xlat_transliteration, $x, $y, $z);
6911             }
6912              
6913              
6914             # Transcoding:
6915             sub transcoding1($)
6916             {
6917 0     0 0 0 my ($x)= @_;
6918 0 0       0 return ref($x) ? $x->transcoding1 : schemaname1('SQL::Yapp::Transcoding', $xlat_transcoding, $x);
6919             }
6920              
6921             sub transcoding2($$)
6922             {
6923 0     0 0 0 my ($x,$y)= @_;
6924 0         0 return schemaname2('SQL::Yapp::Transcoding', $xlat_transcoding, $x, $y);
6925             }
6926              
6927             sub transcoding3($$$)
6928             {
6929 0     0 0 0 my ($x,$y,$z)= @_;
6930 0         0 return schemaname3('SQL::Yapp::Transcoding', $xlat_transcoding, $x, $y, $z);
6931             }
6932              
6933              
6934             # Engine:
6935             sub engine1($)
6936             {
6937 1     1 0 281 my ($x)= @_;
6938 1 50       6 return ref($x) ? $x->engine1 : schemaname1('SQL::Yapp::Engine', $xlat_engine, $x);
6939             }
6940              
6941             sub engine2($$)
6942             {
6943 0     0 0 0 my ($x,$y)= @_;
6944 0         0 return schemaname2('SQL::Yapp::Engine', $xlat_engine, $x, $y);
6945             }
6946              
6947             sub engine3($$$)
6948             {
6949 0     0 0 0 my ($x,$y,$z)= @_;
6950 0         0 return schemaname3('SQL::Yapp::Engine', $xlat_engine, $x, $y, $z);
6951             }
6952              
6953              
6954             # Columns:
6955             sub colname($)
6956             {
6957 10     10 0 15 my ($x)= @_;
6958 10 50       20 if (ref($x)) {
    50          
6959 0         0 return $x->colname;
6960             }
6961             elsif (defined $x) {
6962 10         18 return SQL::Yapp::ColumnName->obj(get_quote_id->($xlat_column->($x)));
6963             }
6964             else {
6965 0         0 croak "Error: Cannot use undef/NULL as a column name";
6966             }
6967             }
6968              
6969             sub column1($)
6970             {
6971 209     209 0 19365 my ($x)= @_;
6972 209 100       373 if (ref($x)) {
    50          
6973 4         8 return $x->column1;
6974             }
6975             elsif (defined $x) {
6976 205         295 return SQL::Yapp::Column->obj(get_quote_id->($xlat_column->($x)));
6977             }
6978             else {
6979 0         0 croak "Error: Cannot use undef/NULL as an identifier";
6980             }
6981             }
6982              
6983             sub column1_single($) #internal
6984             {
6985 25     25 0 23 my ($x)= @_;
6986 25 100       40 if (ref($x)) {
    50          
6987 1         7 return $x->column1_single;
6988             }
6989             elsif (defined $x) {
6990 24         39 return get_quote_id->($xlat_column->($x));
6991             }
6992             else {
6993 0         0 croak "Error: Cannot use undef/NULL as an identifier";
6994             }
6995             }
6996              
6997             sub column2($$)
6998             {
6999 25     25 0 1783 my ($x,$y)= @_;
7000 25         31 return SQL::Yapp::Column->obj(table1($x).'.'.column1_single($y));
7001             }
7002              
7003             sub column3($$$)
7004             {
7005 0     0 0 0 my ($x,$y,$z)= @_;
7006 0         0 return SQL::Yapp::Column->obj(table2($x,$y).'.'.column1_single($z));
7007             }
7008              
7009             sub column4($$$$)
7010             {
7011 0     0 0 0 my ($w,$x,$y,$z)= @_;
7012 0         0 return SQL::Yapp::Column->obj(table3($w,$x,$y).'.'.column1_single($z));
7013             }
7014              
7015             # Generated with mkidentn.pl:
7016 0     0 0 0 sub table1_n($) { map { table1 ($_ ) } @{ $_[0] } }
  0         0  
  0         0  
7017 0     0 0 0 sub table2_1n($$) { map { table2 ($_[0], $_ ) } @{ $_[1] } }
  0         0  
  0         0  
7018 0     0 0 0 sub table2_n1($$) { map { table2 ($_ , $_[1]) } @{ $_[0] } }
  0         0  
  0         0  
7019 0     0 0 0 sub table2_nn($$) { map { table2_1n ($_ , $_[1]) } @{ $_[0] } }
  0         0  
  0         0  
7020 0     0 0 0 sub table3_11n($$$) { map { table3 ($_[0], $_[1], $_ ) } @{ $_[2] } }
  0         0  
  0         0  
7021 0     0 0 0 sub table3_1n1($$$) { map { table3 ($_[0], $_ , $_[2]) } @{ $_[1] } }
  0         0  
  0         0  
7022 0     0 0 0 sub table3_1nn($$$) { map { table3_11n ($_[0], $_ , $_[2]) } @{ $_[1] } }
  0         0  
  0         0  
7023 0     0 0 0 sub table3_n11($$$) { map { table3 ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7024 0     0 0 0 sub table3_n1n($$$) { map { table3_11n ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7025 0     0 0 0 sub table3_nn1($$$) { map { table3_1n1 ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7026 0     0 0 0 sub table3_nnn($$$) { map { table3_1nn ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7027              
7028 0     0 0 0 sub column1_n($) { map { column1 ($_ ) } @{ $_[0] } }
  0         0  
  0         0  
7029 4     4 0 2 sub column2_1n($$) { map { column2 ($_[0], $_ ) } @{ $_[1] } }
  8         10  
  4         7  
7030 0     0 0 0 sub column2_n1($$) { map { column2 ($_ , $_[1]) } @{ $_[0] } }
  0         0  
  0         0  
7031 2     2 0 597 sub column2_nn($$) { map { column2_1n ($_ , $_[1]) } @{ $_[0] } }
  4         11  
  2         6  
7032 0     0 0 0 sub column3_11n($$$) { map { column3 ($_[0], $_[1], $_ ) } @{ $_[2] } }
  0         0  
  0         0  
7033 0     0 0 0 sub column3_1n1($$$) { map { column3 ($_[0], $_ , $_[2]) } @{ $_[1] } }
  0         0  
  0         0  
7034 0     0 0 0 sub column3_1nn($$$) { map { column3_11n ($_[0], $_ , $_[2]) } @{ $_[1] } }
  0         0  
  0         0  
7035 0     0 0 0 sub column3_n11($$$) { map { column3 ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7036 0     0 0 0 sub column3_n1n($$$) { map { column3_11n ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7037 0     0 0 0 sub column3_nn1($$$) { map { column3_1n1 ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7038 0     0 0 0 sub column3_nnn($$$) { map { column3_1nn ($_ , $_[1], $_[2]) } @{ $_[0] } }
  0         0  
  0         0  
7039 0     0 0 0 sub column4_111n($$$$) { map { column4 ($_[0], $_[1], $_[2], $_ ) } @{ $_[3] } }
  0         0  
  0         0  
7040 0     0 0 0 sub column4_11n1($$$$) { map { column4 ($_[0], $_[1], $_ , $_[3]) } @{ $_[2] } }
  0         0  
  0         0  
7041 0     0 0 0 sub column4_11nn($$$$) { map { column4_111n ($_[0], $_[1], $_ , $_[3]) } @{ $_[2] } }
  0         0  
  0         0  
7042 0     0 0 0 sub column4_1n11($$$$) { map { column4 ($_[0], $_ , $_[2], $_[3]) } @{ $_[1] } }
  0         0  
  0         0  
7043 0     0 0 0 sub column4_1n1n($$$$) { map { column4_111n ($_[0], $_ , $_[2], $_[3]) } @{ $_[1] } }
  0         0  
  0         0  
7044 0     0 0 0 sub column4_1nn1($$$$) { map { column4_11n1 ($_[0], $_ , $_[2], $_[3]) } @{ $_[1] } }
  0         0  
  0         0  
7045 0     0 0 0 sub column4_1nnn($$$$) { map { column4_11nn ($_[0], $_ , $_[2], $_[3]) } @{ $_[1] } }
  0         0  
  0         0  
7046 0     0 0 0 sub column4_n111($$$$) { map { column4 ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7047 0     0 0 0 sub column4_n11n($$$$) { map { column4_111n ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7048 0     0 0 0 sub column4_n1n1($$$$) { map { column4_11n1 ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7049 0     0 0 0 sub column4_n1nn($$$$) { map { column4_11nn ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7050 0     0 0 0 sub column4_nn11($$$$) { map { column4_1n11 ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7051 0     0 0 0 sub column4_nn1n($$$$) { map { column4_1n1n ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7052 0     0 0 0 sub column4_nnn1($$$$) { map { column4_1nn1 ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7053 0     0 0 0 sub column4_nnnn($$$$) { map { column4_1nnn ($_ , $_[1], $_[2], $_[3]) } @{ $_[0] } }
  0         0  
  0         0  
7054              
7055             ####################
7056             # stmt interpolation:
7057              
7058             sub stmt($)
7059             {
7060 3     3 0 15 my ($x)= @_;
7061 3 50       3 if (ref($x)) {
7062 3         11 return $x->stmt;
7063             }
7064             else {
7065 0         0 croak "Error: Expected 'Stmt' object, but found: ".my_dumper($x);
7066             }
7067             }
7068              
7069             sub subquery($)
7070             {
7071 1     1 0 2 my ($x1)= @_;
7072 1         4 my $x= SQL::Yapp::Stmt->obj($x1);
7073 1         5 return $x->subquery;
7074             }
7075              
7076             ####################
7077             # expr interpolation:
7078              
7079             sub exprparen($)
7080             {
7081 70     70 0 1308 my ($x)= @_;
7082 70 100       81 if (ref($x)) {
7083 5 50       13 die Dumper($x) if ref($x) eq 'HASH';
7084 5 50       10 die Dumper($x) if ref($x) eq 'ARRAY';
7085 5 50       12 die Dumper($x) if ref($x) eq 'CODE';
7086 5 50       9 die Dumper($x) if ref($x) eq 'SCALAR';
7087 5         15 return $x->exprparen;
7088             }
7089             else {
7090 65         76 return SQL::Yapp::Expr->obj(get_quote_val->($x)); # raw perl scalar: quote as value, no parens
7091             }
7092             }
7093              
7094             sub expr($)
7095             {
7096 153     153 0 5550 my ($x)= @_;
7097 153 100       194 if (ref($x)) {
7098 21 50       58 confess 'Error: Trying to invoke $x->expr() on unblessed reference $x ".
7099             "(maybe missing nested sqlExpr{...} inside a block, or ".
7100             "additional () around {} interpolation?)'
7101             unless blessed($x);
7102 21         44 return $x->expr;
7103             }
7104             else {
7105 132         165 return SQL::Yapp::Expr->obj(get_quote_val->($x)); # raw perl scalar: quote as value
7106             }
7107             }
7108              
7109             sub expr_or_check($)
7110             {
7111 5     5 0 5 my ($x)= @_;
7112 5 100       11 if (ref($x)) {
7113 3         7 return $x->expr_or_check;
7114             }
7115             else {
7116 2         3 return SQL::Yapp::Expr->obj(get_quote_val->($x)); # raw perl scalar: quote as value
7117             }
7118             }
7119              
7120             sub exprparen_hash(\%)
7121             {
7122 3     3 0 4 my ($x)= @_;
7123             return map {
7124 3         14 my $n= $_;
  6         7  
7125 6         9 my $e= $x->{$n};
7126 6 100 66     40 (blessed($e) && $e->isa('SQL::Yapp::Check') ?
7127             '('.get_quote_id->($n).' '.$e->check.')'
7128             : '('.get_quote_id->($n).' = '.exprparen($e).')'
7129             )
7130             }
7131             sort keys %$x;
7132             }
7133              
7134             sub expr_hash(\%)
7135             {
7136 4     4 0 8 my ($x)= @_;
7137             return map {
7138 4         11 my $n= $_;
  7         9  
7139 7         8 my $e= $x->{$n};
7140 7 50 33     22 (blessed($e) && $e->isa('SQL::Yapp::Check') ?
7141             '('.get_quote_id->($n).' '.$e->check.')'
7142             : SQL::Yapp::Infix->obj('=', get_quote_id->($n), exprparen($e))
7143             )
7144             }
7145             sort keys %$x;
7146             }
7147              
7148             ####################
7149             # order interpolation:
7150              
7151             sub asc($)
7152             {
7153 9     9 0 293 my ($x)= @_;
7154 9 100       17 if (ref($x)) {
    50          
7155 4         8 return $x->asc;
7156             }
7157             elsif (defined $x) {
7158 5         8 return column1($x);
7159             }
7160             else {
7161 0         0 return NULL;
7162             }
7163             }
7164              
7165             sub desc($)
7166             {
7167 10     10 0 20 my ($x)= @_;
7168 10 100       17 if (ref($x)) {
    50          
7169 6         11 return $x->desc;
7170             }
7171             elsif (defined $x) {
7172 4         5 return SQL::Yapp::Desc->obj(column1($x));
7173             }
7174             else {
7175 0         0 return NULL;
7176             }
7177             }
7178              
7179             ####################
7180             # table option:
7181              
7182             sub tableopt($)
7183             {
7184 2     2 0 6 my ($x)= @_;
7185 2 50       4 if (ref($x)) {
7186 2         4 return $x->tableopt;
7187             }
7188             else {
7189 0         0 croak "Error: Expected 'TableOption' object, but found: ".my_dumper($x);
7190             }
7191             }
7192              
7193             ####################
7194             # join interpolation:
7195              
7196             sub joinclause($)
7197             {
7198 4     4 0 7 my ($x)= @_;
7199 4 50       8 if (ref($x)) {
7200 4         9 return $x->joinclause;
7201             }
7202             else {
7203 0         0 croak "Error: Expected 'Join' object, but found: ".my_dumper($x);
7204             }
7205             }
7206              
7207             ####################
7208             # limit interpolation:
7209              
7210             sub limit_number($)
7211             {
7212 14     14 0 30 my ($x)= @_;
7213 14 50       38 if (ref($x)) {
    50          
7214 0         0 return $x->limit_number;
7215             }
7216             elsif (looks_like_number $x) {
7217 14         34 return $x;
7218             }
7219             else {
7220 0         0 croak "Error: Expected number or ?, but found: ".my_dumper($x);
7221             }
7222             }
7223              
7224             sub limit($$)
7225             {
7226 2     2 0 4 my ($cnt, $offset)= @_;
7227              
7228             # FIXME: if dialect is 'std' (or maybe 'std2008'), produce OFFSET/FETCH
7229             # clause (SQL-2008).
7230 2 100       5 if (defined $cnt) {
7231 1 50       3 if (defined $offset) {
7232 1         2 return " LIMIT ".limit_number($cnt)." OFFSET ".limit_number($offset);
7233             }
7234             else {
7235 0         0 return " LIMIT ".limit_number($cnt);
7236             }
7237             }
7238             else {
7239 1 50       4 if (defined $offset) {
7240 1 50       3 if ($write_dialect eq 'postgresql') {
7241 0         0 return " LIMIT ALL OFFSET ".limit_number($offset);
7242             }
7243             else {
7244 1         1 return " LIMIT ${\LARGE_LIMIT_CNT} OFFSET ".limit_number($offset);
  1         5  
7245             }
7246             }
7247             else {
7248 0         0 return '';
7249             }
7250             }
7251             }
7252              
7253             ####################
7254             # case:
7255              
7256             sub whenthen($$)
7257             {
7258 6     6 0 7 my ($expr, $then)= @_;
7259 6         46 return 'WHEN '.$expr.' THEN '.$then;
7260             }
7261              
7262             sub caseswitch($$@)
7263             {
7264             #my ($switchval, $default, @whenthen)
7265 8 100   8 0 14 if (scalar(@_) == 2) { # @whenthen is empty => always use default
7266 2         6 return $_[1]; # return default
7267             }
7268             return
7269 6         42 join(' ',
7270             'CASE',
7271             $_[0],
7272             @_[2..$#_], # @whenthen
7273             'ELSE', # always generate default, it's easier.
7274             $_[1],
7275             'END'
7276             );
7277             }
7278              
7279             sub casecond($@)
7280             {
7281             #my ($default, @whenthen)
7282 0 0   0 0   if (scalar(@_) == 1) { # @whenthen is empty => always use default
7283 0           return $_[0]; # return default
7284             }
7285             return
7286 0           join(' ',
7287             'CASE',
7288             @_[1..$#_], # @whenthen
7289             'ELSE', # always generate default, it's easier.
7290             $_[0],
7291             'END'
7292             );
7293             }
7294              
7295             1;
7296              
7297             ######################################################################
7298             ######################################################################
7299             ######################################################################
7300              
7301             __END__