Fix the binop leg of the parser to correctly consider only a single LHS node
[dbsrgits/SQL-Abstract.git] / t / 11parser.t
1 use strict;
2 use warnings;
3
4 use Test::More;
5 use Test::Warn;
6 use SQL::Abstract::Tree;
7
8 my $sqlat = SQL::Abstract::Tree->new;
9 is_deeply($sqlat->parse("SELECT a, b.*, * FROM foo WHERE foo.a =1 and foo.b LIKE 'station'"), [
10   [
11     "SELECT",
12     [
13       [
14         "-LIST",
15         [
16           [
17             "-LITERAL",
18             [
19               "a"
20             ]
21           ],
22           [
23             "-LITERAL",
24             [
25               "b.*"
26             ]
27           ],
28           [
29             "-LITERAL",
30             [
31               "*"
32             ]
33           ]
34         ]
35       ]
36     ]
37   ],
38   [
39     "FROM",
40     [
41       [
42         "-LITERAL",
43         [
44           "foo"
45         ]
46       ]
47     ]
48   ],
49   [
50     "WHERE",
51     [
52       [
53         "AND",
54         [
55           [
56             "=",
57             [
58               [
59                 "-LITERAL",
60                 [
61                   "foo.a"
62                 ]
63               ],
64               [
65                 "-LITERAL",
66                 [
67                   1
68                 ]
69               ]
70             ]
71           ],
72           [
73             "LIKE",
74             [
75               [
76                 "-LITERAL",
77                 [
78                   "foo.b"
79                 ]
80               ],
81               [
82                 "-LITERAL",
83                 [
84                   "'station'"
85                 ]
86               ]
87             ]
88           ]
89         ]
90       ]
91     ]
92   ]
93 ], 'simple statement parsed correctly');
94
95 is_deeply($sqlat->parse( "SELECT * FROM (SELECT * FROM foobar) foo WHERE foo.a =1 and foo.b LIKE 'station'"), [
96   [
97     "SELECT",
98     [
99       [
100         "-LITERAL",
101         [
102           "*"
103         ]
104       ]
105     ]
106   ],
107   [
108     "FROM",
109     [
110       [
111         "-MISC",
112         [
113           [
114             "-PAREN",
115             [
116               [
117                 "SELECT",
118                 [
119                   [
120                     "-LITERAL",
121                     [
122                       "*"
123                     ]
124                   ]
125                 ]
126               ],
127               [
128                 "FROM",
129                 [
130                   [
131                     "-LITERAL",
132                     [
133                       "foobar"
134                     ]
135                   ]
136                 ]
137               ]
138             ]
139           ],
140           [
141             "-LITERAL",
142             [
143               "foo"
144             ]
145           ]
146         ]
147       ]
148     ]
149   ],
150   [
151     "WHERE",
152     [
153       [
154         "AND",
155         [
156           [
157             "=",
158             [
159               [
160                 "-LITERAL",
161                 [
162                   "foo.a"
163                 ]
164               ],
165               [
166                 "-LITERAL",
167                 [
168                   1
169                 ]
170               ]
171             ]
172           ],
173           [
174             "LIKE",
175             [
176               [
177                 "-LITERAL",
178                 [
179                   "foo.b"
180                 ]
181               ],
182               [
183                 "-LITERAL",
184                 [
185                   "'station'"
186                 ]
187               ]
188             ]
189           ]
190         ]
191       ]
192     ]
193   ]
194 ], 'subquery statement parsed correctly');
195
196 is_deeply($sqlat->parse( "SELECT [screen].[id], [screen].[name], [screen].[section_id], [screen].[xtype] FROM [users_roles] [me] JOIN [roles] [role] ON [role].[id] = [me].[role_id] JOIN [roles_permissions] [role_permissions] ON [role_permissions].[role_id] = [role].[id] JOIN [permissions] [permission] ON [permission].[id] = [role_permissions].[permission_id] JOIN [permissionscreens] [permission_screens] ON [permission_screens].[permission_id] = [permission].[id] JOIN [screens] [screen] ON [screen].[id] = [permission_screens].[screen_id] WHERE ( [me].[user_id] = ? ) GROUP BY [screen].[id], [screen].[name], [screen].[section_id], [screen].[xtype]"), [
197   [
198     "SELECT",
199     [
200       [
201         "-LIST",
202         [
203           [
204             "-LITERAL",
205             [
206               "[screen].[id]"
207             ]
208           ],
209           [
210             "-LITERAL",
211             [
212               "[screen].[name]"
213             ]
214           ],
215           [
216             "-LITERAL",
217             [
218               "[screen].[section_id]"
219             ]
220           ],
221           [
222             "-LITERAL",
223             [
224               "[screen].[xtype]"
225             ]
226           ]
227         ]
228       ]
229     ]
230   ],
231   [
232     "FROM",
233     [
234       [
235         "-MISC",
236         [
237           [
238             "-LITERAL",
239             [
240               "[users_roles]"
241             ]
242           ],
243           [
244             "-LITERAL",
245             [
246               "[me]"
247             ]
248           ]
249         ]
250       ]
251     ]
252   ],
253   [
254     "JOIN",
255     [
256       [
257         "-MISC",
258         [
259           [
260             "-LITERAL",
261             [
262               "[roles]"
263             ]
264           ],
265           [
266             "-LITERAL",
267             [
268               "[role]"
269             ]
270           ]
271         ]
272       ]
273     ]
274   ],
275   [
276     "ON",
277     [
278       [
279         "=",
280         [
281           [
282             "-LITERAL",
283             [
284               "[role].[id]"
285             ]
286           ],
287           [
288             "-LITERAL",
289             [
290               "[me].[role_id]"
291             ]
292           ]
293         ]
294       ]
295     ]
296   ],
297   [
298     "JOIN",
299     [
300       [
301         "-MISC",
302         [
303           [
304             "-LITERAL",
305             [
306               "[roles_permissions]"
307             ]
308           ],
309           [
310             "-LITERAL",
311             [
312               "[role_permissions]"
313             ]
314           ]
315         ]
316       ]
317     ]
318   ],
319   [
320     "ON",
321     [
322       [
323         "=",
324         [
325           [
326             "-LITERAL",
327             [
328               "[role_permissions].[role_id]"
329             ]
330           ],
331           [
332             "-LITERAL",
333             [
334               "[role].[id]"
335             ]
336           ]
337         ]
338       ]
339     ]
340   ],
341   [
342     "JOIN",
343     [
344       [
345         "-MISC",
346         [
347           [
348             "-LITERAL",
349             [
350               "[permissions]"
351             ]
352           ],
353           [
354             "-LITERAL",
355             [
356               "[permission]"
357             ]
358           ]
359         ]
360       ]
361     ]
362   ],
363   [
364     "ON",
365     [
366       [
367         "=",
368         [
369           [
370             "-LITERAL",
371             [
372               "[permission].[id]"
373             ]
374           ],
375           [
376             "-LITERAL",
377             [
378               "[role_permissions].[permission_id]"
379             ]
380           ]
381         ]
382       ]
383     ]
384   ],
385   [
386     "JOIN",
387     [
388       [
389         "-MISC",
390         [
391           [
392             "-LITERAL",
393             [
394               "[permissionscreens]"
395             ]
396           ],
397           [
398             "-LITERAL",
399             [
400               "[permission_screens]"
401             ]
402           ]
403         ]
404       ]
405     ]
406   ],
407   [
408     "ON",
409     [
410       [
411         "=",
412         [
413           [
414             "-LITERAL",
415             [
416               "[permission_screens].[permission_id]"
417             ]
418           ],
419           [
420             "-LITERAL",
421             [
422               "[permission].[id]"
423             ]
424           ]
425         ]
426       ]
427     ]
428   ],
429   [
430     "JOIN",
431     [
432       [
433         "-MISC",
434         [
435           [
436             "-LITERAL",
437             [
438               "[screens]"
439             ]
440           ],
441           [
442             "-LITERAL",
443             [
444               "[screen]"
445             ]
446           ]
447         ]
448       ]
449     ]
450   ],
451   [
452     "ON",
453     [
454       [
455         "=",
456         [
457           [
458             "-LITERAL",
459             [
460               "[screen].[id]"
461             ]
462           ],
463           [
464             "-LITERAL",
465             [
466               "[permission_screens].[screen_id]"
467             ]
468           ]
469         ]
470       ]
471     ]
472   ],
473   [
474     "WHERE",
475     [
476       [
477         "-PAREN",
478         [
479           [
480             "=",
481             [
482               [
483                 "-LITERAL",
484                 [
485                   "[me].[user_id]"
486                 ]
487               ],
488               [
489                 "-PLACEHOLDER",
490                 [
491                   "?"
492                 ]
493               ]
494             ]
495           ]
496         ]
497       ]
498     ]
499   ],
500   [
501     "GROUP BY",
502     [
503       [
504         "-LIST",
505         [
506           [
507             "-LITERAL",
508             [
509               "[screen].[id]"
510             ]
511           ],
512           [
513             "-LITERAL",
514             [
515               "[screen].[name]"
516             ]
517           ],
518           [
519             "-LITERAL",
520             [
521               "[screen].[section_id]"
522             ]
523           ],
524           [
525             "-LITERAL",
526             [
527               "[screen].[xtype]"
528             ]
529           ]
530         ]
531       ]
532     ]
533   ]
534 ], 'real life statement 1 parsed correctly');
535
536 is_deeply($sqlat->parse("CASE WHEN FOO() > BAR()"), [
537   [
538     "-MISC",
539     [
540       [
541         "-LITERAL",
542         [
543           "CASE"
544         ]
545       ],
546       [
547         "-LITERAL",
548         [
549           "WHEN"
550         ]
551       ]
552     ]
553   ],
554   [
555     ">",
556     [
557       [
558         "FOO",
559         [
560           [
561             "-PAREN",
562             []
563           ]
564         ]
565       ],
566       [
567         "BAR",
568         [
569           [
570             "-PAREN",
571             []
572           ]
573         ]
574       ]
575     ]
576   ]
577 ]);
578
579
580 is_deeply($sqlat->parse("SELECT x, y FROM foo WHERE x IN (?, ?, ?, ?)"), [
581   [
582     "SELECT",
583     [
584       [
585         "-LIST",
586         [
587           [
588             "-LITERAL",
589             [
590               "x"
591             ]
592           ],
593           [
594             "-LITERAL",
595             [
596               "y"
597             ]
598           ]
599         ]
600       ]
601     ]
602   ],
603   [
604     "FROM",
605     [
606       [
607         "-LITERAL",
608         [
609           "foo"
610         ]
611       ]
612     ]
613   ],
614   [
615     "WHERE",
616     [
617       [
618         "IN",
619         [
620           [
621             "-LITERAL",
622             [
623               "x"
624             ]
625           ],
626           [
627             "-PAREN",
628             [
629               [
630                 "-LIST",
631                 [
632                   [
633                     "-PLACEHOLDER",
634                     [
635                       "?"
636                     ]
637                   ],
638                   [
639                     "-PLACEHOLDER",
640                     [
641                       "?"
642                     ]
643                   ],
644                   [
645                     "-PLACEHOLDER",
646                     [
647                       "?"
648                     ]
649                   ],
650                   [
651                     "-PLACEHOLDER",
652                     [
653                       "?"
654                     ]
655                   ]
656                 ]
657               ]
658             ]
659           ]
660         ]
661       ]
662     ]
663   ]
664 ], 'Lists parsed correctly');
665
666 is_deeply($sqlat->parse('SELECT foo FROM bar ORDER BY x + ? DESC, oomph, y - ? DESC, unf, baz.g / ? ASC, buzz * 0 DESC, foo LIKE ? DESC, ickk ASC'), [
667   [
668     "SELECT",
669     [
670       [
671         "-LITERAL",
672         [
673           "foo"
674         ]
675       ]
676     ]
677   ],
678   [
679     "FROM",
680     [
681       [
682         "-LITERAL",
683         [
684           "bar"
685         ]
686       ]
687     ]
688   ],
689   [
690     "ORDER BY",
691     [
692       [
693         "-LIST",
694         [
695           [
696             "-DESC",
697             [
698               [
699                 "-MISC",
700                 [
701                   [
702                     "-LITERAL",
703                     [
704                       "x"
705                     ]
706                   ],
707                   [
708                     "-LITERAL",
709                     [
710                       "+"
711                     ]
712                   ],
713                   [
714                     "-PLACEHOLDER",
715                     [
716                       "?"
717                     ]
718                   ]
719                 ]
720               ],
721             ]
722           ],
723           [
724             "-LITERAL",
725             [
726               "oomph"
727             ]
728           ],
729           [
730             "-DESC",
731             [
732               [
733                 "-MISC",
734                 [
735                   [
736                     "-LITERAL",
737                     [
738                       "y"
739                     ]
740                   ],
741                   [
742                     "-LITERAL",
743                     [
744                       "-"
745                     ]
746                   ],
747                   [
748                     "-PLACEHOLDER",
749                     [
750                       "?"
751                     ]
752                   ],
753                 ]
754               ],
755             ]
756           ],
757           [
758             "-LITERAL",
759             [
760               "unf"
761             ]
762           ],
763           [
764             "-ASC",
765             [
766               [
767                 "-MISC",
768                 [
769                   [
770                     "-LITERAL",
771                     [
772                       "baz.g"
773                     ]
774                   ],
775                   [
776                     "-LITERAL",
777                     [
778                       "/"
779                     ]
780                   ],
781                   [
782                     "-PLACEHOLDER",
783                     [
784                       "?"
785                     ]
786                   ],
787                 ]
788               ],
789             ]
790           ],
791           [
792             "-DESC",
793             [
794               [
795                 "-MISC",
796                 [
797                   [
798                     "-LITERAL",
799                     [
800                       "buzz"
801                     ]
802                   ],
803                   [
804                     "-LITERAL",
805                     [
806                       "*"
807                     ]
808                   ],
809                   [
810                     "-LITERAL",
811                     [
812                       0
813                     ]
814                   ]
815                 ]
816               ]
817             ]
818           ],
819           [
820             "-DESC",
821             [
822               [
823                 "LIKE",
824                 [
825                   [
826                     "-LITERAL",
827                     [
828                       "foo"
829                     ]
830                   ],
831                   [
832                     "-PLACEHOLDER",
833                     [
834                       "?"
835                     ]
836                   ],
837                 ],
838               ],
839             ]
840           ],
841           [
842             "-ASC",
843             [
844               [
845                 "-LITERAL",
846                 [
847                   "ickk"
848                 ]
849               ]
850             ]
851           ]
852         ]
853       ]
854     ]
855   ]
856 ], 'Crazy ORDER BY parsed correctly');
857
858 is_deeply( $sqlat->parse("META SELECT * * FROM (SELECT *, FROM foobar baz buzz) foo bar WHERE NOT NOT NOT EXISTS (SELECT 'cr,ap') AND foo.a = ? STUFF moar(stuff) and not (foo.b LIKE 'station') and x = y and z in ((1, 2)) and a = b and GROUP BY , ORDER BY x x1 x2 y asc, max(y) desc x z desc"), [
859   [
860     "-LITERAL",
861     [
862       "META"
863     ]
864   ],
865   [
866     "SELECT",
867     [
868       [
869         "-MISC",
870         [
871           [
872             "-LITERAL",
873             [
874               "*"
875             ]
876           ],
877           [
878             "-LITERAL",
879             [
880               "*"
881             ]
882           ]
883         ]
884       ]
885     ]
886   ],
887   [
888     "FROM",
889     [
890       [
891         "-MISC",
892         [
893           [
894             "-PAREN",
895             [
896               [
897                 "SELECT",
898                 [
899                   [
900                     "-LIST",
901                     [
902                       [
903                         "-LITERAL",
904                         [
905                           "*"
906                         ]
907                       ],
908                       []
909                     ]
910                   ]
911                 ]
912               ],
913               [
914                 "FROM",
915                 [
916                   [
917                     "-MISC",
918                     [
919                       [
920                         "-LITERAL",
921                         [
922                           "foobar"
923                         ]
924                       ],
925                       [
926                         "-LITERAL",
927                         [
928                           "baz"
929                         ]
930                       ],
931                       [
932                         "-LITERAL",
933                         [
934                           "buzz"
935                         ]
936                       ]
937                     ]
938                   ]
939                 ]
940               ]
941             ]
942           ],
943           [
944             "-LITERAL",
945             [
946               "foo"
947             ]
948           ],
949           [
950             "-LITERAL",
951             [
952               "bar"
953             ]
954           ]
955         ]
956       ]
957     ]
958   ],
959   [
960     "WHERE",
961     [
962       [
963         "AND",
964         [
965           [
966             "NOT",
967             []
968           ],
969           [
970             "NOT",
971             []
972           ],
973           [
974             "NOT EXISTS",
975             [
976               [
977                 "-PAREN",
978                 [
979                   [
980                     "SELECT",
981                     [
982                       [
983                         "-LIST",
984                         [
985                           [
986                             "-LITERAL",
987                             [
988                               "'cr"
989                             ]
990                           ],
991                           [
992                             "-LITERAL",
993                             [
994                               "ap'"
995                             ]
996                           ]
997                         ]
998                       ]
999                     ]
1000                   ]
1001                 ]
1002               ]
1003             ]
1004           ],
1005           [
1006             "-MISC",
1007             [
1008               [
1009                 "=",
1010                 [
1011                   [
1012                     "-LITERAL",
1013                     [
1014                       "foo.a"
1015                     ]
1016                   ],
1017                   [
1018                     "-PLACEHOLDER",
1019                     [
1020                       "?"
1021                     ]
1022                   ],
1023                 ],
1024               ],
1025               [
1026                 "-LITERAL",
1027                 [
1028                   "STUFF"
1029                 ]
1030               ],
1031             ],
1032           ],
1033           [
1034             'moar',
1035             [
1036               [
1037                 '-PAREN',
1038                 [
1039                   [
1040                     '-LITERAL',
1041                     [
1042                       'stuff'
1043                     ]
1044                  ]
1045                 ]
1046               ]
1047             ]
1048           ],
1049           [
1050             "NOT",
1051             [
1052               [
1053                 "-PAREN",
1054                 [
1055                   [
1056                     "LIKE",
1057                     [
1058                       [
1059                         "-LITERAL",
1060                         [
1061                           "foo.b"
1062                         ]
1063                       ],
1064                       [
1065                         "-LITERAL",
1066                         [
1067                           "'station'"
1068                         ]
1069                       ]
1070                     ]
1071                   ]
1072                 ]
1073               ]
1074             ]
1075           ],
1076           [
1077             "=",
1078             [
1079               [
1080                 "-LITERAL",
1081                 [
1082                   "x"
1083                 ]
1084               ],
1085               [
1086                 "-LITERAL",
1087                 [
1088                   "y"
1089                 ]
1090               ]
1091             ]
1092           ],
1093           [
1094             'IN',
1095             [
1096               [
1097                 '-LITERAL',
1098                 [
1099                   'z',
1100                 ],
1101               ],
1102               [
1103                 '-PAREN',
1104                 [
1105                   [
1106                     '-PAREN',
1107                     [
1108                       [
1109                         '-LIST',
1110                         [
1111                           [
1112                             '-LITERAL',
1113                             [
1114                               '1'
1115                             ]
1116                           ],
1117                           [
1118                             '-LITERAL',
1119                             [
1120                               '2'
1121                             ]
1122                           ],
1123                         ],
1124                       ],
1125                     ],
1126                   ],
1127                 ],
1128               ],
1129             ],
1130           ],
1131           [
1132             "=",
1133             [
1134               [
1135                 "-LITERAL",
1136                 [
1137                   "a"
1138                 ]
1139               ],
1140               [
1141                 "-LITERAL",
1142                 [
1143                   "b"
1144                 ]
1145               ]
1146             ]
1147           ]
1148         ]
1149       ]
1150     ]
1151   ],
1152   [
1153     "GROUP BY",
1154     [
1155       [
1156         "-LIST",
1157         [
1158           [],
1159           []
1160         ]
1161       ]
1162     ]
1163   ],
1164   [
1165     "ORDER BY",
1166     [
1167       [
1168         "-LIST",
1169         [
1170           [
1171             "-ASC",
1172             [
1173               [
1174                 "-MISC",
1175                 [
1176                   [
1177                     "-LITERAL",
1178                     [
1179                       "x"
1180                     ]
1181                   ],
1182                   [
1183                     "-LITERAL",
1184                     [
1185                       "x1"
1186                     ]
1187                   ],
1188                   [
1189                     "-LITERAL",
1190                     [
1191                       "x2"
1192                     ]
1193                   ],
1194                   [
1195                     "-LITERAL",
1196                     [
1197                       "y"
1198                     ]
1199                   ]
1200                 ]
1201               ],
1202             ],
1203           ],
1204           [
1205                 "-DESC",
1206                 [
1207                   [
1208                     "-MISC",
1209                     [
1210                       [
1211                         "-DESC",
1212                         [
1213                           [
1214                             "max",
1215                             [
1216                               [
1217                                 "-PAREN",
1218                                 [
1219                                   [
1220                                     "-LITERAL",
1221                                     [
1222                                       "y"
1223                                     ]
1224                                   ]
1225                                 ]
1226                               ]
1227                             ],
1228                           ]
1229                         ]
1230                       ],
1231                       [
1232                         "-LITERAL",
1233                         [
1234                           "x"
1235                         ]
1236                       ],
1237                   [
1238                     "-LITERAL",
1239                     [
1240                       "z"
1241                     ]
1242                   ]
1243                 ]
1244               ]
1245             ]
1246           ]
1247         ]
1248       ]
1249     ]
1250   ]
1251 ], 'Deliberately malformed SQL parsed "correctly"');
1252
1253
1254 # test for recursion warnings on huge selectors
1255 my @lst = ('AA' .. 'zz');
1256 #@lst = ('AAA' .. 'zzz'); # if you really want to wait a while
1257 warnings_are {
1258   my $sql = sprintf 'SELECT %s FROM foo', join (', ',  (map { qq|( "$_" )| } @lst), (map { qq|"$_"| } @lst), (map { qq|"$_", ( "$_" )| } @lst) );
1259   my $tree = $sqlat->parse($sql);
1260
1261   is_deeply( $tree, [
1262     [
1263       "SELECT",
1264       [
1265         [
1266           "-LIST",
1267           [
1268             (map { [ -PAREN => [ [ -LITERAL => [ qq|"$_"| ] ] ] ] } @lst),
1269             (map { [ -LITERAL => [ qq|"$_"| ] ] } @lst),
1270             (map { [ -LITERAL => [ qq|"$_"| ] ], [ -PAREN => [ [ -LITERAL => [ qq|"$_"| ] ] ] ] } @lst),
1271           ]
1272         ]
1273       ]
1274     ],
1275     [
1276       "FROM",
1277       [
1278         [
1279           "-LITERAL",
1280           [
1281             "foo"
1282           ]
1283         ]
1284       ]
1285     ]
1286   ], 'long list parsed correctly');
1287
1288   is( $sqlat->unparse($tree), $sql, 'roundtrip ok');
1289 } [], 'no recursion warnings on insane SQL';
1290
1291 done_testing;