File: QueryRewriter.t

package info (click to toggle)
percona-toolkit 3.2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 68,916 kB
  • sloc: perl: 241,287; sql: 22,868; sh: 19,746; javascript: 6,799; makefile: 353; awk: 38; python: 30; sed: 1
file content (1484 lines) | stat: -rw-r--r-- 44,205 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
#!/usr/bin/perl


BEGIN {
   die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
      unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
   unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
};

use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Test::More;

use QueryRewriter;
use QueryParser;
use PerconaTest;

my $qp = new QueryParser();
my $qr = new QueryRewriter(QueryParser=>$qp);

# #############################################################################
# strip_comments()
# #############################################################################

is(
   $qr->strip_comments("select \n--bar\n foo"),
   "select \n\n foo",
   'Removes one-line comments',
);

is(
   $qr->strip_comments("select foo--bar\nfoo"),
   "select foo\nfoo",
   'Removes one-line comments without running them together',
);

is(
   $qr->strip_comments("select foo -- bar"),
   "select foo ",
   'Removes one-line comments at end of line',
);

is(
   $qr->strip_comments("select /*\nhello!*/ 1"),
   'select  1',
   'Stripped star comment',
);

is(
   $qr->strip_comments('select /*!40101 hello*/ 1'),
   'select /*!40101 hello*/ 1',
   'Left version star comment',
);

# #############################################################################
# fingerprint()
# #############################################################################

is(
   $qr->fingerprint(
      q{UPDATE groups_search SET  charter = '   -------3\'\' XXXXXXXXX.\n    \n    -----------------------------------------------------', show_in_list = 'Y' WHERE group_id='aaaaaaaa'}),
   'update groups_search set charter = ?, show_in_list = ? where group_id=?',
   'complex comments',
);

is(
   $qr->fingerprint("SELECT /*!40001 SQL_NO_CACHE */ * FROM `film`"),
   "mysqldump",
   'Fingerprints all mysqldump SELECTs together',
);

is(
   $qr->fingerprint("CALL foo(1, 2, 3)"),
   "call foo",
   'Fingerprints stored procedure calls specially',
);


is(
   $qr->fingerprint('administrator command: Init DB'),
   'administrator command: Init DB',
   'Fingerprints admin commands as themselves',
);

is(
   $qr->fingerprint(
      q{REPLACE /*foo.bar:3/3*/ INTO checksum.checksum (db, tbl, }
      .q{chunk, boundaries, this_cnt, this_crc) SELECT 'foo', 'bar', }
      .q{2 AS chunk_num, '`id` >= 2166633', COUNT(*) AS cnt, }
      .q{LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `created_by`, }
      .q{`created_date`, `updated_by`, `updated_date`, `ppc_provider`, }
      .q{`account_name`, `provider_account_id`, `campaign_name`, }
      .q{`provider_campaign_id`, `adgroup_name`, `provider_adgroup_id`, }
      .q{`provider_keyword_id`, `provider_ad_id`, `foo`, `reason`, }
      .q{`foo_bar_bazz_id`, `foo_bar_baz`, CONCAT(ISNULL(`created_by`), }
      .q{ISNULL(`created_date`), ISNULL(`updated_by`), ISNULL(`updated_date`), }
      .q{ISNULL(`ppc_provider`), ISNULL(`account_name`), }
      .q{ISNULL(`provider_account_id`), ISNULL(`campaign_name`), }
      .q{ISNULL(`provider_campaign_id`), ISNULL(`adgroup_name`), }
      .q{ISNULL(`provider_adgroup_id`), ISNULL(`provider_keyword_id`), }
      .q{ISNULL(`provider_ad_id`), ISNULL(`foo`), ISNULL(`reason`), }
      .q{ISNULL(`foo_base_foo_id`), ISNULL(`fooe_foo_id`)))) AS UNSIGNED)), 10, }
      .q{16)) AS crc FROM `foo`.`bar` USE INDEX (`PRIMARY`) WHERE }
      .q{(`id` >= 2166633); }),
   'percona-toolkit',
   'Fingerprints mk-table-checksum queries together',
);

is(
   $qr->fingerprint("use `foo`"),
   "use ?",
   'Removes identifier from USE',
);

is(
   $qr->fingerprint("select \n--bar\n foo"),
   "select foo",
   'Removes one-line comments in fingerprints',
);


is(
   $qr->fingerprint("select foo--bar\nfoo"),
   "select foo foo",
   'Removes one-line comments in fingerprint without mushing things together',
);

is(
   $qr->fingerprint("select foo -- bar\n"),
   "select foo ",
   'Removes one-line EOL comments in fingerprints',
);

# This one is too expensive!
#is(
#   $qr->fingerprint(
#      "select a,b ,c , d from tbl where a=5 or a = 5 or a=5 or a =5"),
#   "select a, b, c, d from tbl where a=? or a=? or a=? or a=?",
#   "Normalizes commas and equals",
#);

is(
   $qr->fingerprint("select null, 5.001, 5001. from foo"),
   "select ?, ?, ? from foo",
   "Handles bug from perlmonks thread 728718",
);

is(
   $qr->fingerprint("select 'hello', '\nhello\n', \"hello\", '\\'' from foo"),
   "select ?, ?, ?, ? from foo",
   "Handles quoted strings",
);


is(
   $qr->fingerprint("select 'hello'\n"),
   "select ?",
   "Handles trailing newline",
);

# This is a known deficiency, fixes seem to be expensive though.
is(
   $qr->fingerprint("select '\\\\' from foo"),
   "select '\\ from foo",
   "Does not handle all quoted strings",
);

is(
   $qr->fingerprint("select   foo"),
   "select foo",
   'Collapses whitespace',
);

is(
   $qr->fingerprint('SELECT * from foo where a = 5'),
   'select * from foo where a = ?',
   'Lowercases, replaces integer',
);

is(
   $qr->fingerprint('select 0e0, +6e-30, -6.00 from foo where a = 5.5 or b=0.5 or c=.5'),
   'select ?, ?, ? from foo where a = ? or b=? or c=?',
   'Floats',
);

is(
   $qr->fingerprint("select 0x0, x'123', 0b1010, b'10101' from foo"),
   'select ?, ?, ?, ? from foo',
   'Hex/bit',
);

is(
   $qr->fingerprint(" select  * from\nfoo where a = 5"),
   'select * from foo where a = ?',
   'Collapses whitespace',
);

is(
   $qr->fingerprint("select * from foo where a in (5) and b in (5, 8,9 ,9 , 10)"),
   'select * from foo where a in(?+) and b in(?+)',
   'IN lists',
);

is(
   $qr->fingerprint("select foo_1 from foo_2_3"),
   'select foo_? from foo_?_?',
   'Numeric table names',
);

# 123f00 => ?oo because f "looks like it could be a number".
is(
   $qr->fingerprint("select 123foo from 123foo", { prefixes => 1 }),
   'select ?oo from ?oo',
   'Numeric table name prefixes',
);

is(
   $qr->fingerprint("select 123_foo from 123_foo", { prefixes => 1 }),
   'select ?_foo from ?_foo',
   'Numeric table name prefixes with underscores',
);

is(
   $qr->fingerprint("insert into abtemp.coxed select foo.bar from foo"),
   'insert into abtemp.coxed select foo.bar from foo',
   'A string that needs no changes',
);

is(
   $qr->fingerprint('insert into foo(a, b, c) values(2, 4, 5)'),
   'insert into foo(a, b, c) values(?+)',
   'VALUES lists',
);


is(
   $qr->fingerprint('insert into foo(a, b, c) values(2, 4, 5) , (2,4,5)'),
   'insert into foo(a, b, c) values(?+)',
   'VALUES lists with multiple ()',
);

is(
   $qr->fingerprint('insert into foo(a, b, c) value(2, 4, 5)'),
   'insert into foo(a, b, c) value(?+)',
   'VALUES lists with VALUE()',
);

is(
   $qr->fingerprint('select * from foo limit 5'),
   'select * from foo limit ?',
   'limit alone',
);

is(
   $qr->fingerprint('select * from foo limit 5, 10'),
   'select * from foo limit ?',
   'limit with comma-offset',
);

is(
   $qr->fingerprint('select * from foo limit 5 offset 10'),
   'select * from foo limit ?',
   'limit with offset',
);

is(
   $qr->fingerprint('select 1 union select 2 union select 4'),
   'select ? /*repeat union*/',
   'union fingerprints together',
);

is(
   $qr->fingerprint('select 1 union all select 2 union all select 4'),
   'select ? /*repeat union all*/',
   'union all fingerprints together',
);

is(
   $qr->fingerprint(
      q{select * from (select 1 union all select 2 union all select 4) as x }
      . q{join (select 2 union select 2 union select 3) as y}),
   q{select * from (select ? /*repeat union all*/) as x }
      . q{join (select ? /*repeat union*/) as y},
   'union all fingerprints together',
);

# Issue 322: mk-query-digest segfault before report
is(
   $qr->fingerprint( load_file('t/lib/samples/huge_replace_into_values.txt') ),
   q{replace into `film_actor` values(?+)},
   'huge replace into values() (issue 322)',
);
is(
   $qr->fingerprint( load_file('t/lib/samples/huge_insert_ignore_into_values.txt') ),
   q{insert ignore into `film_actor` values(?+)},
   'huge insert ignore into values() (issue 322)',
);
is(
   $qr->fingerprint( load_file('t/lib/samples/huge_explicit_cols_values.txt') ),
   q{insert into foo (a,b,c,d,e,f,g,h) values(?+)},
   'huge insert with explicit columns before values() (issue 322)',
);

# Those ^ aren't huge enough.  This one is 1.2M large. 
my $zcat = `uname` =~ m/Darwin/ ? 'gzcat' : 'zcat';
my $huge_insert = `$zcat $trunk/t/lib/samples/slowlogs/slow039.txt.gz | tail -n 1`;
is(
   $qr->fingerprint($huge_insert),
   q{insert into the_universe values(?+)},
   'truly huge insert 1/2 (issue 687)'
);
$huge_insert = `$zcat $trunk/t/lib/samples/slowlogs/slow040.txt.gz | tail -n 2`;
is(
   $qr->fingerprint($huge_insert),
   q{insert into the_universe values(?+)},
   'truly huge insert 2/2 (issue 687)'
);

# Issue 1030: Fingerprint can remove ORDER BY ASC
is(
   $qr->fingerprint(
      "select c from t where i=1 order by c asc",
   ),
   "select c from t where i=? order by c",
   "Remove ASC from ORDER BY"
);
is(
   $qr->fingerprint(
      "select * from t where i=1 order by a, b ASC, d DESC, e asc",
   ),
   "select * from t where i=? order by a, b, d desc, e",
   "Remove only ASC from ORDER BY"
);
is(
   $qr->fingerprint(
      "select * from t where i=1      order            by 
      a,  b          ASC, d    DESC,    
                             
                             e asc",
   ),
   "select * from t where i=? order by a, b, d desc, e",
   "Remove ASC from spacey ORDER BY"
);

is(
   $qr->fingerprint("LOAD DATA INFILE '/tmp/foo.txt' INTO db.tbl"),
   "load data infile ? into db.tbl",
   "Fingerprint LOAD DATA INFILE"
);

# fingerprint MD5 checksums, 32 char hex strings.  This is a
# special feature used by pt-fingerprint.
$qr = new QueryRewriter(
   QueryParser     => $qp,
   match_md5_checksums => 1,
);

is(
   $qr->fingerprint(
      "SELECT * FROM db.fbc5e685a5d3d45aa1d0347fdb7c4d35_temp where id=1"
   ),
   "select * from db.?_temp where id=?",
   "Fingerprint db.MD5_tbl"
);

is(
   $qr->fingerprint(
      "SELECT * FROM db.temp_fbc5e685a5d3d45aa1d0347fdb7c4d35 where id=1"
   ),
   "select * from db.temp_? where id=?",
   "Fingerprint db.tbl_MD5"
);

$qr = new QueryRewriter(
   QueryParser     => $qp,
   match_md5_checksums => 1,
   match_embedded_numbers => 1,
);

is(
   $qr->fingerprint(
      "SELECT * FROM db.fbc5e685a5d3d45aa1d0347fdb7c4d35_temp where id=1"
   ),
   "select * from db.?_temp where id=?",
   "Fingerprint db.MD5_tbl (with match_embedded_numbers)"
);

is(
   $qr->fingerprint(
      "SELECT * FROM db.temp_fbc5e685a5d3d45aa1d0347fdb7c4d35 where id=1"
   ),
   "select * from db.temp_? where id=?",
   "Fingerprint db.tbl_MD5 (with match_embedded_numbers)"
);

$qr = new QueryRewriter(
   QueryParser => $qp,
   match_embedded_numbers => 1,
);

is(
   $qr->fingerprint(
      "SELECT * FROM prices.rt_5min where id=1"
   ),
   "select * from prices.rt_5min where id=?",
   "Fingerprint db.tbl<number>name (preserve number)"
);


is(
   $qr->fingerprint(
      "/* -- S++ SU ABORTABLE -- spd_user: rspadim */SELECT SQL_SMALL_RESULT SQL_CACHE DISTINCT centro_atividade FROM est_dia WHERE unidade_id=1001 AND item_id=67 AND item_id_red=573"
   ),
   "select sql_small_result sql_cache distinct centro_atividade from est_dia where unidade_id=? and item_id=? and item_id_red=?",
   "Fingerprint /* -- comment */ SELECT (bug 1174956)"
);


# issue 965553

is(
   $qr->fingerprint('SELECT * FROM tbl WHERE id=1 AND flag=true AND trueflag=FALSE'),
   'select * from tbl where id=? and flag=? and trueflag=?',
   'boolean values abstracted correctly',
);


# #############################################################################
# convert_to_select()
# #############################################################################

is($qr->convert_to_select(), undef, 'No query');

is(
   $qr->convert_to_select(
      'select * from tbl where id = 1'
   ),
   'select * from tbl where id = 1',
   'Does not convert select to select',
);

is(
   $qr->convert_to_select(q{INSERT INTO foo.bar (col1, col2, col3)
       VALUES ('unbalanced(', 'val2', 3)}),
   q{select * from  foo.bar  where col1='unbalanced(' and  }
   . q{col2= 'val2' and  col3= 3},
   'unbalanced paren inside a string in VALUES',
);

# convert REPLACE #############################################################

is(
   $qr->convert_to_select(
      'replace into foo select * from bar',
   ),
   'select * from bar',
   'convert REPLACE SELECT',
);

is(
   $qr->convert_to_select(
      'replace into foo select`faz` from bar',
   ),
   'select`faz` from bar',
   'convert REPLACE SELECT`col`',
);

is(
   $qr->convert_to_select(
      'replace into foo(a, b, c) values(1, 3, 5) on duplicate key update foo=bar',
   ),
   'select * from  foo where a=1 and  b= 3 and  c= 5',
   'convert REPLACE (cols) VALUES ON DUPE KEY',
);

is(
   $qr->convert_to_select(
      'replace into foo(a, b, c) values(now(), "3", 5)',
   ),
   'select * from  foo where a=now() and  b= "3" and  c= 5',
   'convert REPLACE (cols) VALUES (now())',
);

is(
   $qr->convert_to_select(
      'replace into foo(a, b, c) values(current_date - interval 1 day, "3", 5)',
   ),
   'select * from  foo where a=current_date - interval 1 day and  b= "3" and  c= 5',
   'convert REPLACE (cols) VALUES (complex expression)',
);

is(
   $qr->convert_to_select(q{
REPLACE DELAYED INTO
`db1`.`tbl2`(`col1`,col2)
VALUES ('617653','2007-09-11')}),
   qq{select * from \n`db1`.`tbl2` where `col1`='617653' and col2='2007-09-11'},
   'convert REPLACE DELAYED (cols) VALUES',
);

is(
   $qr->convert_to_select(
      'replace into tbl set col1="a val", col2=123, col3=null',
   ),
   'select * from  tbl where col1="a val" and  col2=123 and  col3=null ',
   'convert REPLACE SET'
);

# convert INSERT ##############################################################

is(
   $qr->convert_to_select(
      'insert into foo(a, b, c) values(1, 3, 5)',
   ),
   'select * from  foo where a=1 and  b= 3 and  c= 5',
   'convert INSERT (cols) VALUES',
);

is(
   $qr->convert_to_select(
      'insert into foo(a, b, c) value(1, 3, 5)',
   ),
   'select * from  foo where a=1 and  b= 3 and  c= 5',
   'convert INSERT (cols) VALUE',
);

# Issue 599: mk-slave-prefetch doesn't parse INSERT IGNORE
is(
   $qr->convert_to_select(
      'insert ignore into foo(a, b, c) values(1, 3, 5)',
   ),
   'select * from  foo where a=1 and  b= 3 and  c= 5',
   'convert INSERT IGNORE (cols) VALUES',
);

is(
   $qr->convert_to_select(
      'INSERT IGNORE INTO Foo (clm1, clm2) VALUE (1,2)',
   ),
   'select * from  Foo  where clm1=1 and  clm2=2',
   'convert INSERT IGNORE (cols) VALUE',
);

is(
   $qr->convert_to_select(
      'insert into foo select * from bar join baz using (bat)',
   ),
   'select * from bar join baz using (bat)',
   'convert INSERT SELECT',
);

# Issue 600: mk-slave-prefetch doesn't parse INSERT INTO Table SET c1 = v1,
# c2 = v2 ...
is(
   $qr->convert_to_select(
      "INSERT INTO Table SET c1 = 'v1', c2 = 'v2', c3 = 'v3'",
   ),
   "select * from  Table where c1 = 'v1' and  c2 = 'v2' and  c3 = 'v3' ",
   'convert INSERT SET char cols',
);

is(
   $qr->convert_to_select(
      "INSERT INTO db.tbl SET c1=NULL,c2=42,c3='some value with spaces'",
   ),
   "select * from  db.tbl where c1=NULL and c2=42 and c3='some value with spaces' ",
   'convert INSERT SET NULL col, int col, char col with space',
);

is(
   $qr->convert_to_select(
      'insert into foo (col1) values (1) on duplicate key update',
   ),
   'select * from  foo  where col1=1',
   'convert INSERT (cols) VALUES ON DUPE KEY UPDATE'
);

is(
   $qr->convert_to_select(
      'insert into foo (col1) value (1) on duplicate key update',
   ),
   'select * from  foo  where col1=1',
   'convert INSERT (cols) VALUE ON DUPE KEY UPDATE'
);

is(
   $qr->convert_to_select(
      "insert into tbl set col='foo', col2='some val' on duplicate key update",
   ),
   "select * from  tbl where col='foo' and  col2='some val' ",
   'convert INSERT SET ON DUPE KEY UPDATE',
);

is(
   $qr->convert_to_select(
      'insert into foo select * from bar where baz=bat on duplicate key update',
   ),
   'select * from bar where baz=bat',
   'convert INSERT SELECT ON DUPE KEY UPDATE',
);

# convert UPDATE ##############################################################

is(
   $qr->convert_to_select(
      'update foo set bar=baz where bat=fiz',
   ),
   'select  bar=baz from foo where  bat=fiz',
   'update set',
);

is(
   $qr->convert_to_select(
      'update foo inner join bar using(baz) set big=little',
   ),
   'select  big=little from foo inner join bar using(baz) ',
   'delete inner join',
);

is(
   $qr->convert_to_select(
      'update foo set bar=baz limit 50',
   ),
   'select  bar=baz  from foo  limit 50 ',
   'update with limit',
);

is(
   $qr->convert_to_select(
q{UPDATE foo.bar
SET    whereproblem= '3364', apple = 'fish'
WHERE  gizmo='5091'}
   ),
   q{select     whereproblem= '3364', apple = 'fish' from foo.bar where   gizmo='5091'},
   'unknown issue',
);

# Insanity...
is(
   $qr->convert_to_select('
update db2.tbl1 as p
   inner join (
      select p2.col1, p2.col2
      from db2.tbl1 as p2
         inner join db2.tbl3 as ba
            on p2.col1 = ba.tbl3
      where col4 = 0
      order by priority desc, col1, col2
      limit 10
   ) as chosen on chosen.col1 = p.col1
      and chosen.col2 = p.col2
   set p.col4 = 149945'),
   'select  p.col4 = 149945 from db2.tbl1 as p
   inner join (
      select p2.col1, p2.col2
      from db2.tbl1 as p2
         inner join db2.tbl3 as ba
            on p2.col1 = ba.tbl3
      where col4 = 0
      order by priority desc, col1, col2
      limit 10
   ) as chosen on chosen.col1 = p.col1
      and chosen.col2 = p.col2 ',
   'SELECT in the FROM clause',
);

is(
   $qr->convert_to_select("UPDATE tbl SET col='wherex'WHERE crazy=1"),
   "select  col='wherex' from tbl where  crazy=1",
   "update with SET col='wherex'WHERE"
);

is($qr->convert_to_select(
   q{UPDATE GARDEN_CLUPL PL, GARDENJOB GC, APLTRACT_GARDENPLANT ABU SET }
   . q{GC.MATCHING_POT = 5, GC.LAST_GARDENPOT = 5, GC.LAST_NAME=}
   . q{'Rotary', GC.LAST_BUCKET='Pail', GC.LAST_UPDATE='2008-11-27 04:00:59'WHERE}
   . q{ PL.APLTRACT_GARDENPLANT_ID = GC.APLTRACT_GARDENPLANT_ID AND PL.}
   . q{APLTRACT_GARDENPLANT_ID = ABU.ID AND GC.MATCHING_POT = 0 AND GC.PERFORM_DIG=1}
   . q{ AND ABU.DIG = 6 AND ( ((SOIL-COST) > -80.0}
   . q{ AND BUGS < 60.0 AND (SOIL-COST) < 200.0) AND POTS < 10.0 )}),
   "select  GC.MATCHING_POT = 5, GC.LAST_GARDENPOT = 5, GC.LAST_NAME='Rotary', GC.LAST_BUCKET='Pail', GC.LAST_UPDATE='2008-11-27 04:00:59' from GARDEN_CLUPL PL, GARDENJOB GC, APLTRACT_GARDENPLANT ABU where  PL.APLTRACT_GARDENPLANT_ID = GC.APLTRACT_GARDENPLANT_ID AND PL.APLTRACT_GARDENPLANT_ID = ABU.ID AND GC.MATCHING_POT = 0 AND GC.PERFORM_DIG=1 AND ABU.DIG = 6 AND ( ((SOIL-COST) > -80.0 AND BUGS < 60.0 AND (SOIL-COST) < 200.0) AND POTS < 10.0 )",
   'update with no space between quoted string and where (issue 168)'
);

is(
   $qr->convert_to_select("UPDATE LOW_PRIORITY db.tbl SET field='new' WHERE id=1"),
   "select  field='new' from db.tbl where  id=1",
   "update with LOW_PRIORITY"
);

is(
   $qr->convert_to_select("UPDATE ignore db.tbl SET field='new' WHERE id=1"),
   "select  field='new' from db.tbl where  id=1",
   "update with IGNORE"
);

# convert DELETE ##############################################################

is(
   $qr->convert_to_select(
      'delete from foo where bar = baz',
   ),
   'select * from  foo where bar = baz',
   'delete',
);

is(
   $qr->convert_to_select(q{delete foo.bar b from foo.bar b left join baz.bat c on a=b where nine>eight}),
   'select 1 from  foo.bar b left join baz.bat c on a=b where nine>eight',
   'Do not select * from a join',
);

is(
   $qr->convert_to_select("DELETE LOW_PRIORITY FROM tbl WHERE id=1"),
   "select * from  tbl WHERE id=1",
   "delete with LOW_PRIORITY"
);

is(
   $qr->convert_to_select("delete ignore from tbl WHERE id=1"),
   "select * from  tbl WHERE id=1",
   "delete with IGNORE"
);

is(
   $qr->convert_to_select("delete from file where id='ima-long-uuid-string'"),
   "select * from  file where id='ima-long-uuid-string'",
   "Peter's DELTE"
);

# do not convert subqueries ###################################################

is(
   $qr->convert_to_select("UPDATE mybbl_MBMessage SET groupId = (select groupId from Group_ where name = 'Guest')"),
   undef,
   'Do not convert subquery'
);

# #############################################################################
# wrap_in_derived()
# #############################################################################

is($qr->wrap_in_derived(), undef, 'Cannot wrap undef');

is(
   $qr->wrap_in_derived(
      'select * from foo',
   ),
   'select 1 from (select * from foo) as x limit 1',
   'wrap in derived table',
);

is(
   $qr->wrap_in_derived('set timestamp=134'),
   'set timestamp=134',
   'Do not wrap non-SELECT queries',
);

# #############################################################################
# convert_select_list()
# #############################################################################

is(
   $qr->convert_select_list('select * from tbl'),
   'select 1 from tbl',
   'Star to one',
);

is(
   $qr->convert_select_list('select a, b, c from tbl'),
   'select isnull(coalesce( a, b, c )) from tbl',
   'column list to isnull/coalesce'
);

# #############################################################################
# shorten()
# #############################################################################

is(
   $qr->shorten("insert into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
   "insert into t(a,b,c) values(a,b,c) /*... omitted ...*/",
   "shorten simple insert",
);

is(
   $qr->shorten("insert low_priority into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
   "insert low_priority into t(a,b,c) values(a,b,c) /*... omitted ...*/",
   "shorten low_priority simple insert",
);

is(
   $qr->shorten("insert delayed into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
   "insert delayed into t(a,b,c) values(a,b,c) /*... omitted ...*/",
   "shorten delayed simple insert",
);

is(
   $qr->shorten("insert high_priority into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
   "insert high_priority into t(a,b,c) values(a,b,c) /*... omitted ...*/",
   "shorten high_priority simple insert",
);

is(
   $qr->shorten("insert ignore into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
   "insert ignore into t(a,b,c) values(a,b,c) /*... omitted ...*/",
   "shorten ignore simple insert",
);

is(
   $qr->shorten("insert high_priority ignore into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
   "insert high_priority ignore into t(a,b,c) values(a,b,c) /*... omitted ...*/",
   "shorten high_priority ignore simple insert",
);

is(
   $qr->shorten("replace low_priority into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
   "replace low_priority into t(a,b,c) values(a,b,c) /*... omitted ...*/",
   "shorten replace low_priority",
);

is(
   $qr->shorten("replace delayed into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i)"),
   "replace delayed into t(a,b,c) values(a,b,c) /*... omitted ...*/",
   "shorten replace delayed",
);

is(
   $qr->shorten("insert into t(a,b,c) values(a,b,c),(d,e,f),(g,h,i) on duplicate key update a = b"),
   "insert into t(a,b,c) values(a,b,c) /*... omitted ...*/on duplicate key update a = b",
   "shorten insert ... odku",
);

is(
   $qr->shorten(
      "select * from a where b in(" . join(',', 1..60) . ") and "
         . "a in(" . join(',', 1..5000) . ")", 1),
   "select * from a where b in(" . join(',', 1..20) . "/*... omitted 40 items ...*/)"
      . " and a in(" . join(',', 1..20) . "/*... omitted 4980 items ...*/)",
   "shorten two IN() lists of numbers",
);

is(
   $qr->shorten("select * from a", 1),
   "select * from a",
   "Does not shorten strings it does not match",
);

is(
   $qr->shorten("select * from a where b in(". join(',', 1..100) . ")", 1024),
   "select * from a where b in(". join(',', 1..100) . ")",
   "shorten IN() list numbers but not those that are already short enough",
);

is(
   $qr->shorten("select * from a where b in(" . join(',', 1..100) . "'a,b')", 1),
   "select * from a where b in(" . join(',', 1..20) . "/*... omitted 81 items ...*/)",
   "Test case to document that commas are expected to mess up omitted count",
);

is(
   $qr->shorten("select * from a where b in(1, 'a)b', " . join(',', 1..100) . ")", 1),
   "select * from a where b in(1, 'a)b', " . join(',', 1..100) . ")",
   "Test case to document that parens are expected to prevent shortening",
);

# #############################################################################
# distill()
# All tests below here are distill() tests.  There's a lot of them.
# #############################################################################

is(
   $qr->distill("SELECT /*!40001 SQL_NO_CACHE */ * FROM `film`"),
   "SELECT film",
   'Distills mysqldump SELECTs to selects',
);

is(
   $qr->distill("CALL foo(1, 2, 3)"),
   "CALL foo",
   'Distills stored procedure calls specially',
);

is(
   $qr->distill(
      q{REPLACE /*foo.bar:3/3*/ INTO checksum.checksum (db, tbl, }
      .q{chunk, boundaries, this_cnt, this_crc) SELECT 'foo', 'bar', }
      .q{2 AS chunk_num, '`id` >= 2166633', COUNT(*) AS cnt, }
      .q{LOWER(CONV(BIT_XOR(CAST(CRC32(CONCAT_WS('#', `id`, `created_by`, }
      .q{`created_date`, `updated_by`, `updated_date`, `ppc_provider`, }
      .q{`account_name`, `provider_account_id`, `campaign_name`, }
      .q{`provider_campaign_id`, `adgroup_name`, `provider_adgroup_id`, }
      .q{`provider_keyword_id`, `provider_ad_id`, `foo`, `reason`, }
      .q{`foo_bar_bazz_id`, `foo_bar_baz`, CONCAT(ISNULL(`created_by`), }
      .q{ISNULL(`created_date`), ISNULL(`updated_by`), ISNULL(`updated_date`), }
      .q{ISNULL(`ppc_provider`), ISNULL(`account_name`), }
      .q{ISNULL(`provider_account_id`), ISNULL(`campaign_name`), }
      .q{ISNULL(`provider_campaign_id`), ISNULL(`adgroup_name`), }
      .q{ISNULL(`provider_adgroup_id`), ISNULL(`provider_keyword_id`), }
      .q{ISNULL(`provider_ad_id`), ISNULL(`foo`), ISNULL(`reason`), }
      .q{ISNULL(`foo_base_foo_id`), ISNULL(`fooe_foo_id`)))) AS UNSIGNED)), 10, }
      .q{16)) AS crc FROM `foo`.`bar` USE INDEX (`PRIMARY`) WHERE }
      .q{(`id` >= 2166633); }),
   'REPLACE SELECT checksum.checksum foo.bar',
   'Distills mk-table-checksum query',
);

is(
   $qr->distill("use `foo`"),
   "USE",
   'distills USE',
);

is(
   $qr->distill(q{delete foo.bar b from foo.bar b left join baz.bat c on a=b where nine>eight}),
   'DELETE foo.bar baz.bat',
   'distills and then collapses same tables',
);

is(
   $qr->distill("select \n--bar\n foo"),
   "SELECT",
   'distills queries from DUAL',
);

is(
   $qr->distill("select null, 5.001, 5001. from foo"),
   "SELECT foo",
   "distills simple select",
);

is(
   $qr->distill("select 'hello', '\nhello\n', \"hello\", '\\'' from foo"),
   "SELECT foo",
   "distills with quoted strings",
);

is(
   $qr->distill("select foo_1 from foo_2_3"),
   'SELECT foo_?_?',
   'distills numeric table names',
);

is(
   $qr->distill("insert into abtemp.coxed select foo.bar from foo"),
   'INSERT SELECT abtemp.coxed foo',
   'distills insert/select',
);

is(
   $qr->distill('insert into foo(a, b, c) values(2, 4, 5)'),
   'INSERT foo',
   'distills value lists',
);

is(
   $qr->distill('select 1 union select 2 union select 4'),
   'SELECT UNION',
   'distill unions together',
);

is(
   $qr->distill(
      'delete from foo where bar = baz',
   ),
   'DELETE foo',
   'distills delete',
);

is(
   $qr->distill('set timestamp=134'),
   'SET',
   'distills set',
);

is(
   $qr->distill(
      'replace into foo(a, b, c) values(1, 3, 5) on duplicate key update foo=bar',
   ),
   'REPLACE UPDATE foo',
   'distills ODKU',
);

is($qr->distill(
   q{UPDATE GARDEN_CLUPL PL, GARDENJOB GC, APLTRACT_GARDENPLANT ABU SET }
   . q{GC.MATCHING_POT = 5, GC.LAST_GARDENPOT = 5, GC.LAST_NAME=}
   . q{'Rotary', GC.LAST_BUCKET='Pail', GC.LAST_UPDATE='2008-11-27 04:00:59'WHERE}
   . q{ PL.APLTRACT_GARDENPLANT_ID = GC.APLTRACT_GARDENPLANT_ID AND PL.}
   . q{APLTRACT_GARDENPLANT_ID = ABU.ID AND GC.MATCHING_POT = 0 AND GC.PERFORM_DIG=1}
   . q{ AND ABU.DIG = 6 AND ( ((SOIL-COST) > -80.0}
   . q{ AND BUGS < 60.0 AND (SOIL-COST) < 200.0) AND POTS < 10.0 )}),
   'UPDATE GARDEN_CLUPL GARDENJOB APLTRACT_GARDENPLANT',
   'distills where there is alias and comma-join',
);

is(
   $qr->distill(q{SELECT STRAIGHT_JOIN distinct foo, bar FROM A, B, C}),
   'SELECT A B C',
   'distill with STRAIGHT_JOIN',
);

is (
   $qr->distill(q{
REPLACE DELAYED INTO
`db1`.`tbl2`(`col1`,col2)
VALUES ('617653','2007-09-11')}),
   'REPLACE db?.tbl?',
   'distills replace-delayed',
);

is(
   $qr->distill(
      'update foo inner join bar using(baz) set big=little',
   ),
   'UPDATE foo bar',
   'distills update-multi',
);

is(
   $qr->distill('
update db2.tbl1 as p
   inner join (
      select p2.col1, p2.col2
      from db2.tbl1 as p2
         inner join db2.tbl3 as ba
            on p2.col1 = ba.tbl3
      where col4 = 0
      order by priority desc, col1, col2
      limit 10
   ) as chosen on chosen.col1 = p.col1
      and chosen.col2 = p.col2
   set p.col4 = 149945'),
   'UPDATE SELECT db?.tbl?',
   'distills complex subquery',
);

is(
   $qr->distill(
      'replace into checksum.checksum select `last_update`, `foo` from foo.foo'),
   'REPLACE SELECT checksum.checksum foo.foo',
   'distill with reserved words');

is($qr->distill('SHOW STATUS'), 'SHOW STATUS', 'distill SHOW STATUS');

is($qr->distill('commit'), 'COMMIT', 'distill COMMIT');

is($qr->distill('FLUSH TABLES WITH READ LOCK'), 'FLUSH', 'distill FLUSH');

is($qr->distill('BEGIN'), 'BEGIN', 'distill BEGIN');

is($qr->distill('start'), 'START', 'distill START');

is($qr->distill('ROLLBACK'), 'ROLLBACK', 'distill ROLLBACK');

is(
   $qr->distill(
      'insert into foo select * from bar join baz using (bat)',
   ),
   'INSERT SELECT foo bar baz',
   'distills insert select',
);

is(
   $qr->distill('create database foo'),
   'CREATE DATABASE foo',
   'distills create database'
);
is(
   $qr->distill('create table foo'),
   'CREATE TABLE foo',
   'distills create table'
);
is(
   $qr->distill('alter database foo'),
   'ALTER DATABASE foo',
   'distills alter database'
);
is(
   $qr->distill('alter table foo'),
   'ALTER TABLE foo',
   'distills alter table'
);
is(
   $qr->distill('drop database foo'),
   'DROP DATABASE foo',
   'distills drop database'
);
is(
   $qr->distill('drop table foo'),
   'DROP TABLE foo',
   'distills drop table'
);
is(
   $qr->distill('rename database foo'),
   'RENAME DATABASE foo',
   'distills rename database'
);
is(
   $qr->distill('rename table foo'),
   'RENAME TABLE foo',
   'distills rename table'
);
is(
   $qr->distill('truncate table foo'),
   'TRUNCATE TABLE foo',
   'distills truncate table'
);

# Test generic distillation for memcached, http, etc.
my $trf = sub {
   my ( $query ) = @_;
   $query =~ s/(\S+ \S+?)(?:[?;].+)/$1/;
   return $query;
};

is(
   $qr->distill('get percona.com/', generic => 1, trf => $trf),
   'GET percona.com/',
   'generic distill HTTP get'
);

is(
   $qr->distill('get percona.com/page.html?some=thing', generic => 1, trf => $trf),
   'GET percona.com/page.html',
   'generic distill HTTP get with args'
);

is(
   $qr->distill('put percona.com/contacts.html', generic => 1, trf => $trf),
   'PUT percona.com/contacts.html',
   'generic distill HTTP put'
);

is(
   $qr->distill(
      'update foo set bar=baz where bat=fiz',
   ),
   'UPDATE foo',
   'distills update',
);

# Issue 563: Lock tables is not distilled
is(
   $qr->distill('LOCK TABLES foo WRITE'),
   'LOCK foo',
   'distills lock tables'
);
is(
   $qr->distill('LOCK TABLES foo READ, bar WRITE'),
   'LOCK foo bar',
   'distills lock tables (2 tables)'
);
is(
   $qr->distill('UNLOCK TABLES'),
   'UNLOCK',
   'distills unlock tables'
);

#  Issue 712: Queries not handled by "distill"
is(
   $qr->distill('XA START 0x123'),
   'XA_START',
   'distills xa start'
);
is(
   $qr->distill('XA PREPARE 0x123'),
   'XA_PREPARE',
   'distills xa prepare'
);
is(
   $qr->distill('XA COMMIT 0x123'),
   'XA_COMMIT',
   'distills xa commit'
);
is(
   $qr->distill('XA END 0x123'),
   'XA_END',
   'distills xa end'
);

is(
   $qr->distill("/* mysql-connector-java-5.1-nightly-20090730 ( Revision: \${svn.Revision} ) */SHOW VARIABLES WHERE Variable_name ='language' OR Variable_name =
   'net_write_timeout' OR Variable_name = 'interactive_timeout' OR
   Variable_name = 'wait_timeout' OR Variable_name = 'character_set_client' OR
   Variable_name = 'character_set_connection' OR Variable_name =
   'character_set' OR Variable_name = 'character_set_server' OR Variable_name
   = 'tx_isolation' OR Variable_name = 'transaction_isolation' OR
   Variable_name = 'character_set_results' OR Variable_name = 'timezone' OR
   Variable_name = 'time_zone' OR Variable_name = 'system_time_zone' OR
   Variable_name = 'lower_case_table_names' OR Variable_name =
   'max_allowed_packet' OR Variable_name = 'net_buffer_length' OR
   Variable_name = 'sql_mode' OR Variable_name = 'query_cache_type' OR
   Variable_name = 'query_cache_size' OR Variable_name = 'init_connect'"),
   'SHOW VARIABLES',
   'distills /* comment */SHOW VARIABLES'
);

# This is a list of all the types of syntax for SHOW on
# http://dev.mysql.com/doc/refman/5.0/en/show.html
my %status_tests = (
   'SHOW BINARY LOGS'                           => 'SHOW BINARY LOGS',
   'SHOW BINLOG EVENTS in "log_name"'           => 'SHOW BINLOG EVENTS',
   'SHOW CHARACTER SET LIKE "pattern"'          => 'SHOW CHARACTER SET',
   'SHOW COLLATION WHERE "something"'           => 'SHOW COLLATION',
   'SHOW COLUMNS FROM tbl'                      => 'SHOW COLUMNS',
   'SHOW FULL COLUMNS FROM tbl'                 => 'SHOW COLUMNS',
   'SHOW COLUMNS FROM tbl in db'                => 'SHOW COLUMNS',
   'SHOW COLUMNS FROM tbl IN db LIKE "pattern"' => 'SHOW COLUMNS',
   'SHOW CREATE DATABASE db_name'               => 'SHOW CREATE DATABASE',
   'SHOW CREATE SCHEMA db_name'                 => 'SHOW CREATE DATABASE',
   'SHOW CREATE FUNCTION func'                  => 'SHOW CREATE FUNCTION',
   'SHOW CREATE PROCEDURE proc'                 => 'SHOW CREATE PROCEDURE',
   'SHOW CREATE TABLE tbl_name'                 => 'SHOW CREATE TABLE',
   'SHOW CREATE VIEW vw_name'                   => 'SHOW CREATE VIEW',
   'SHOW DATABASES'                             => 'SHOW DATABASES',
   'SHOW SCHEMAS'                               => 'SHOW DATABASES',
   'SHOW DATABASES LIKE "pattern"'              => 'SHOW DATABASES',
   'SHOW DATABASES WHERE foo=bar'               => 'SHOW DATABASES',
   'SHOW ENGINE ndb status'                     => 'SHOW NDB STATUS',
   'SHOW ENGINE innodb status'                  => 'SHOW INNODB STATUS',
   'SHOW ENGINES'                               => 'SHOW ENGINES',
   'SHOW STORAGE ENGINES'                       => 'SHOW ENGINES',
   'SHOW ERRORS'                                => 'SHOW ERRORS',
   'SHOW ERRORS limit 5'                        => 'SHOW ERRORS',
   'SHOW COUNT(*) ERRORS'                       => 'SHOW ERRORS',
   'SHOW FUNCTION CODE func'                    => 'SHOW FUNCTION CODE',
   'SHOW FUNCTION STATUS'                       => 'SHOW FUNCTION STATUS',
   'SHOW FUNCTION STATUS LIKE "pattern"'        => 'SHOW FUNCTION STATUS',
   'SHOW FUNCTION STATUS WHERE foo=bar'         => 'SHOW FUNCTION STATUS',
   'SHOW GRANTS'                                => 'SHOW GRANTS',
   'SHOW GRANTS FOR user@localhost'             => 'SHOW GRANTS',
   'SHOW INDEX'                                 => 'SHOW INDEX',
   'SHOW INDEXES'                               => 'SHOW INDEX',
   'SHOW KEYS'                                  => 'SHOW INDEX',
   'SHOW INDEX FROM tbl'                        => 'SHOW INDEX',
   'SHOW INDEX FROM tbl IN db'                  => 'SHOW INDEX',
   'SHOW INDEX IN tbl FROM db'                  => 'SHOW INDEX',
   'SHOW INNODB STATUS'                         => 'SHOW INNODB STATUS',
   'SHOW LOGS'                                  => 'SHOW LOGS',
   'SHOW MASTER STATUS'                         => 'SHOW MASTER STATUS',
   'SHOW MUTEX STATUS'                          => 'SHOW MUTEX STATUS',
   'SHOW OPEN TABLES'                           => 'SHOW OPEN TABLES',
   'SHOW OPEN TABLES FROM db'                   => 'SHOW OPEN TABLES',
   'SHOW OPEN TABLES IN db'                     => 'SHOW OPEN TABLES',
   'SHOW OPEN TABLES IN db LIKE "pattern"'      => 'SHOW OPEN TABLES',
   'SHOW OPEN TABLES IN db WHERE foo=bar'       => 'SHOW OPEN TABLES',
   'SHOW OPEN TABLES WHERE foo=bar'             => 'SHOW OPEN TABLES',
   'SHOW PRIVILEGES'                            => 'SHOW PRIVILEGES',
   'SHOW PROCEDURE CODE proc'                   => 'SHOW PROCEDURE CODE',
   'SHOW PROCEDURE STATUS'                      => 'SHOW PROCEDURE STATUS',
   'SHOW PROCEDURE STATUS LIKE "pattern"'       => 'SHOW PROCEDURE STATUS',
   'SHOW PROCEDURE STATUS WHERE foo=bar'        => 'SHOW PROCEDURE STATUS',
   'SHOW PROCESSLIST'                           => 'SHOW PROCESSLIST',
   'SHOW FULL PROCESSLIST'                      => 'SHOW PROCESSLIST',
   'SHOW PROFILE'                               => 'SHOW PROFILE',
   'SHOW PROFILES'                              => 'SHOW PROFILES',
   'SHOW PROFILES CPU FOR QUERY 1'              => 'SHOW PROFILES CPU',
   'SHOW SLAVE HOSTS'                           => 'SHOW SLAVE HOSTS',
   'SHOW SLAVE STATUS'                          => 'SHOW SLAVE STATUS',
   'SHOW STATUS'                                => 'SHOW STATUS',
   'SHOW GLOBAL STATUS'                         => 'SHOW GLOBAL STATUS',
   'SHOW SESSION STATUS'                        => 'SHOW STATUS',
   'SHOW STATUS LIKE "pattern"'                 => 'SHOW STATUS',
   'SHOW STATUS WHERE foo=bar'                  => 'SHOW STATUS',
   'SHOW TABLE STATUS'                          => 'SHOW TABLE STATUS',
   'SHOW TABLE STATUS FROM db_name'             => 'SHOW TABLE STATUS',
   'SHOW TABLE STATUS IN db_name'               => 'SHOW TABLE STATUS',
   'SHOW TABLE STATUS LIKE "pattern"'           => 'SHOW TABLE STATUS',
   'SHOW TABLE STATUS WHERE foo=bar'            => 'SHOW TABLE STATUS',
   'SHOW TABLES'                                => 'SHOW TABLES',
   'SHOW FULL TABLES'                           => 'SHOW TABLES',
   'SHOW TABLES FROM db'                        => 'SHOW TABLES',
   'SHOW TABLES IN db'                          => 'SHOW TABLES',
   'SHOW TABLES LIKE "pattern"'                 => 'SHOW TABLES',
   'SHOW TABLES FROM db LIKE "pattern"'         => 'SHOW TABLES',
   'SHOW TABLES WHERE foo=bar'                  => 'SHOW TABLES',
   'SHOW TRIGGERS'                              => 'SHOW TRIGGERS',
   'SHOW TRIGGERS IN db'                        => 'SHOW TRIGGERS',
   'SHOW TRIGGERS FROM db'                      => 'SHOW TRIGGERS',
   'SHOW TRIGGERS LIKE "pattern"'               => 'SHOW TRIGGERS',
   'SHOW TRIGGERS WHERE foo=bar'                => 'SHOW TRIGGERS',
   'SHOW VARIABLES'                             => 'SHOW VARIABLES',
   'SHOW GLOBAL VARIABLES'                      => 'SHOW GLOBAL VARIABLES',
   'SHOW SESSION VARIABLES'                     => 'SHOW VARIABLES',
   'SHOW VARIABLES LIKE "pattern"'              => 'SHOW VARIABLES',
   'SHOW VARIABLES WHERE foo=bar'               => 'SHOW VARIABLES',
   'SHOW WARNINGS'                              => 'SHOW WARNINGS',
   'SHOW WARNINGS LIMIT 5'                      => 'SHOW WARNINGS',
   'SHOW COUNT(*) WARNINGS'                     => 'SHOW WARNINGS',
   'SHOW COUNT ( *) WARNINGS'                   => 'SHOW WARNINGS',
);

foreach my $key ( keys %status_tests ) {
   is($qr->distill($key), $status_tests{$key}, "distills $key");
}

is(
   $qr->distill('SHOW SLAVE STATUS'),
   'SHOW SLAVE STATUS',
   'distills SHOW SLAVE STATUS'
);
is(
   $qr->distill('SHOW INNODB STATUS'),
   'SHOW INNODB STATUS',
   'distills SHOW INNODB STATUS'
);
is(
   $qr->distill('SHOW CREATE TABLE'),
   'SHOW CREATE TABLE',
   'distills SHOW CREATE TABLE'
);

my @show = qw(COLUMNS GRANTS INDEX STATUS TABLES TRIGGERS WARNINGS);
foreach my $show ( @show ) {
   is(
      $qr->distill("SHOW $show"),
      "SHOW $show",
      "distills SHOW $show"
   );
}

#  Issue 735: mk-query-digest doesn't distill query correctly
is( 
	$qr->distill('SHOW /*!50002 GLOBAL */ STATUS'),
	'SHOW GLOBAL STATUS',
	"distills SHOW /*!50002 GLOBAL */ STATUS"
);

is( 
	$qr->distill('SHOW /*!50002 ENGINE */ INNODB STATUS'),
	'SHOW INNODB STATUS',
	"distills SHOW INNODB STATUS"
);

is( 
	$qr->distill('SHOW MASTER LOGS'),
	'SHOW MASTER LOGS',
	"distills SHOW MASTER LOGS"
);

is( 
	$qr->distill('SHOW GLOBAL STATUS'),
	'SHOW GLOBAL STATUS',
	"distills SHOW GLOBAL STATUS"
);

is( 
	$qr->distill('SHOW GLOBAL VARIABLES'),
	'SHOW GLOBAL VARIABLES',
	"distills SHOW GLOBAL VARIABLES"
);

is( 
	$qr->distill('administrator command: Statistics'),
	'ADMIN STATISTICS',
	"distills ADMIN STATISTICS"
);

# Issue 781: mk-query-digest doesn't distill or extract tables properly
is( 
	$qr->distill("SELECT `id` FROM (`field`) WHERE `id` = '10000016228434112371782015185031'"),
	'SELECT field',
	'distills SELECT clm from (`tbl`)'
);

is(  
	$qr->distill("INSERT INTO (`jedi_forces`) (name, side, email) values ('Anakin Skywalker', 'jedi', 'anakin_skywalker_at_jedi.sw')"),
	'INSERT jedi_forces',
	'distills INSERT INTO (`tbl`)' 
);

is(  
	$qr->distill("UPDATE (`jedi_forces`) set side = 'dark' and name = 'Lord Vader' where name = 'Anakin Skywalker'"),
	'UPDATE jedi_forces',
	'distills UPDATE (`tbl`)'
);

is(
	$qr->distill("select c from (tbl1 JOIN tbl2 on (id)) where x=y"),
	'SELECT tbl?',
	'distills SELECT (t1 JOIN t2)'
);

is(
	$qr->distill("insert into (t1) value('a')"),
	'INSERT t?',
	'distills INSERT (tbl)'
);

# Something that will (should) never distill.
is(
	$qr->distill("-- how /*did*/ `THIS` #happen?"),
	'',
	'distills nonsense'
);

is(
	$qr->distill("peek tbl poke db"),
	'',
	'distills non-SQL'
);

# Issue 1176: mk-query-digest incorrectly distills queries with certain keywords

# I want to see first how this is handled.  It's correct because the query
# really does read from tables a and c; table b is just an alias.
is(
   $qr->distill("select c from (select * from a) as b where exists (select * from c where id is null)"),
   "SELECT a c",
   "distills SELECT with subquery in FROM and WHERE"
);

is(
	$qr->distill("select c from t where col='delete'"),
	'SELECT t',
   'distills SELECT with keyword as value (issue 1176)'
);

is(
   $qr->distill('SELECT c, replace(foo, bar) FROM t WHERE col <> "insert"'),
   'SELECT t',
   'distills SELECT with REPLACE function (issue 1176)'
);

# LOAD DATA
# https://bugs.launchpad.net/percona-toolkit/+bug/821692
# INSERT and REPLACE without INTO
# https://bugs.launchpad.net/percona-toolkit/+bug/984053
is(
   $qr->distill("LOAD DATA LOW_PRIORITY LOCAL INFILE 'file' INTO TABLE tbl"),
   "LOAD DATA tbl",
   "distill LOAD DATA (bug 821692)"
);

is(
   $qr->distill("LOAD DATA LOW_PRIORITY LOCAL INFILE 'file' INTO TABLE `tbl`"),
   "LOAD DATA tbl",
   "distill LOAD DATA (bug 821692)"
);

is(
   $qr->distill("insert ignore_bar (id) values (4029731)"),
   "INSERT ignore_bar",
   "distill INSERT without INTO (bug 984053)"
);

is(
   $qr->distill("replace ignore_bar (id) values (4029731)"),
   "REPLACE ignore_bar",
   "distill REPLACE without INTO (bug 984053)"
);

# IF EXISTS
# https://bugs.launchpad.net/percona-toolkit/+bug/821690
is(
   $qr->distill("DROP TABLE IF EXISTS foo"),
   "DROP TABLE foo",
   "distill DROP TABLE IF EXISTS foo (bug 821690)"
);

is(
   $qr->distill("CREATE TABLE IF NOT EXISTS foo"),
   "CREATE TABLE foo",
   "distill CREATE TABLE IF NOT EXISTS foo",
);


$qr = new QueryRewriter(
   QueryParser => $qp,
   match_embedded_numbers => 1,
);

is(
   $qr->fingerprint(
      "SELECT * FROM prices2.t1 where id=1"
   ),
   "select * from prices2.t1 where id=?",
   "Fingerprint db.tbl<number>name (preserve number)"
);

# #############################################################################
# Done.
# #############################################################################
done_testing;