~percona-toolkit-dev/percona-toolkit/mysql-5.6-test-fixes

« back to all changes in this revision

Viewing changes to t/lib/SQLParser.t

  • Committer: Daniel Nichter
  • Date: 2011-06-24 17:22:06 UTC
  • Revision ID: daniel@percona.com-20110624172206-c7q4s4ad6r260zz6
Add lib/, t/lib/, and sandbox/.  All modules are updated and passing on MySQL 5.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/perl
 
2
 
 
3
BEGIN {
 
4
   die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
 
5
      unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
 
6
   unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
 
7
};
 
8
 
 
9
use strict;
 
10
use warnings FATAL => 'all';
 
11
 
 
12
use Test::More tests => 137;
 
13
use English qw(-no_match_vars);
 
14
 
 
15
use MaatkitTest;
 
16
use SQLParser;
 
17
 
 
18
use Data::Dumper;
 
19
$Data::Dumper::Indent    = 1;
 
20
$Data::Dumper::Sortkeys  = 1;
 
21
$Data::Dumper::Quotekeys = 0;
 
22
 
 
23
my $sp = new SQLParser();
 
24
 
 
25
# ############################################################################
 
26
# Should throw some errors for stuff it can't do.
 
27
# ############################################################################
 
28
throws_ok(
 
29
   sub { $sp->parse('drop table foo'); },
 
30
   qr/Cannot parse DROP queries/,
 
31
   "Dies if statement type cannot be parsed"
 
32
);
 
33
 
 
34
# ############################################################################
 
35
# parse_csv
 
36
# ############################################################################
 
37
sub test_parse_csv {
 
38
   my ( $in, $expect, %args ) = @_;
 
39
   my $got = $sp->_parse_csv($in, %args);
 
40
   is_deeply(
 
41
      $got,
 
42
      $expect,
 
43
      "parse_csv($in)"
 
44
   ) or print Dumper($got);
 
45
   return;
 
46
}
 
47
 
 
48
test_parse_csv(
 
49
   "a,b",
 
50
   [qw(a b)],
 
51
);
 
52
 
 
53
test_parse_csv(
 
54
   q{'', ','},
 
55
   [q{''}, q{','}],
 
56
   quoted_values => 1,
 
57
);
 
58
 
 
59
test_parse_csv(
 
60
   q{"", "a"},
 
61
   [q{""}, q{"a"}],
 
62
   quoted_values => 1,
 
63
);
 
64
 
 
65
test_parse_csv(
 
66
   q{'hello, world!','hi'},
 
67
   [q{'hello, world!'}, q{'hi'}],
 
68
   quoted_values => 1,
 
69
);
 
70
 
 
71
test_parse_csv(
 
72
   q{'a',   "b",   c},
 
73
   [q{'a'}, q{"b"}, q{c}],
 
74
   quoted_values => 1,
 
75
);
 
76
 
 
77
test_parse_csv(
 
78
   q{"x, y", "", a, 'b'},
 
79
   [q{"x, y"}, q{""}, q{a}, q{'b'}],
 
80
   quoted_values => 1,
 
81
);
 
82
 
 
83
# ############################################################################
 
84
# is_identifier
 
85
# ############################################################################
 
86
sub test_is_identifier {
 
87
   my ( $thing, $expect ) = @_;
 
88
   is(
 
89
      $sp->is_identifier($thing),
 
90
      $expect,
 
91
      "$thing is" . ($expect ? "" : " not") . " an ident"
 
92
   );
 
93
   return;
 
94
}
 
95
 
 
96
test_is_identifier("tbl",        1);
 
97
test_is_identifier("`tbl`",      1);
 
98
test_is_identifier("'tbl'",      0);
 
99
test_is_identifier("\"tbl\"",    0);
 
100
test_is_identifier('db.tbl',     1);
 
101
test_is_identifier('"db.tbl"',   0);
 
102
test_is_identifier('db.tbl.col', 1);
 
103
test_is_identifier('1',          0);
 
104
 
 
105
# #############################################################################
 
106
# WHERE where_condition
 
107
# #############################################################################
 
108
sub test_where {
 
109
   my ( $where, $struct ) = @_;
 
110
   is_deeply(
 
111
      $sp->parse_where($where),
 
112
      $struct,
 
113
      "WHERE " . substr($where, 0, 60) 
 
114
         . (length $where > 60 ? '...' : ''),
 
115
   );
 
116
};
 
117
 
 
118
test_where(
 
119
   'i=1',
 
120
   [
 
121
      {
 
122
         predicate => undef,
 
123
         left_arg  => 'i',
 
124
         operator  => '=',
 
125
         right_arg => '1',
 
126
      },
 
127
   ],
 
128
);
 
129
 
 
130
test_where(
 
131
   'i=1 or j<10 or k>100 or l != 0',
 
132
   [
 
133
      {
 
134
         predicate => undef,
 
135
         left_arg  => 'i',
 
136
         operator  => '=',
 
137
         right_arg => '1',
 
138
      },
 
139
      {
 
140
         predicate => 'or',
 
141
         left_arg  => 'j',
 
142
         operator  => '<',
 
143
         right_arg => '10',
 
144
      },
 
145
      {
 
146
         predicate => 'or',
 
147
         left_arg  => 'k',
 
148
         operator  => '>',
 
149
         right_arg => '100',
 
150
      },
 
151
      {
 
152
         predicate => 'or',
 
153
         left_arg  => 'l',
 
154
         operator  => '!=',
 
155
         right_arg => '0',
 
156
      },
 
157
   ],
 
158
);
 
159
 
 
160
test_where(
 
161
   'i=1 and foo="bar"',
 
162
   [
 
163
      {
 
164
         predicate => undef,
 
165
         left_arg  => 'i',
 
166
         operator  => '=',
 
167
         right_arg => '1',
 
168
      },
 
169
      {
 
170
         predicate => 'and',
 
171
         left_arg  => 'foo',
 
172
         operator  => '=',
 
173
         right_arg => '"bar"',
 
174
      },
 
175
   ],
 
176
);
 
177
 
 
178
test_where(
 
179
   '(i=1 and foo="bar")',
 
180
   [
 
181
      {
 
182
         predicate => undef,
 
183
         left_arg  => 'i',
 
184
         operator  => '=',
 
185
         right_arg => '1',
 
186
      },
 
187
      {
 
188
         predicate => 'and',
 
189
         left_arg  => 'foo',
 
190
         operator  => '=',
 
191
         right_arg => '"bar"',
 
192
      },
 
193
   ],
 
194
);
 
195
 
 
196
test_where(
 
197
   '(i=1) and (foo="bar")',
 
198
   [
 
199
      {
 
200
         predicate => undef,
 
201
         left_arg  => 'i',
 
202
         operator  => '=',
 
203
         right_arg => '1',
 
204
      },
 
205
      {
 
206
         predicate => 'and',
 
207
         left_arg  => 'foo',
 
208
         operator  => '=',
 
209
         right_arg => '"bar"',
 
210
      },
 
211
   ],
 
212
);
 
213
 
 
214
test_where(
 
215
   'i= 1 and foo ="bar" or j = 2',
 
216
   [
 
217
      {
 
218
         predicate => undef,
 
219
         left_arg  => 'i',
 
220
         operator  => '=',
 
221
         right_arg => '1',
 
222
      },
 
223
      {
 
224
         predicate => 'and',
 
225
         left_arg  => 'foo',
 
226
         operator  => '=',
 
227
         right_arg => '"bar"',
 
228
      },
 
229
      {
 
230
         predicate => 'or',
 
231
         left_arg  => 'j',
 
232
         operator  => '=',
 
233
         right_arg => '2',
 
234
      },
 
235
   ],
 
236
);
 
237
 
 
238
test_where(
 
239
   'i=1 and foo="i have spaces and a keyword!"',
 
240
   [
 
241
      {
 
242
         predicate => undef,
 
243
         left_arg  => 'i',
 
244
         operator  => '=',
 
245
         right_arg => '1',
 
246
      },
 
247
      {
 
248
         predicate => 'and',
 
249
         left_arg  => 'foo',
 
250
         operator  => '=',
 
251
         right_arg => '"i have spaces and a keyword!"',
 
252
      },
 
253
   ],
 
254
);
 
255
 
 
256
test_where(
 
257
   'i="this and this" or j<>"that and that" and k="and or and" and z=1',
 
258
   [
 
259
      {
 
260
         predicate => undef,
 
261
         left_arg  => 'i',
 
262
         operator  => '=',
 
263
         right_arg => '"this and this"',
 
264
      },
 
265
      {
 
266
         predicate => 'or',
 
267
         left_arg  => 'j',
 
268
         operator  => '<>',
 
269
         right_arg => '"that and that"',
 
270
      },
 
271
      {
 
272
         predicate => 'and',
 
273
         left_arg  => 'k',
 
274
         operator  => '=',
 
275
         right_arg => '"and or and"',
 
276
      },
 
277
      {
 
278
         predicate => 'and',
 
279
         left_arg  => 'z',
 
280
         operator  => '=',
 
281
         right_arg => '1',
 
282
      },
 
283
   ],
 
284
);
 
285
 
 
286
test_where(
 
287
   'i="this and this" or j in ("and", "or") and x is not null or a between 1 and 10 and sz="the keyword \'and\' is in the middle or elsewhere hidden"',
 
288
   [
 
289
      {
 
290
         predicate => undef,
 
291
         left_arg  => 'i',
 
292
         operator  => '=',
 
293
         right_arg => '"this and this"',
 
294
      },
 
295
      {
 
296
         predicate => 'or',
 
297
         left_arg  => 'j',
 
298
         operator  => 'in',
 
299
         right_arg => '("and", "or")',
 
300
      },
 
301
      {
 
302
         predicate => 'and',
 
303
         left_arg  => 'x',
 
304
         operator  => 'is not',
 
305
         right_arg => 'null',
 
306
      },
 
307
      {
 
308
         predicate => 'or',
 
309
         left_arg  => 'a',
 
310
         operator  => 'between',
 
311
         right_arg => '1 and 10',
 
312
      },
 
313
      {
 
314
         predicate => 'and',
 
315
         left_arg  => 'sz',
 
316
         operator  => '=',
 
317
         right_arg => '"the keyword \'and\' is in the middle or elsewhere hidden"',
 
318
      },
 
319
   ],
 
320
);
 
321
 
 
322
test_where(
 
323
   "(`ga_announcement`.`disabled` = 0)",
 
324
   [
 
325
      {
 
326
         predicate => undef,
 
327
         left_arg  => '`ga_announcement`.`disabled`',
 
328
         operator  => '=',
 
329
         right_arg => '0',
 
330
      },
 
331
   ]
 
332
);
 
333
 
 
334
test_where(
 
335
   "1",
 
336
   [
 
337
      {
 
338
         predicate => undef,
 
339
         left_arg  => undef,
 
340
         operator  => undef,
 
341
         right_arg => '1',
 
342
      },
 
343
   ]
 
344
);
 
345
 
 
346
test_where(
 
347
   "1 and foo not like '%bar%'",
 
348
   [
 
349
      {
 
350
         predicate => undef,
 
351
         left_arg  => undef,
 
352
         operator  => undef,
 
353
         right_arg => '1',
 
354
      },
 
355
      {
 
356
         predicate => 'and',
 
357
         left_arg  => 'foo',
 
358
         operator  => 'not like',
 
359
         right_arg => '\'%bar%\'',
 
360
      },
 
361
   ]
 
362
);
 
363
 
 
364
test_where(
 
365
   "TRUE or FALSE",
 
366
   [
 
367
      {
 
368
         predicate => undef,
 
369
         left_arg  => undef,
 
370
         operator  => undef,
 
371
         right_arg => 'true',
 
372
      },
 
373
      {
 
374
         predicate => 'or',
 
375
         left_arg  => undef,
 
376
         operator  => undef,
 
377
         right_arg => 'false',
 
378
      },
 
379
   ]
 
380
);
 
381
 
 
382
test_where(
 
383
   "TO_DAYS(column) < TO_DAYS(NOW()) - 5",
 
384
   [
 
385
      {
 
386
         predicate => undef,
 
387
         left_arg  => "TO_DAYS(column)",
 
388
         operator  => '<',
 
389
         right_arg => 'TO_DAYS(NOW()) - 5',
 
390
      },
 
391
   ]
 
392
);
 
393
 
 
394
test_where(
 
395
   "id <> CONV(ff, 16, 10)",
 
396
   [
 
397
      {
 
398
         predicate => undef,
 
399
         left_arg  => 'id',
 
400
         operator  => '<>',
 
401
         right_arg => 'CONV(ff, 16, 10)',
 
402
      },
 
403
   ]
 
404
);
 
405
 
 
406
test_where(
 
407
   "edpik.input_key = input_key.id",
 
408
   [
 
409
      {
 
410
         predicate => undef,
 
411
         left_arg  => 'edpik.input_key',
 
412
         operator  => '=',
 
413
         right_arg => 'input_key.id'
 
414
      },
 
415
   ]
 
416
);
 
417
 
 
418
test_where(
 
419
   "((`sakila`.`city`.`country_id` = `sakila`.`country`.`country_id`) and (`sakila`.`country`.`country` = 'Brazil') and (`sakila`.`city`.`city` like 'A%'))",
 
420
   [
 
421
      {
 
422
         predicate => undef,
 
423
         left_arg  => '`sakila`.`city`.`country_id`',
 
424
         operator  => '=',
 
425
         right_arg => '`sakila`.`country`.`country_id`'
 
426
      },
 
427
      {
 
428
        predicate => 'and',
 
429
        left_arg  => '`sakila`.`country`.`country`',
 
430
        operator  => '=',
 
431
        right_arg => '\'Brazil\''
 
432
      },
 
433
      {
 
434
        predicate => 'and',
 
435
        left_arg  => '`sakila`.`city`.`city`',
 
436
        operator  => 'like',
 
437
        right_arg => '\'A%\''
 
438
      }
 
439
   ]
 
440
);
 
441
 
 
442
# #############################################################################
 
443
# Whitespace and comments.
 
444
# #############################################################################
 
445
is(
 
446
   $sp->clean_query(' /* leading comment */select *
 
447
      from tbl where /* comment */ id=1  /*trailing comment*/ '
 
448
   ),
 
449
   'select * from tbl where  id=1',
 
450
   'Remove extra whitespace and comment blocks'
 
451
);
 
452
 
 
453
is(
 
454
   $sp->clean_query('/*
 
455
      leading comment
 
456
      on multiple lines
 
457
*/ select * from tbl where /* another
 
458
silly comment */ id=1
 
459
/*trailing comment
 
460
also on mutiple lines*/ '
 
461
   ),
 
462
   'select * from tbl where  id=1',
 
463
   'Remove multi-line comment blocks'
 
464
);
 
465
 
 
466
is(
 
467
   $sp->clean_query('-- SQL style      
 
468
   -- comments
 
469
   --
 
470
 
 
471
  
 
472
select now()
 
473
'
 
474
   ),
 
475
   'select now()',
 
476
   'Remove multiple -- comment lines and blank lines'
 
477
);
 
478
 
 
479
 
 
480
# #############################################################################
 
481
# Normalize space around certain SQL keywords.  (This makes parsing easier.)
 
482
# #############################################################################
 
483
is(
 
484
   $sp->normalize_keyword_spaces('insert into t value(1)'),
 
485
   'insert into t value (1)',
 
486
   'Add space VALUE (cols)'
 
487
);
 
488
 
 
489
is(
 
490
   $sp->normalize_keyword_spaces('insert into t values(1)'),
 
491
   'insert into t values (1)',
 
492
   'Add space VALUES (cols)'
 
493
);
 
494
 
 
495
is(
 
496
   $sp->normalize_keyword_spaces('select * from a join b on(foo)'),
 
497
   'select * from a join b on (foo)',
 
498
   'Add space ON (conditions)'
 
499
);
 
500
 
 
501
is(
 
502
   $sp->normalize_keyword_spaces('select * from a join b on(foo) join c on(bar)'),
 
503
   'select * from a join b on (foo) join c on (bar)',
 
504
   'Add space multiple ON (conditions)'
 
505
);
 
506
 
 
507
is(
 
508
   $sp->normalize_keyword_spaces('select * from a join b using(foo)'),
 
509
   'select * from a join b using (foo)',
 
510
   'Add space using (conditions)'
 
511
);
 
512
 
 
513
is(
 
514
   $sp->normalize_keyword_spaces('select * from a join b using(foo) join c using(bar)'),
 
515
   'select * from a join b using (foo) join c using (bar)',
 
516
   'Add space multiple USING (conditions)'
 
517
);
 
518
 
 
519
is(
 
520
   $sp->normalize_keyword_spaces('select * from a join b using(foo) join c on(bar)'),
 
521
   'select * from a join b using (foo) join c on (bar)',
 
522
   'Add space USING and ON'
 
523
);
 
524
 
 
525
# ###########################################################################
 
526
# GROUP BY
 
527
# ###########################################################################
 
528
is_deeply(
 
529
   $sp->parse_group_by('col, tbl.bar, 4, col2 ASC, MIN(bar)'),
 
530
   [
 
531
      { column => 'col', },
 
532
      { table => 'tbl', column => 'bar', },
 
533
      { position => '4', },
 
534
      { column => 'col2', sort => 'ASC', },
 
535
      { function => 'MIN', expression => 'bar' },
 
536
   ],
 
537
   "GROUP BY col, tbl.bar, 4, col2 ASC, MIN(bar)"
 
538
);
 
539
 
 
540
# ###########################################################################
 
541
# ORDER BY
 
542
# ###########################################################################
 
543
is_deeply(
 
544
   $sp->parse_order_by('foo'),
 
545
   [{column=>'foo'}],
 
546
   'ORDER BY foo'
 
547
);
 
548
is_deeply(
 
549
   $sp->parse_order_by('foo'),
 
550
   [{column=>'foo'}],
 
551
   'order by foo'
 
552
);
 
553
is_deeply(
 
554
   $sp->parse_order_by('foo, bar'),
 
555
   [
 
556
      {column => 'foo'},
 
557
      {column => 'bar'},
 
558
   ],
 
559
   'order by foo, bar'
 
560
);
 
561
is_deeply(
 
562
   $sp->parse_order_by('foo asc, bar'),
 
563
   [
 
564
      {column => 'foo', sort => 'ASC'},
 
565
      {column => 'bar'},
 
566
   ],
 
567
   'order by foo asc, bar'
 
568
);
 
569
is_deeply(
 
570
   $sp->parse_order_by('1'),
 
571
   [{position => '1'}],
 
572
   'ORDER BY 1'
 
573
);
 
574
is_deeply(
 
575
   $sp->parse_order_by('RAND()'),
 
576
   [{function => 'RAND'}],
 
577
   'ORDER BY RAND()'
 
578
);
 
579
 
 
580
# ###########################################################################
 
581
# LIMIT
 
582
# ###########################################################################
 
583
is_deeply(
 
584
   $sp->parse_limit('1'),
 
585
   { row_count => 1, },
 
586
   'LIMIT 1'
 
587
);
 
588
is_deeply(
 
589
   $sp->parse_limit('1, 2'),
 
590
   { row_count => 2,
 
591
     offset    => 1,
 
592
   },
 
593
   'LIMIT 1, 2'
 
594
);
 
595
is_deeply(
 
596
   $sp->parse_limit('5 OFFSET 10'),
 
597
   { row_count       => 5,
 
598
     offset          => 10,
 
599
     explicit_offset => 1,
 
600
   },
 
601
   'LIMIT 5 OFFSET 10'
 
602
);
 
603
 
 
604
 
 
605
# ###########################################################################
 
606
# FROM table_references
 
607
# ###########################################################################
 
608
 
 
609
sub test_from {
 
610
   my ( $from, $struct ) = @_;
 
611
   my $got = $sp->parse_from($from);
 
612
   is_deeply(
 
613
      $got,
 
614
      $struct,
 
615
      "FROM $from"
 
616
   ) or print Dumper($got);
 
617
};
 
618
 
 
619
test_from(
 
620
   'tbl',
 
621
   [ { tbl => 'tbl', } ],
 
622
);
 
623
 
 
624
test_from(
 
625
   'tbl ta',
 
626
   [ { tbl  => 'tbl', alias => 'ta', }  ],
 
627
);
 
628
 
 
629
test_from(
 
630
   'tbl AS ta',
 
631
   [ { tbl           => 'tbl',
 
632
       alias          => 'ta',
 
633
       explicit_alias => 1,
 
634
   } ],
 
635
);
 
636
 
 
637
test_from(
 
638
   't1, t2',
 
639
   [
 
640
      { tbl => 't1', },
 
641
      {
 
642
         tbl => 't2',
 
643
         join => {
 
644
            to    => 't1',
 
645
            type  => 'inner',
 
646
            ansi  => 0,
 
647
         },
 
648
      }
 
649
   ],
 
650
);
 
651
 
 
652
test_from(
 
653
   't1 a, t2 as b',
 
654
   [
 
655
      { tbl  => 't1',
 
656
        alias => 'a',
 
657
      },
 
658
      {
 
659
        tbl           => 't2',
 
660
        alias          => 'b',
 
661
        explicit_alias => 1,
 
662
        join           => {
 
663
            to   => 't1',
 
664
            type => 'inner',
 
665
            ansi => 0,
 
666
         },
 
667
      }
 
668
   ],
 
669
);
 
670
 
 
671
 
 
672
test_from(
 
673
   't1 JOIN t2 ON t1.id=t2.id',
 
674
   [
 
675
      {
 
676
         tbl => 't1',
 
677
      },
 
678
      {
 
679
         tbl => 't2',
 
680
         join => {
 
681
            to         => 't1',
 
682
            type       => 'inner',
 
683
            condition  => 'on',
 
684
            where      => [
 
685
               {
 
686
                  predicate => undef,
 
687
                  left_arg  => 't1.id',
 
688
                  operator  => '=',
 
689
                  right_arg => 't2.id',
 
690
               },
 
691
            ],
 
692
            ansi       => 1,
 
693
         },
 
694
      }
 
695
   ],
 
696
);
 
697
 
 
698
test_from(
 
699
   't1 a JOIN t2 as b USING (id)',
 
700
   [
 
701
      {
 
702
         tbl  => 't1',
 
703
         alias => 'a',
 
704
      },
 
705
      {
 
706
         tbl  => 't2',
 
707
         alias => 'b',
 
708
         explicit_alias => 1,
 
709
         join  => {
 
710
            to         => 't1',
 
711
            type       => 'inner',
 
712
            condition  => 'using',
 
713
            columns    => ['id'],
 
714
            ansi       => 1,
 
715
         },
 
716
      },
 
717
   ],
 
718
);
 
719
 
 
720
test_from(
 
721
   't1 JOIN t2 ON t1.id=t2.id JOIN t3 ON t1.id=t3.id',
 
722
   [
 
723
      {
 
724
         tbl  => 't1',
 
725
      },
 
726
      {
 
727
         tbl  => 't2',
 
728
         join  => {
 
729
            to         => 't1',
 
730
            type       => 'inner',
 
731
            condition  => 'on',
 
732
            where      => [
 
733
               {
 
734
                  predicate => undef,
 
735
                  left_arg  => 't1.id',
 
736
                  operator  => '=',
 
737
                  right_arg => 't2.id',
 
738
               },
 
739
            ],
 
740
            ansi       => 1,
 
741
         },
 
742
      },
 
743
      {
 
744
         tbl  => 't3',
 
745
         join  => {
 
746
            to         => 't2',
 
747
            type       => 'inner',
 
748
            condition  => 'on',
 
749
            where      => [
 
750
               {
 
751
                  predicate => undef,
 
752
                  left_arg  => 't1.id',
 
753
                  operator  => '=',
 
754
                  right_arg => 't3.id',
 
755
               },
 
756
            ],
 
757
            ansi       => 1,
 
758
         },
 
759
      },
 
760
   ],
 
761
);
 
762
 
 
763
test_from(
 
764
   't1 AS a LEFT JOIN t2 b ON a.id = b.id',
 
765
   [
 
766
      {
 
767
         tbl  => 't1',
 
768
         alias => 'a',
 
769
         explicit_alias => 1,
 
770
      },
 
771
      {
 
772
         tbl  => 't2',
 
773
         alias => 'b',
 
774
         join  => {
 
775
            to         => 't1',
 
776
            type       => 'left',
 
777
            condition  => 'on',
 
778
            where      => [
 
779
               {
 
780
                  predicate => undef,
 
781
                  left_arg  => 'a.id',
 
782
                  operator  => '=',
 
783
                  right_arg => 'b.id',
 
784
               },
 
785
            ],
 
786
            ansi       => 1,
 
787
         },
 
788
      },
 
789
   ],
 
790
);
 
791
 
 
792
test_from(
 
793
   't1 a NATURAL RIGHT OUTER JOIN t2 b',
 
794
   [
 
795
      {
 
796
         tbl  => 't1',
 
797
         alias => 'a',
 
798
      },
 
799
      {
 
800
         tbl  => 't2',
 
801
         alias => 'b',
 
802
         join  => {
 
803
            to   => 't1',
 
804
            type => 'natural right outer',
 
805
            ansi => 1,
 
806
         },
 
807
      },
 
808
   ],
 
809
);
 
810
 
 
811
# http://pento.net/2009/04/03/join-and-comma-precedence/
 
812
test_from(
 
813
   'a, b LEFT JOIN c ON c.c = a.a',
 
814
   [
 
815
      {
 
816
         tbl  => 'a',
 
817
      },
 
818
      {
 
819
         tbl  => 'b',
 
820
         join  => {
 
821
            to   => 'a',
 
822
            type => 'inner',
 
823
            ansi => 0,
 
824
         },
 
825
      },
 
826
      {
 
827
         tbl  => 'c',
 
828
         join  => {
 
829
            to         => 'b',
 
830
            type       => 'left',
 
831
            condition  => 'on',
 
832
            where      => [
 
833
               {
 
834
                  predicate => undef,
 
835
                  left_arg  => 'c.c',
 
836
                  operator  => '=',
 
837
                  right_arg => 'a.a',
 
838
               },
 
839
            ],
 
840
            ansi       => 1, 
 
841
         },
 
842
      },
 
843
   ],
 
844
);
 
845
 
 
846
test_from(
 
847
   'a, b, c CROSS JOIN d USING (id)',
 
848
   [
 
849
      {
 
850
         tbl  => 'a',
 
851
      },
 
852
      {
 
853
         tbl  => 'b',
 
854
         join  => {
 
855
            to   => 'a',
 
856
            type => 'inner',
 
857
            ansi => 0,
 
858
         },
 
859
      },
 
860
      {
 
861
         tbl  => 'c',
 
862
         join  => {
 
863
            to   => 'b',
 
864
            type => 'inner',
 
865
            ansi => 0,
 
866
         },
 
867
      },
 
868
      {
 
869
         tbl  => 'd',
 
870
         join  => {
 
871
            to         => 'c',
 
872
            type       => 'cross',
 
873
            condition  => 'using',
 
874
            columns    => ['id'],
 
875
            ansi       => 1, 
 
876
         },
 
877
      },
 
878
   ],
 
879
);
 
880
 
 
881
# Index hints.
 
882
test_from(
 
883
   'tbl FORCE INDEX (foo)',
 
884
   [
 
885
      {
 
886
         tbl       => 'tbl',
 
887
         index_hint => 'FORCE INDEX (foo)',
 
888
      }
 
889
   ]
 
890
);
 
891
 
 
892
test_from(
 
893
   'tbl USE INDEX(foo)',
 
894
   [
 
895
      {
 
896
         tbl       => 'tbl',
 
897
         index_hint => 'USE INDEX(foo)',
 
898
      }
 
899
   ]
 
900
);
 
901
 
 
902
test_from(
 
903
   'tbl FORCE KEY(foo)',
 
904
   [
 
905
      {
 
906
         tbl       => 'tbl',
 
907
         index_hint => 'FORCE KEY(foo)',
 
908
      }
 
909
   ]
 
910
);
 
911
 
 
912
test_from(
 
913
   'tbl t FORCE KEY(foo)',
 
914
   [
 
915
      {
 
916
         tbl       => 'tbl',
 
917
         alias      => 't',
 
918
         index_hint => 'FORCE KEY(foo)',
 
919
      }
 
920
   ]
 
921
);
 
922
 
 
923
test_from(
 
924
   'tbl AS t FORCE KEY(foo)',
 
925
   [
 
926
      {
 
927
         tbl           => 'tbl',
 
928
         alias          => 't',
 
929
         explicit_alias => 1,
 
930
         index_hint     => 'FORCE KEY(foo)',
 
931
      }
 
932
   ]
 
933
);
 
934
 
 
935
# Database-qualified tables.
 
936
test_from(
 
937
   'db.tbl',
 
938
   [{
 
939
      db   => 'db',
 
940
      tbl => 'tbl',
 
941
   }],
 
942
);
 
943
 
 
944
test_from(
 
945
   '`db`.`tbl`',
 
946
   [{
 
947
      db   => 'db',
 
948
      tbl => 'tbl',
 
949
   }],
 
950
);
 
951
 
 
952
test_from(
 
953
   '`ryan likes`.`to break stuff`',
 
954
   [{
 
955
      db   => 'ryan likes',
 
956
      tbl => 'to break stuff',
 
957
   }],
 
958
);
 
959
 
 
960
test_from(
 
961
   '`db`.`tbl` LEFT JOIN `foo`.`bar` USING (glue)',
 
962
   [
 
963
      {  db   => 'db',
 
964
         tbl => 'tbl',
 
965
      },
 
966
      {  db   => 'foo',
 
967
         tbl => 'bar',
 
968
         join => {
 
969
            to        => 'tbl',
 
970
            type      => 'left',
 
971
            ansi      => 1,
 
972
            condition => 'using',
 
973
            columns   => ['glue'],
 
974
         },
 
975
      },
 
976
   ],
 
977
);
 
978
 
 
979
test_from(
 
980
   'tblB AS dates LEFT JOIN dbF.tblC AS scraped ON dates.dt = scraped.dt AND dates.version = scraped.version',
 
981
   [
 
982
      {
 
983
        tbl           => 'tblB',
 
984
        alias          => 'dates',
 
985
        explicit_alias => 1,
 
986
      },
 
987
      {
 
988
        tbl           => 'tblC',
 
989
        alias          => 'scraped',
 
990
        explicit_alias => 1,
 
991
        db             => 'dbF',
 
992
        join           => {
 
993
          condition  => 'on',
 
994
          ansi       => 1,
 
995
          to         => 'tblB',
 
996
          type       => 'left',
 
997
          where      => [
 
998
            {
 
999
              predicate => undef,
 
1000
              left_arg  => 'dates.dt',
 
1001
              operator  => '=',
 
1002
              right_arg => 'scraped.dt',
 
1003
            },
 
1004
            {
 
1005
              predicate => 'and',
 
1006
              left_arg  => 'dates.version',
 
1007
              operator  => '=',
 
1008
              right_arg => 'scraped.version',
 
1009
            },
 
1010
          ],
 
1011
        },
 
1012
      },
 
1013
   ],
 
1014
);
 
1015
 
 
1016
# The parser needs to match the join condition verb ON or USING
 
1017
# but these table names have those words embedded in the full
 
1018
# table name.
 
1019
test_from(
 
1020
   "db.version",
 
1021
   [ { db=>'db', tbl=>'version', } ],
 
1022
);
 
1023
 
 
1024
test_from(
 
1025
   "db.like_using_odd_table_names",
 
1026
   [ { db=>'db', tbl=>'like_using_odd_table_names', } ],
 
1027
);
 
1028
 
 
1029
test_from(
 
1030
   "db.`on`",  # don't name your table this :-(
 
1031
   [ { db=>'db', tbl=>'on', } ],
 
1032
);
 
1033
 
 
1034
test_from(
 
1035
   "db.`using`",  # or this
 
1036
   [ { db=>'db', tbl=>'using', } ],
 
1037
);
 
1038
 
 
1039
# #############################################################################
 
1040
# parse_table_reference()
 
1041
# #############################################################################
 
1042
sub test_parse_table_reference {
 
1043
   my ( $tbl, $struct ) = @_;
 
1044
   my $s = $sp->parse_table_reference($tbl);
 
1045
   is_deeply(
 
1046
      $s,
 
1047
      $struct,
 
1048
      $tbl
 
1049
   );
 
1050
   return;
 
1051
}
 
1052
 
 
1053
test_parse_table_reference('tbl',
 
1054
   { tbl => 'tbl', }
 
1055
);
 
1056
 
 
1057
test_parse_table_reference('tbl a',
 
1058
   { tbl => 'tbl', alias => 'a', }
 
1059
);
 
1060
 
 
1061
test_parse_table_reference('tbl as a',
 
1062
   { tbl => 'tbl', alias => 'a', explicit_alias => 1, }
 
1063
);
 
1064
 
 
1065
test_parse_table_reference('tbl AS a',
 
1066
   { tbl => 'tbl', alias => 'a', explicit_alias => 1, }
 
1067
);
 
1068
 
 
1069
test_parse_table_reference('db.tbl',
 
1070
   { tbl => 'tbl', db => 'db', }
 
1071
);
 
1072
 
 
1073
test_parse_table_reference('db.tbl a',
 
1074
   { tbl => 'tbl', db => 'db', alias => 'a', }
 
1075
);
 
1076
 
 
1077
test_parse_table_reference('db.tbl AS a',
 
1078
   { tbl => 'tbl', db => 'db', alias => 'a', explicit_alias => 1, }
 
1079
);
 
1080
 
 
1081
 
 
1082
test_parse_table_reference('`tbl`',
 
1083
   { tbl => 'tbl', }
 
1084
);
 
1085
 
 
1086
test_parse_table_reference('`tbl` `a`',
 
1087
   { tbl => 'tbl', alias => 'a', }
 
1088
);
 
1089
 
 
1090
test_parse_table_reference('`tbl` as `a`',
 
1091
   { tbl => 'tbl', alias => 'a', explicit_alias => 1, }
 
1092
);
 
1093
 
 
1094
test_parse_table_reference('`tbl` AS `a`',
 
1095
   { tbl => 'tbl', alias => 'a', explicit_alias => 1, }
 
1096
);
 
1097
 
 
1098
test_parse_table_reference('`db`.`tbl`',
 
1099
   { tbl => 'tbl', db => 'db', }
 
1100
);
 
1101
 
 
1102
test_parse_table_reference('`db`.`tbl` `a`',
 
1103
   { tbl => 'tbl', db => 'db', alias => 'a', }
 
1104
);
 
1105
 
 
1106
test_parse_table_reference('`db`.`tbl` AS `a`',
 
1107
   { tbl => 'tbl', db => 'db', alias => 'a', explicit_alias => 1, }
 
1108
);
 
1109
 
 
1110
# #############################################################################
 
1111
# parse_columns()
 
1112
# #############################################################################
 
1113
sub test_parse_columns {
 
1114
   my ( $cols, $struct ) = @_;
 
1115
   my $s = $sp->parse_columns($cols);
 
1116
   is_deeply(
 
1117
      $s,
 
1118
      $struct,
 
1119
      $cols,
 
1120
   );
 
1121
   return;
 
1122
}
 
1123
 
 
1124
test_parse_columns('tbl.* foo',
 
1125
   [ { col => '*', tbl => 'tbl', alias => 'foo' } ],
 
1126
);
 
1127
 
 
1128
# #############################################################################
 
1129
# parse_set()
 
1130
# #############################################################################
 
1131
sub test_parse_set {
 
1132
   my ( $set, $struct ) = @_;
 
1133
   my $got = $sp->parse_set($set);
 
1134
   is_deeply(
 
1135
      $got,
 
1136
      $struct,
 
1137
      "parse_set($set)"
 
1138
   ) or print Dumper($got);
 
1139
}
 
1140
 
 
1141
test_parse_set(
 
1142
   "col='val'",
 
1143
   [{col=>"col", value=>"'val'"}],
 
1144
);
 
1145
 
 
1146
test_parse_set(
 
1147
   'a.foo="bar", b.foo=NOW()',
 
1148
   [
 
1149
      {tbl=>"a", col=>"foo", value=>'"bar"'},
 
1150
      {tbl=>"b", col=>"foo", value=>'NOW()'},
 
1151
   ],
 
1152
);
 
1153
 
 
1154
# #############################################################################
 
1155
# Subqueries.
 
1156
# #############################################################################
 
1157
 
 
1158
my $query = "DELETE FROM t1
 
1159
WHERE s11 > ANY
 
1160
(SELECT COUNT(*) /* no hint */ FROM t2 WHERE NOT EXISTS
 
1161
   (SELECT * FROM t3 WHERE ROW(5*t2.s1,77)=
 
1162
      (SELECT 50,11*s1 FROM
 
1163
         (SELECT * FROM t5) AS t5
 
1164
      )
 
1165
   )
 
1166
)";
 
1167
my @subqueries = $sp->remove_subqueries($sp->clean_query($query));
 
1168
is_deeply(
 
1169
   \@subqueries,
 
1170
   [
 
1171
      'DELETE FROM t1 WHERE s11 > ANY (__SQ3__)',
 
1172
      {
 
1173
         query   => 'SELECT * FROM t5',
 
1174
         context => 'identifier',
 
1175
         nested  => 1,
 
1176
      },
 
1177
      {
 
1178
         query   => 'SELECT 50,11*s1 FROM __SQ0__ AS t5',
 
1179
         context => 'scalar',
 
1180
         nested  => 2,
 
1181
      },
 
1182
      {
 
1183
         query   => 'SELECT * FROM t3 WHERE ROW(5*t2.s1,77)= __SQ1__',
 
1184
         context => 'list',
 
1185
         nested  => 3,
 
1186
      },
 
1187
      {
 
1188
         query   => 'SELECT COUNT(*)  FROM t2 WHERE NOT EXISTS (__SQ2__)',
 
1189
         context => 'list',
 
1190
      }
 
1191
   ],
 
1192
   'DELETE with nested subqueries'
 
1193
);
 
1194
 
 
1195
$query = "select col from tbl
 
1196
          where id=(select max(id) from tbl2 where foo='bar') limit 1";
 
1197
@subqueries = $sp->remove_subqueries($sp->clean_query($query));
 
1198
is_deeply(
 
1199
   \@subqueries,
 
1200
   [
 
1201
      'select col from tbl where id=__SQ0__ limit 1',
 
1202
      {
 
1203
         query   => "select max(id) from tbl2 where foo='bar'",
 
1204
         context => 'scalar',
 
1205
      },
 
1206
   ],
 
1207
   'Subquery as scalar'
 
1208
);
 
1209
 
 
1210
$query = "select col from tbl
 
1211
          where id=(select max(id) from tbl2 where foo='bar') and col in(select foo from tbl3) limit 1";
 
1212
@subqueries = $sp->remove_subqueries($sp->clean_query($query));
 
1213
is_deeply(
 
1214
   \@subqueries,
 
1215
   [
 
1216
      'select col from tbl where id=__SQ1__ and col in(__SQ0__) limit 1',
 
1217
      {
 
1218
         query   => "select foo from tbl3",
 
1219
         context => 'list',
 
1220
      },
 
1221
      {
 
1222
         query   => "select max(id) from tbl2 where foo='bar'",
 
1223
         context => 'scalar',
 
1224
      },
 
1225
   ],
 
1226
   'Subquery as scalar and IN()'
 
1227
);
 
1228
 
 
1229
$query = "SELECT NOW() AS a1, (SELECT f1(5)) AS a2";
 
1230
@subqueries = $sp->remove_subqueries($sp->clean_query($query));
 
1231
is_deeply(
 
1232
   \@subqueries,
 
1233
   [
 
1234
      'SELECT NOW() AS a1, __SQ0__ AS a2',
 
1235
      {
 
1236
         query   => "SELECT f1(5)",
 
1237
         context => 'identifier',
 
1238
      },
 
1239
   ],
 
1240
   'Subquery as SELECT column'
 
1241
);
 
1242
 
 
1243
$query = "SELECT DISTINCT store_type FROM stores s1
 
1244
WHERE NOT EXISTS (
 
1245
SELECT * FROM cities WHERE NOT EXISTS (
 
1246
SELECT * FROM cities_stores
 
1247
WHERE cities_stores.city = cities.city
 
1248
AND cities_stores.store_type = stores.store_type))";
 
1249
@subqueries = $sp->remove_subqueries(
 
1250
   $sp->clean_query($sp->normalize_keyword_spaces($query)));
 
1251
is_deeply(
 
1252
   \@subqueries,
 
1253
   [
 
1254
      'SELECT DISTINCT store_type FROM stores s1 WHERE NOT EXISTS (__SQ1__)',
 
1255
      {
 
1256
         query   => "SELECT * FROM cities_stores WHERE cities_stores.city = cities.city AND cities_stores.store_type = stores.store_type",
 
1257
         context => 'list',
 
1258
         nested  => 1,
 
1259
      },
 
1260
      {
 
1261
         query   => "SELECT * FROM cities WHERE NOT EXISTS (__SQ0__)",
 
1262
         context => 'list',
 
1263
      },
 
1264
   ],
 
1265
   'Two nested NOT EXISTS subqueries'
 
1266
);
 
1267
 
 
1268
$query = "select col from tbl
 
1269
          where id=(select max(id) from tbl2 where foo='bar')
 
1270
          and col in(select foo from
 
1271
            (select b from fn where id=1
 
1272
               and b > any(select a from a)
 
1273
            )
 
1274
         ) limit 1";
 
1275
@subqueries = $sp->remove_subqueries($sp->clean_query($query));
 
1276
is_deeply(
 
1277
   \@subqueries,
 
1278
   [
 
1279
      'select col from tbl where id=__SQ3__ and col in(__SQ2__) limit 1',
 
1280
      {
 
1281
         query   => 'select a from a',
 
1282
         context => 'list',
 
1283
         nested  => 1,
 
1284
      },
 
1285
      {
 
1286
         query   => 'select b from fn where id=1 and b > any(__SQ0__)',
 
1287
         context => 'identifier',
 
1288
         nested  => 2,
 
1289
      },
 
1290
      {
 
1291
         query   => 'select foo from __SQ1__',
 
1292
         context => 'list',
 
1293
      },
 
1294
      {
 
1295
         query   => 'select max(id) from tbl2 where foo=\'bar\'',
 
1296
         context => 'scalar',
 
1297
      },
 
1298
   ],
 
1299
   'Mutiple and nested subqueries'
 
1300
);
 
1301
 
 
1302
$query = "select (select now()) from universe";
 
1303
@subqueries = $sp->remove_subqueries($sp->clean_query($query));
 
1304
is_deeply(
 
1305
   \@subqueries,
 
1306
   [
 
1307
      'select __SQ0__ from universe',
 
1308
      {
 
1309
         query   => 'select now()',
 
1310
         context => 'identifier',
 
1311
      },
 
1312
   ],
 
1313
   'Subquery as non-aliased column identifier'
 
1314
);
 
1315
 
 
1316
# #############################################################################
 
1317
# Test parsing full queries.
 
1318
# #############################################################################
 
1319
 
 
1320
my @cases = (
 
1321
 
 
1322
   # ########################################################################
 
1323
   # DELETE
 
1324
   # ########################################################################
 
1325
   {  name   => 'DELETE FROM',
 
1326
      query  => 'DELETE FROM tbl',
 
1327
      struct => {
 
1328
         type    => 'delete',
 
1329
         clauses => { from => 'tbl', },
 
1330
         from    => [ { tbl => 'tbl', } ],
 
1331
         unknown => undef,
 
1332
      },
 
1333
   },
 
1334
   {  name   => 'DELETE FROM WHERE',
 
1335
      query  => 'DELETE FROM tbl WHERE id=1',
 
1336
      struct => {
 
1337
         type    => 'delete',
 
1338
         clauses => { 
 
1339
            from  => 'tbl ',
 
1340
            where => 'id=1',
 
1341
         },
 
1342
         from    => [ { tbl => 'tbl', } ],
 
1343
         where   => [
 
1344
            {
 
1345
               predicate => undef,
 
1346
               left_arg  => 'id',
 
1347
               operator  => '=',
 
1348
               right_arg => '1',
 
1349
            },
 
1350
         ],
 
1351
         unknown => undef,
 
1352
      },
 
1353
   },
 
1354
   {  name   => 'DELETE FROM LIMIT',
 
1355
      query  => 'DELETE FROM tbl LIMIT 5',
 
1356
      struct => {
 
1357
         type    => 'delete',
 
1358
         clauses => {
 
1359
            from  => 'tbl ',
 
1360
            limit => '5',
 
1361
         },
 
1362
         from    => [ { tbl => 'tbl', } ],
 
1363
         limit   => {
 
1364
            row_count => 5,
 
1365
         },
 
1366
         unknown => undef,
 
1367
      },
 
1368
   },
 
1369
   {  name   => 'DELETE FROM ORDER BY',
 
1370
      query  => 'DELETE FROM tbl ORDER BY foo',
 
1371
      struct => {
 
1372
         type    => 'delete',
 
1373
         clauses => {
 
1374
            from     => 'tbl ',
 
1375
            order_by => 'foo',
 
1376
         },
 
1377
         from     => [ { tbl => 'tbl', } ],
 
1378
         order_by => [{column=>'foo'}],
 
1379
         unknown  => undef,
 
1380
      },
 
1381
   },
 
1382
   {  name   => 'DELETE FROM WHERE LIMIT',
 
1383
      query  => 'DELETE FROM tbl WHERE id=1 LIMIT 3',
 
1384
      struct => {
 
1385
         type    => 'delete',
 
1386
         clauses => { 
 
1387
            from  => 'tbl ',
 
1388
            where => 'id=1 ',
 
1389
            limit => '3',
 
1390
         },
 
1391
         from    => [ { tbl => 'tbl', } ],
 
1392
         where   => [
 
1393
            {
 
1394
               predicate => undef,
 
1395
               left_arg  => 'id',
 
1396
               operator  => '=',
 
1397
               right_arg  => '1',
 
1398
            },
 
1399
         ],
 
1400
         limit   => {
 
1401
            row_count => 3,
 
1402
         },
 
1403
         unknown => undef,
 
1404
      },
 
1405
   },
 
1406
   {  name   => 'DELETE FROM WHERE ORDER BY',
 
1407
      query  => 'DELETE FROM tbl WHERE id=1 ORDER BY id',
 
1408
      struct => {
 
1409
         type    => 'delete',
 
1410
         clauses => { 
 
1411
            from     => 'tbl ',
 
1412
            where    => 'id=1 ',
 
1413
            order_by => 'id',
 
1414
         },
 
1415
         from     => [ { tbl => 'tbl', } ],
 
1416
         where   => [
 
1417
            {
 
1418
               predicate => undef,
 
1419
               left_arg  => 'id',
 
1420
               operator  => '=',
 
1421
               right_arg => '1',
 
1422
            },
 
1423
         ],
 
1424
         order_by => [{column=>'id'}],
 
1425
         unknown  => undef,
 
1426
      },
 
1427
   },
 
1428
   {  name   => 'DELETE FROM WHERE ORDER BY LIMIT',
 
1429
      query  => 'DELETE FROM tbl WHERE id=1 ORDER BY id ASC LIMIT 1 OFFSET 3',
 
1430
      struct => {
 
1431
         type    => 'delete',
 
1432
         clauses => { 
 
1433
            from     => 'tbl ',
 
1434
            where    => 'id=1 ',
 
1435
            order_by => 'id ASC ',
 
1436
            limit    => '1 OFFSET 3',
 
1437
         },
 
1438
         from    => [ { tbl => 'tbl', } ],
 
1439
         where   => [
 
1440
            {
 
1441
               predicate => undef,
 
1442
               left_arg  => 'id',
 
1443
               operator  => '=',
 
1444
               right_arg => '1',
 
1445
            },
 
1446
         ],
 
1447
         order_by=> [{column=>'id', sort=>'ASC'}],
 
1448
         limit   => {
 
1449
            row_count       => 1,
 
1450
            offset          => 3,
 
1451
            explicit_offset => 1,
 
1452
         },
 
1453
         unknown => undef,
 
1454
      },
 
1455
   },
 
1456
 
 
1457
   # ########################################################################
 
1458
   # INSERT
 
1459
   # ########################################################################
 
1460
   {  name   => 'INSERT INTO VALUES',
 
1461
      query  => 'INSERT INTO tbl VALUES (1,"foo")',
 
1462
      struct => {
 
1463
         type    => 'insert',
 
1464
         clauses => { 
 
1465
            into   => 'tbl',
 
1466
            values => '(1,"foo")',
 
1467
         },
 
1468
         into   => [ { tbl => 'tbl', } ],
 
1469
         values => [ '1', q{"foo"}, ],
 
1470
         unknown => undef,
 
1471
      },
 
1472
   },
 
1473
   {  name   => 'INSERT INTO VALUES with complex CSV values',
 
1474
      query  => 'INSERT INTO tbl VALUES ("hello, world!", "", a, \'b\')',
 
1475
      struct => {
 
1476
         type    => 'insert',
 
1477
         clauses => { 
 
1478
            into   => 'tbl',
 
1479
            values => '("hello, world!", "", a, \'b\')',
 
1480
         },
 
1481
         into   => [ { tbl => 'tbl', } ],
 
1482
         values => [
 
1483
            q{"hello, world!"},
 
1484
            q{""},
 
1485
            q{a},
 
1486
            q{'b'},
 
1487
         ],
 
1488
         unknown => undef,
 
1489
      },
 
1490
   },
 
1491
   {  name   => 'INSERT VALUE',
 
1492
      query  => 'INSERT tbl VALUE (1,"foo")',
 
1493
      struct => {
 
1494
         type    => 'insert',
 
1495
         clauses => { 
 
1496
            into   => 'tbl',
 
1497
            values => '(1,"foo")',
 
1498
         },
 
1499
         into   => [ { tbl => 'tbl', } ],
 
1500
         values => [ '1', q{"foo"}, ],
 
1501
         unknown => undef,
 
1502
      },
 
1503
   },
 
1504
   {  name   => 'INSERT INTO cols VALUES',
 
1505
      query  => 'INSERT INTO db.tbl (id, name) VALUE (2,"bob")',
 
1506
      struct => {
 
1507
         type    => 'insert',
 
1508
         clauses => { 
 
1509
            into    => 'db.tbl',
 
1510
            columns => 'id, name ',
 
1511
            values  => '(2,"bob")',
 
1512
         },
 
1513
         into    => [ { tbl => 'tbl', db => 'db' } ],
 
1514
         columns => [ { col => 'id' }, { col => 'name' } ],
 
1515
         values  => [ '2', q{"bob"} ],
 
1516
         unknown => undef,
 
1517
      },
 
1518
   },
 
1519
   {  name   => 'INSERT INTO VALUES ON DUPLICATE',
 
1520
      query  => 'INSERT INTO tbl VALUE (3,"bob") ON DUPLICATE KEY UPDATE col1=9',
 
1521
      struct => {
 
1522
         type    => 'insert',
 
1523
         clauses => { 
 
1524
            into         => 'tbl',
 
1525
            values       => '(3,"bob")',
 
1526
            on_duplicate => 'col1=9',
 
1527
         },
 
1528
         into         => [ { tbl => 'tbl', } ],
 
1529
         values       => [ '3', q{"bob"} ],
 
1530
         on_duplicate => ['col1=9',],
 
1531
         unknown      => undef,
 
1532
      },
 
1533
   },
 
1534
   {  name   => 'INSERT INTO SET',
 
1535
      query  => 'INSERT INTO tbl SET id=1, foo=NULL',
 
1536
      struct => {
 
1537
         type    => 'insert',
 
1538
         clauses => { 
 
1539
            into => 'tbl',
 
1540
            set  => 'id=1, foo=NULL',
 
1541
         },
 
1542
         into    => [ { tbl => 'tbl', } ],
 
1543
         set     => [
 
1544
            { col => 'id',  value => '1',    },
 
1545
            { col => 'foo', value => 'NULL', },
 
1546
         ],
 
1547
         unknown => undef,
 
1548
      },
 
1549
   },
 
1550
   {  name   => 'INSERT INTO SET ON DUPLICATE',
 
1551
      query  => 'INSERT INTO tbl SET i=3 ON DUPLICATE KEY UPDATE col1=9',
 
1552
      struct => {
 
1553
         type    => 'insert',
 
1554
         clauses => { 
 
1555
            into         => 'tbl',
 
1556
            set          => 'i=3',
 
1557
            on_duplicate => 'col1=9',
 
1558
         },
 
1559
         into         => [ { tbl => 'tbl', } ],
 
1560
         set          => [{col =>'i', value=>'3'}],
 
1561
         on_duplicate => ['col1=9',],
 
1562
         unknown      => undef,
 
1563
      },
 
1564
   },
 
1565
   {  name   => 'INSERT ... SELECT',
 
1566
      query  => 'INSERT INTO tbl (col) SELECT id FROM tbl2 WHERE id > 100',
 
1567
      struct => {
 
1568
         type    => 'insert',
 
1569
         clauses => { 
 
1570
            into    => 'tbl',
 
1571
            columns => 'col ',
 
1572
            select  => 'id FROM tbl2 WHERE id > 100',
 
1573
         },
 
1574
         into         => [ { tbl => 'tbl', } ],
 
1575
         columns      => [ { col => 'col' } ],
 
1576
         select       => {
 
1577
            type    => 'select',
 
1578
            clauses => { 
 
1579
               columns => 'id ',
 
1580
               from    => 'tbl2 ',
 
1581
               where   => 'id > 100',
 
1582
            },
 
1583
            columns => [ { col => 'id' } ],
 
1584
            from    => [ { tbl => 'tbl2', } ],
 
1585
            where   => [
 
1586
               {
 
1587
                  predicate => undef,
 
1588
                  left_arg  => 'id',
 
1589
                  operator  => '>',
 
1590
                  right_arg => '100',
 
1591
               },
 
1592
            ],
 
1593
            unknown => undef,
 
1594
         },
 
1595
         unknown      => undef,
 
1596
      },
 
1597
   },
 
1598
   {  name   => 'INSERT INTO VALUES()',
 
1599
      query  => 'INSERT INTO db.tbl (id, name) VALUES(2,"bob")',
 
1600
      struct => {
 
1601
         type    => 'insert',
 
1602
         clauses => { 
 
1603
            into    => 'db.tbl',
 
1604
            columns => 'id, name ',
 
1605
            values  => '(2,"bob")',
 
1606
         },
 
1607
         into    => [ { tbl => 'tbl', db => 'db' } ],
 
1608
         columns => [ { col => 'id' }, { col => 'name' } ],
 
1609
         values  => [ '2', q{"bob"} ],
 
1610
         unknown => undef,
 
1611
      },
 
1612
   },
 
1613
 
 
1614
   # ########################################################################
 
1615
   # REPLACE
 
1616
   # ########################################################################
 
1617
   # REPLACE are parsed by parse_insert() so if INSERT is well-tested we
 
1618
   # shouldn't need to test REPLACE much.
 
1619
   {  name   => 'REPLACE INTO VALUES',
 
1620
      query  => 'REPLACE INTO tbl VALUES (1,"foo")',
 
1621
      struct => {
 
1622
         type    => 'replace',
 
1623
         clauses => { 
 
1624
            into   => 'tbl',
 
1625
            values => '(1,"foo")',
 
1626
         },
 
1627
         into   => [ { tbl => 'tbl', } ],
 
1628
         values => [ '1', q{"foo"} ],
 
1629
         unknown => undef,
 
1630
      },
 
1631
   },
 
1632
   {  name   => 'REPLACE VALUE',
 
1633
      query  => 'REPLACE tbl VALUE (1,"foo")',
 
1634
      struct => {
 
1635
         type    => 'replace',
 
1636
         clauses => { 
 
1637
            into   => 'tbl',
 
1638
            values => '(1,"foo")',
 
1639
         },
 
1640
         into   => [ { tbl => 'tbl', } ],
 
1641
         values => [ '1', q{"foo"} ],
 
1642
         unknown => undef,
 
1643
      },
 
1644
   },
 
1645
   {  name   => 'REPLACE INTO cols VALUES',
 
1646
      query  => 'REPLACE INTO db.tbl (id, name) VALUE (2,"bob")',
 
1647
      struct => {
 
1648
         type    => 'replace',
 
1649
         clauses => { 
 
1650
            into    => 'db.tbl',
 
1651
            columns => 'id, name ',
 
1652
            values  => '(2,"bob")',
 
1653
         },
 
1654
         into    => [ { tbl => 'tbl', db => 'db' } ],
 
1655
         columns => [ { col => 'id' }, { col => 'name' } ],
 
1656
         values  => [ '2', q{"bob"} ],
 
1657
         unknown => undef,
 
1658
      },
 
1659
   },
 
1660
   {
 
1661
      name  => 'REPLACE SELECT JOIN ON',
 
1662
      query => 'REPLACE INTO db.tblA (dt, ncpc) SELECT dates.dt, scraped.total_r FROM tblB AS dates LEFT JOIN dbF.tblC AS scraped ON dates.dt = scraped.dt AND dates.version = scraped.version',
 
1663
      struct => {
 
1664
         type    => 'replace',
 
1665
         clauses => {
 
1666
            columns => 'dt, ncpc ',
 
1667
            into    => 'db.tblA',
 
1668
            select  => 'dates.dt, scraped.total_r FROM tblB AS dates LEFT JOIN dbF.tblC AS scraped ON dates.dt = scraped.dt AND dates.version = scraped.version',
 
1669
         },
 
1670
         columns => [ { col => 'dt' }, { col => 'ncpc' } ],
 
1671
         into    => [ { db => 'db', tbl => 'tblA' } ],
 
1672
         select  => {
 
1673
            type    => 'select',
 
1674
            clauses => {
 
1675
               columns => 'dates.dt, scraped.total_r ',
 
1676
               from    => 'tblB AS dates LEFT JOIN dbF.tblC AS scraped ON dates.dt = scraped.dt AND dates.version = scraped.version',
 
1677
            },
 
1678
            columns => [
 
1679
               { tbl => 'dates',   col => 'dt'      },
 
1680
               { tbl => 'scraped', col => 'total_r' },
 
1681
            ],
 
1682
            from    => [
 
1683
               {
 
1684
                 tbl            => 'tblB',
 
1685
                 alias          => 'dates',
 
1686
                 explicit_alias => 1,
 
1687
               },
 
1688
               {
 
1689
                 tbl            => 'tblC',
 
1690
                 alias          => 'scraped',
 
1691
                 explicit_alias => 1,
 
1692
                 db             => 'dbF',
 
1693
                 join           => {
 
1694
                   condition => 'on',
 
1695
                   ansi  => 1,
 
1696
                   to    => 'tblB',
 
1697
                   type  => 'left',
 
1698
                   where => [
 
1699
                     {
 
1700
                       predicate => undef,
 
1701
                       left_arg  => 'dates.dt',
 
1702
                       operator  => '=',
 
1703
                       right_arg => 'scraped.dt',
 
1704
                     },
 
1705
                     {
 
1706
                       predicate => 'and',
 
1707
                       left_arg  => 'dates.version',
 
1708
                       operator  => '=',
 
1709
                       right_arg => 'scraped.version',
 
1710
                     },
 
1711
                   ],
 
1712
                 },
 
1713
               },
 
1714
            ],
 
1715
            unknown => undef,
 
1716
         },
 
1717
         unknown => undef,
 
1718
      },
 
1719
   },
 
1720
 
 
1721
   # ########################################################################
 
1722
   # SELECT
 
1723
   # ########################################################################
 
1724
   {  name   => 'SELECT',
 
1725
      query  => 'SELECT NOW()',
 
1726
      struct => {
 
1727
         type    => 'select',
 
1728
         clauses => { 
 
1729
            columns => 'NOW()',
 
1730
         },
 
1731
         columns => [ { col => 'NOW()' } ],
 
1732
         unknown => undef,
 
1733
      },
 
1734
   },
 
1735
   {  name   => 'SELECT var',
 
1736
      query  => 'select @@version_comment',
 
1737
      struct => {
 
1738
         type    => 'select',
 
1739
         clauses => { 
 
1740
            columns => '@@version_comment',
 
1741
         },
 
1742
         columns => [ { col => '@@version_comment' } ],
 
1743
         unknown => undef,
 
1744
      },
 
1745
   },
 
1746
   {  name   => 'SELECT FROM',
 
1747
      query  => 'SELECT col1, col2 FROM tbl',
 
1748
      struct => {
 
1749
         type    => 'select',
 
1750
         clauses => { 
 
1751
            columns => 'col1, col2 ',
 
1752
            from    => 'tbl',
 
1753
         },
 
1754
         columns => [ { col => 'col1' }, { col => 'col2' } ],
 
1755
         from    => [ { tbl => 'tbl', } ],
 
1756
         unknown => undef,
 
1757
      },
 
1758
   },
 
1759
   {  name   => 'SELECT FROM JOIN WHERE GROUP BY ORDER BY LIMIT',
 
1760
      query  => '/* nonsensical but covers all the basic clauses */
 
1761
         SELECT t1.col1 a, t1.col2 as b
 
1762
         FROM tbl1 t1
 
1763
            LEFT JOIN tbl2 AS t2 ON t1.id = t2.id
 
1764
         WHERE
 
1765
            t2.col IS NOT NULL
 
1766
            AND t2.name = "bob"
 
1767
         GROUP BY a, b
 
1768
         ORDER BY t2.name ASC
 
1769
         LIMIT 100, 10
 
1770
      ',
 
1771
      struct => {
 
1772
         type    => 'select',
 
1773
         clauses => { 
 
1774
            columns  => 't1.col1 a, t1.col2 as b ',
 
1775
            from     => 'tbl1 t1 LEFT JOIN tbl2 AS t2 ON t1.id = t2.id ',
 
1776
            where    => 't2.col IS NOT NULL AND t2.name = "bob" ',
 
1777
            group_by => 'a, b ',
 
1778
            order_by => 't2.name ASC ',
 
1779
            limit    => '100, 10',
 
1780
         },
 
1781
         columns => [ { col => 'col1', tbl => 't1', alias => 'a' },
 
1782
                      { col => 'col2', tbl => 't1', alias => 'b',
 
1783
                        explicit_alias => 1 } ],
 
1784
         from    => [
 
1785
            {
 
1786
               tbl   => 'tbl1',
 
1787
               alias => 't1',
 
1788
            },
 
1789
            {
 
1790
               tbl   => 'tbl2',
 
1791
               alias => 't2',
 
1792
               explicit_alias => 1,
 
1793
               join  => {
 
1794
                  to        => 'tbl1',
 
1795
                  type      => 'left',
 
1796
                  condition => 'on',
 
1797
                  where      => [
 
1798
                     {
 
1799
                        predicate => undef,
 
1800
                        left_arg  => 't1.id',
 
1801
                        operator  => '=',
 
1802
                        right_arg => 't2.id',
 
1803
                     },
 
1804
                  ],
 
1805
                  ansi      => 1,
 
1806
               },
 
1807
            },
 
1808
         ],
 
1809
         where    => [
 
1810
            {
 
1811
               predicate => undef,
 
1812
               left_arg  => 't2.col',
 
1813
               operator  => 'is not',
 
1814
               right_arg => 'null',
 
1815
            },
 
1816
            {
 
1817
               predicate => 'and',
 
1818
               left_arg  => 't2.name',
 
1819
               operator  => '=',
 
1820
               right_arg => '"bob"',
 
1821
            },
 
1822
         ],
 
1823
         group_by => [
 
1824
            { column => 'a' },
 
1825
            { column => 'b' },
 
1826
         ],
 
1827
         order_by => [{table=>'t2', column=>'name', sort=>'ASC'}],
 
1828
         limit    => {
 
1829
            row_count => 10,
 
1830
            offset    => 100,
 
1831
         },
 
1832
         unknown => undef,
 
1833
      },
 
1834
   },
 
1835
   {  name   => 'SELECT FROM JOIN ON() JOIN USING() WHERE',
 
1836
      query  => 'SELECT t1.col1 a, t1.col2 as b
 
1837
 
 
1838
         FROM tbl1 t1
 
1839
 
 
1840
            JOIN tbl2 AS t2 ON(t1.id = t2.id)
 
1841
 
 
1842
            JOIN tbl3 t3 USING(id) 
 
1843
 
 
1844
         WHERE
 
1845
            t2.col IS NOT NULL',
 
1846
      struct => {
 
1847
         type    => 'select',
 
1848
         clauses => { 
 
1849
            columns  => 't1.col1 a, t1.col2 as b ',
 
1850
            from     => 'tbl1 t1 JOIN tbl2 AS t2 on (t1.id = t2.id) JOIN tbl3 t3 using (id) ',
 
1851
            where    => 't2.col IS NOT NULL',
 
1852
         },
 
1853
         columns => [ { col => 'col1', tbl => 't1', alias => 'a' },
 
1854
                      { col => 'col2', tbl => 't1', alias => 'b',
 
1855
                        explicit_alias => 1 } ],
 
1856
         from    => [
 
1857
            {
 
1858
               tbl   => 'tbl1',
 
1859
               alias => 't1',
 
1860
            },
 
1861
            {
 
1862
               tbl   => 'tbl2',
 
1863
               alias => 't2',
 
1864
               explicit_alias => 1,
 
1865
               join  => {
 
1866
                  to        => 'tbl1',
 
1867
                  type      => 'inner',
 
1868
                  condition => 'on',
 
1869
                  where      => [
 
1870
                     {
 
1871
                        predicate => undef,
 
1872
                        left_arg  => 't1.id',
 
1873
                        operator  => '=',
 
1874
                        right_arg => 't2.id',
 
1875
                     },
 
1876
                  ],
 
1877
                  ansi      => 1,
 
1878
               },
 
1879
            },
 
1880
            {
 
1881
               tbl   => 'tbl3',
 
1882
               alias => 't3',
 
1883
               join  => {
 
1884
                  to        => 'tbl2',
 
1885
                  type      => 'inner',
 
1886
                  condition => 'using',
 
1887
                  columns   => ['id'],
 
1888
                  ansi      => 1,
 
1889
               },
 
1890
            },
 
1891
         ],
 
1892
         where    => [
 
1893
            {
 
1894
               predicate => undef,
 
1895
               left_arg  => 't2.col',
 
1896
               operator  => 'is not',
 
1897
               right_arg => 'null',
 
1898
            },
 
1899
         ],
 
1900
         unknown => undef,
 
1901
      },
 
1902
   },
 
1903
   {  name   => 'SELECT keywords',
 
1904
      query  => 'SELECT all high_priority SQL_CALC_FOUND_ROWS NOW() LOCK IN SHARE MODE',
 
1905
      struct => {
 
1906
         type     => 'select',
 
1907
         clauses  => { 
 
1908
            columns => 'NOW()',
 
1909
         },
 
1910
         columns  => [ { col => 'NOW()' } ],
 
1911
         keywords => {
 
1912
            all                 => 1,
 
1913
            high_priority       => 1,
 
1914
            sql_calc_found_rows => 1,
 
1915
            lock_in_share_mode  => 1,
 
1916
         },
 
1917
         unknown  => undef,
 
1918
      },
 
1919
   },
 
1920
   { name   => 'SELECT * FROM WHERE',
 
1921
     query  => 'SELECT * FROM tbl WHERE ip="127.0.0.1"',
 
1922
     struct => {
 
1923
         type     => 'select',
 
1924
         clauses  => { 
 
1925
            columns => '* ',
 
1926
            from    => 'tbl ',
 
1927
            where   => 'ip="127.0.0.1"',
 
1928
         },
 
1929
         columns  => [ { col => '*' } ],
 
1930
         from     => [ { tbl => 'tbl' } ],
 
1931
         where    => [
 
1932
            {
 
1933
               predicate => undef,
 
1934
               left_arg  => 'ip',
 
1935
               operator  => '=',
 
1936
               right_arg => '"127.0.0.1"',
 
1937
            },
 
1938
         ],
 
1939
         unknown  => undef,
 
1940
      },
 
1941
   },
 
1942
   { name    => 'SELECT with simple subquery',
 
1943
     query   => 'select * from t where id in(select col from t2)',
 
1944
     struct  => {
 
1945
         type    => 'select',
 
1946
         clauses => { 
 
1947
            columns => '* ',
 
1948
            from    => 't ',
 
1949
            where   => 'id in(__SQ0__)',
 
1950
         },
 
1951
         columns    => [ { col => '*' } ],
 
1952
         from       => [ { tbl => 't' } ],
 
1953
         where      => [
 
1954
            {
 
1955
               predicate => undef,
 
1956
               left_arg  => 'id',
 
1957
               operator  => 'in',
 
1958
               right_arg => '(__SQ0__)',
 
1959
            },
 
1960
         ],
 
1961
         unknown    => undef,
 
1962
         subqueries => [
 
1963
            {
 
1964
               query   => 'select col from t2',
 
1965
               context => 'list',
 
1966
               type    => 'select',
 
1967
               clauses => { 
 
1968
                  columns => 'col ',
 
1969
                  from    => 't2',
 
1970
               },
 
1971
               columns    => [ { col => 'col' } ],
 
1972
               from       => [ { tbl => 't2' } ],
 
1973
               unknown    => undef,
 
1974
            },
 
1975
         ],
 
1976
      },
 
1977
   },
 
1978
   { name    => 'Complex SELECT, multiple JOIN and subqueries',
 
1979
     query   => 'select now(), (select foo from bar where id=1)
 
1980
                 from t1, t2 join (select * from sqt1) as t3 using (`select`)
 
1981
                 join t4 on t4.id=t3.id 
 
1982
                 where c1 > any(select col2 as z from sqt2 zz
 
1983
                    where sqtc<(select max(col) from l where col<100))
 
1984
                 and s in ("select", "tricky") or s <> "select"
 
1985
                 group by 1 limit 10',
 
1986
      struct => {
 
1987
         type       => 'select',
 
1988
         clauses    => { 
 
1989
            columns  => 'now(), __SQ3__ ',
 
1990
            from     => 't1, t2 join __SQ2__ as t3 using (`select`) join t4 on t4.id=t3.id ',
 
1991
            where    => 'c1 > any(__SQ1__) and s in ("select", "tricky") or s <> "select" ',
 
1992
            group_by => '1 ',
 
1993
            limit    => '10',
 
1994
         },
 
1995
         columns    => [ { col => 'now()' }, { col => '__SQ3__' } ],
 
1996
         from       => [
 
1997
            {
 
1998
               tbl => 't1',
 
1999
            },
 
2000
            {
 
2001
               tbl  => 't2',
 
2002
               join => {
 
2003
                  to   => 't1',
 
2004
                  ansi => 0,
 
2005
                  type => 'inner',
 
2006
               },
 
2007
            },
 
2008
            {
 
2009
               tbl => '__SQ2__',
 
2010
               alias => 't3',
 
2011
               explicit_alias => 1,
 
2012
               join  => {
 
2013
                  to   => 't2',
 
2014
                  ansi => 1,
 
2015
                  type => 'inner',
 
2016
                  columns    => ['`select`'],
 
2017
                  condition  => 'using',
 
2018
               },
 
2019
            },
 
2020
            {
 
2021
               tbl => 't4',
 
2022
               join => {
 
2023
                  to   => '__SQ2__',
 
2024
                  ansi => 1,
 
2025
                  type => 'inner',
 
2026
                  where      => [
 
2027
                     {
 
2028
                        predicate => undef,
 
2029
                        left_arg  => 't4.id',
 
2030
                        operator  => '=',
 
2031
                        right_arg => 't3.id',
 
2032
                     },
 
2033
                  ],
 
2034
                  condition  => 'on',
 
2035
               },
 
2036
            },
 
2037
         ],
 
2038
         where      => [
 
2039
            {
 
2040
               predicate => undef,
 
2041
               left_arg  => 'c1',
 
2042
               operator  => '>',
 
2043
               right_arg => 'any(__SQ1__)',
 
2044
            },
 
2045
            {
 
2046
               predicate => 'and',
 
2047
               left_arg  => 's',
 
2048
               operator  => 'in',
 
2049
               right_arg => '("select", "tricky")',
 
2050
            },
 
2051
            {
 
2052
               predicate => 'or',
 
2053
               left_arg  => 's',
 
2054
               operator  => '<>',
 
2055
               right_arg => '"select"',
 
2056
            },
 
2057
         ],
 
2058
         limit      => { row_count => 10 },
 
2059
         group_by   => [ { position => '1' } ],
 
2060
         unknown    => undef,
 
2061
         subqueries => [
 
2062
            {
 
2063
               clauses => {
 
2064
                  columns => 'max(col) ',
 
2065
                  from    => 'l ',
 
2066
                  where   => 'col<100'
 
2067
               },
 
2068
               columns => [ { col => 'max(col)' } ],
 
2069
               context => 'scalar',
 
2070
               from    => [ { tbl => 'l' } ],
 
2071
               nested  => 1,
 
2072
               query   => 'select max(col) from l where col<100',
 
2073
               type    => 'select',
 
2074
               unknown => undef,
 
2075
               where   => [
 
2076
                  {
 
2077
                     predicate => undef,
 
2078
                     left_arg  => 'col',
 
2079
                     operator  => '<',
 
2080
                     right_arg => '100',
 
2081
                  },
 
2082
               ],
 
2083
            },
 
2084
            {
 
2085
               clauses  => {
 
2086
                  columns => 'col2 as z ',
 
2087
                  from    => 'sqt2 zz ',
 
2088
                  where   => 'sqtc<__SQ0__'
 
2089
               },
 
2090
               columns => [
 
2091
                  { alias => 'z', explicit_alias => 1, col => 'col2' }
 
2092
               ],
 
2093
               context  => 'list',
 
2094
               from     => [ { alias => 'zz', tbl => 'sqt2' } ],
 
2095
               query    => 'select col2 as z from sqt2 zz where sqtc<__SQ0__',
 
2096
               type     => 'select',
 
2097
               unknown  => undef,
 
2098
               where    => [
 
2099
                  {
 
2100
                     predicate => undef,
 
2101
                     left_arg  => 'sqtc',
 
2102
                     operator  => '<',
 
2103
                     right_arg => '__SQ0__',
 
2104
                  },
 
2105
               ],
 
2106
            },
 
2107
            {
 
2108
               clauses  => {
 
2109
                  columns => '* ',
 
2110
                  from    => 'sqt1'
 
2111
               },
 
2112
               columns  => [ { col => '*' } ],
 
2113
               context  => 'identifier',
 
2114
               from     => [ { tbl => 'sqt1' } ],
 
2115
               query    => 'select * from sqt1',
 
2116
               type     => 'select',
 
2117
               unknown  => undef
 
2118
            },
 
2119
            {
 
2120
               clauses  => {
 
2121
               columns  => 'foo ',
 
2122
                  from  => 'bar ',
 
2123
                  where => 'id=1'
 
2124
               },
 
2125
               columns  => [ { col => 'foo' } ],
 
2126
               context  => 'identifier',
 
2127
               from     => [ { tbl => 'bar' } ],
 
2128
               query    => 'select foo from bar where id=1',
 
2129
               type     => 'select',
 
2130
               unknown  => undef,
 
2131
               where    => [
 
2132
                  {
 
2133
                     predicate => undef,
 
2134
                     left_arg  => 'id',
 
2135
                     operator  => '=',
 
2136
                     right_arg => '1',
 
2137
                  },
 
2138
               ],
 
2139
            },
 
2140
         ],
 
2141
      },
 
2142
   },
 
2143
   {  name   => 'Table joined twice',
 
2144
      query  => "SELECT *
 
2145
                 FROM   `w_chapter`
 
2146
                 INNER JOIN `w_series` AS `w_chapter__series`
 
2147
                 ON `w_chapter`.`series_id` = `w_chapter__series`.`id`,
 
2148
                 `w_series`,
 
2149
                 `auth_user`
 
2150
                 WHERE `w_chapter`.`status` = 1",
 
2151
      struct => {
 
2152
         type    => 'select',
 
2153
         clauses => { 
 
2154
            columns => "* ",
 
2155
            from    => "`w_chapter` INNER JOIN `w_series` AS `w_chapter__series` ON `w_chapter`.`series_id` = `w_chapter__series`.`id`, `w_series`, `auth_user` ",
 
2156
            where   => "`w_chapter`.`status` = 1",
 
2157
         },
 
2158
         columns => [{col => '*'}],
 
2159
         from    => [
 
2160
          {
 
2161
            tbl => 'w_chapter'
 
2162
          },
 
2163
          {
 
2164
            alias => 'w_chapter__series',
 
2165
            explicit_alias => 1,
 
2166
            join => {
 
2167
              ansi => 1,
 
2168
              condition => 'on',
 
2169
               where      => [
 
2170
                  {
 
2171
                     predicate => undef,
 
2172
                     left_arg  => '`w_chapter`.`series_id`',
 
2173
                     operator  => '=',
 
2174
                     right_arg => '`w_chapter__series`.`id`',
 
2175
                  },
 
2176
               ],
 
2177
              to => 'w_chapter',
 
2178
              type => 'inner'
 
2179
            },
 
2180
            tbl => 'w_series'
 
2181
          },
 
2182
          {
 
2183
            join => {
 
2184
              ansi => 0,
 
2185
              to => 'w_series',
 
2186
              type => 'inner'
 
2187
            },
 
2188
            tbl => 'w_series'
 
2189
          },
 
2190
          {
 
2191
            join => {
 
2192
              ansi => 0,
 
2193
              to => 'w_series',
 
2194
              type => 'inner'
 
2195
            },
 
2196
            tbl => 'auth_user'
 
2197
          }
 
2198
         ],
 
2199
         where   => [
 
2200
            {
 
2201
               predicate => undef,
 
2202
               left_arg  => '`w_chapter`.`status`',
 
2203
               operator  => '=',
 
2204
               right_arg => '1',
 
2205
            },
 
2206
         ],
 
2207
         unknown => undef,
 
2208
      },
 
2209
   },
 
2210
 
 
2211
   # ########################################################################
 
2212
   # UPDATE
 
2213
   # ########################################################################
 
2214
   {  name   => 'UPDATE SET',
 
2215
      query  => 'UPDATE tbl SET col=1',
 
2216
      struct => {
 
2217
         type    => 'update',
 
2218
         clauses => { 
 
2219
            tables => 'tbl ',
 
2220
            set    => 'col=1',
 
2221
         },
 
2222
         tables  => [ { tbl => 'tbl', } ],
 
2223
         set     => [ { col =>'col', value => '1' } ],
 
2224
         unknown => undef,
 
2225
      },
 
2226
   },
 
2227
   {  name   => 'UPDATE SET WHERE ORDER BY LIMIT',
 
2228
      query  => 'UPDATE tbl AS t SET foo=NULL WHERE foo IS NOT NULL ORDER BY id LIMIT 10',
 
2229
      struct => {
 
2230
         type    => 'update',
 
2231
         clauses => { 
 
2232
            tables   => 'tbl AS t ',
 
2233
            set      => 'foo=NULL ',
 
2234
            where    => 'foo IS NOT NULL ',
 
2235
            order_by => 'id ',
 
2236
            limit    => '10',
 
2237
         },
 
2238
         tables   => [ { tbl => 'tbl', alias => 't', explicit_alias => 1, } ],
 
2239
         set      => [ { col => 'foo', value => 'NULL' } ],
 
2240
         where    => [
 
2241
            {
 
2242
               predicate => undef,
 
2243
               left_arg  => 'foo',
 
2244
               operator  => 'is not',
 
2245
               right_arg => 'null',
 
2246
            },
 
2247
         ],
 
2248
         order_by => [{column=>'id'}],
 
2249
         limit    => { row_count => 10 },
 
2250
         unknown => undef,
 
2251
      },
 
2252
   },
 
2253
 
 
2254
   # ########################################################################
 
2255
   # EXPLAIN EXTENDED fully-qualified queries.
 
2256
   # ########################################################################
 
2257
   {  name   => 'EXPLAIN EXTENDED SELECT',
 
2258
      query  => 'select `sakila`.`city`.`country_id` AS `country_id` from `sakila`.`city` where (`sakila`.`city`.`country_id` = 1)',
 
2259
      struct => {
 
2260
         type    => 'select',
 
2261
         clauses => { 
 
2262
            columns => '`sakila`.`city`.`country_id` AS `country_id` ',
 
2263
            from    => '`sakila`.`city` ',
 
2264
            where   => '(`sakila`.`city`.`country_id` = 1)',
 
2265
         },
 
2266
         columns => [
 
2267
            { db    => 'sakila',
 
2268
              tbl   => 'city',
 
2269
              col   => 'country_id',
 
2270
              alias => 'country_id',
 
2271
              explicit_alias => 1,
 
2272
            },
 
2273
         ],
 
2274
         from    => [ { db=>'sakila', tbl=>'city' } ],
 
2275
         where   => [ {
 
2276
            predicate => undef,
 
2277
            left_arg  => '`sakila`.`city`.`country_id`',
 
2278
            operator  => '=',
 
2279
            right_arg => '1',
 
2280
         } ],
 
2281
         unknown => undef,
 
2282
      },
 
2283
   },
 
2284
);
 
2285
 
 
2286
foreach my $test ( @cases ) {
 
2287
   my $struct = $sp->parse($test->{query});
 
2288
   is_deeply(
 
2289
      $struct,
 
2290
      $test->{struct},
 
2291
      $test->{name},
 
2292
   ) or print Dumper($struct);
 
2293
   die if $test->{stop};
 
2294
}
 
2295
 
 
2296
# ############################################################################
 
2297
# Use Schema to achieve full awesomeness.
 
2298
# ############################################################################
 
2299
use OptionParser;
 
2300
use DSNParser;
 
2301
use Quoter;
 
2302
use TableParser;
 
2303
use FileIterator;
 
2304
use Schema;
 
2305
use SchemaIterator;
 
2306
 
 
2307
my $o  = new OptionParser(description => 'SchemaIterator');
 
2308
$o->get_specs("$trunk/bin/pt-table-checksum");
 
2309
 
 
2310
my $q          = new Quoter;
 
2311
my $tp         = new TableParser(Quoter => $q);
 
2312
my $fi         = new FileIterator();
 
2313
my $file_itr   = $fi->get_file_itr("$trunk/t/lib/samples/mysqldump-no-data/dump001.txt");
 
2314
my $schema     = new Schema();
 
2315
my $schema_itr = new SchemaIterator(
 
2316
   file_itr     => $file_itr,
 
2317
   OptionParser => $o,
 
2318
   Quoter       => $q,
 
2319
   TableParser  => $tp,
 
2320
   keep_ddl     => 1,
 
2321
   Schema       => $schema,
 
2322
);
 
2323
# Init schema.
 
2324
1 while ($schema_itr->next_schema_object());
 
2325
 
 
2326
# Notice how c3 and b aren't qualified.
 
2327
is_deeply(
 
2328
   $sp->parse("select c3 from b where 'foo'=c3"),
 
2329
   {
 
2330
      type     => 'select',
 
2331
      clauses  => {
 
2332
         columns => 'c3 ',
 
2333
         from    => 'b ',
 
2334
         where   => '\'foo\'=c3',
 
2335
      },
 
2336
      columns  => [ { col => 'c3' } ],
 
2337
      from     => [ { tbl => 'b' } ],
 
2338
      where    => [ {
 
2339
         left_arg  => "'foo'",
 
2340
         operator  => '=',
 
2341
         right_arg => 'c3',
 
2342
         predicate => undef,
 
2343
      } ],
 
2344
      unknown  => undef,
 
2345
   },
 
2346
   "Query struct without Schema"
 
2347
);
 
2348
 
 
2349
# Now they're qualified.
 
2350
$sp->set_Schema($schema);
 
2351
is_deeply(
 
2352
   $sp->parse("select c3 from b where 'foo'=c3"),
 
2353
   {
 
2354
      type     => 'select',
 
2355
      clauses  => {
 
2356
         columns => 'c3 ',
 
2357
         from    => 'b ',
 
2358
         where   => '\'foo\'=c3',
 
2359
      },
 
2360
      columns  => [ { db => 'test', tbl => 'b', col => 'c3' } ],
 
2361
      from     => [ { db => 'test', tbl => 'b' } ],
 
2362
      where    => [ {
 
2363
         left_arg  => "'foo'",
 
2364
         operator  => '=',
 
2365
         right_arg => 'c3',
 
2366
         predicate => undef,
 
2367
      } ],
 
2368
      unknown  => undef,
 
2369
   },
 
2370
   "Query struct with Schema"
 
2371
);
 
2372
 
 
2373
# #############################################################################
 
2374
# Done.
 
2375
# #############################################################################
 
2376
exit;