Sort hash keys so that the SQL produced is stable
[dbsrgits/SQL-Abstract-2.0-ish.git] / t / compat / ast / 01.t
1 use strict;
2 use warnings;
3
4 use FindBin;
5 use lib "$FindBin::Bin/../../lib";
6 use SQLADumperSort;
7
8 use SQL::Abstract::Compat;
9
10 use Test::More tests => 12;
11 use Test::Differences;
12
13 ok(my $visitor = SQL::Abstract::Compat->new);
14
15
16 my $foo_id = { -type => 'name', args => [qw/foo/] };
17 my $bar_id = { -type => 'name', args => [qw/bar/] };
18
19 my $foo_eq_1 = field_op_value($foo_id, '==', 1);
20 my $bar_eq_str = field_op_value($bar_id, '==', 'some str');
21
22 eq_or_diff
23   $visitor->recurse_where({ foo => 1 }),
24   $foo_eq_1,
25   "Single value hash";
26
27
28
29 eq_or_diff
30   $visitor->recurse_where({ foo => 1, bar => 'some str' }),
31   { -type => 'expr',
32     op => 'and',
33     args => [
34       $bar_eq_str,
35       $foo_eq_1,
36     ]
37   },
38   "two keys in hash";
39
40 eq_or_diff
41   $visitor->recurse_where({ -or => { foo => 1, bar => 'some str' } }),
42   { -type => 'expr',
43     op => 'or',
44     args => [
45       $bar_eq_str,
46       $foo_eq_1,
47     ]
48   },
49   "-or key in hash";
50
51
52 eq_or_diff
53   $visitor->recurse_where([ -and => { foo => 1, bar => 'some str' } ]),
54   { -type => 'expr',
55     op => 'and',
56     args => [
57       $bar_eq_str,
58       $foo_eq_1,
59     ]
60   },
61   "-and as first element of array";
62
63
64 eq_or_diff
65   $visitor->recurse_where([ -and => { foo => 1, bar => 'some str' }, { foo => 1} ]),
66   { -type => 'expr',
67     op => 'or',
68     args => [
69       { -type => 'expr',
70         op => 'and',
71         args => [
72           $bar_eq_str,
73           $foo_eq_1,
74         ]
75       },
76       $foo_eq_1,
77     ]
78   },
79   "-and as first element of array + hash";
80
81 eq_or_diff
82   $visitor->recurse_where({ foo => { '!=' => 'bar' } }),
83   field_op_value($foo_id, '!=', 'bar'),
84   "foo => { '!=' => 'bar' }";
85
86 eq_or_diff
87   $visitor->recurse_where({ foo => [ 1, 'bar' ] }),
88   { -type => 'expr',
89     op => 'or',
90     args => [
91       $foo_eq_1,
92       field_op_value($foo_id, '==', 'bar'),
93     ],
94   },
95   "foo => [ 1, 'bar' ]";
96
97 eq_or_diff
98   $visitor->recurse_where({ foo => { -in => [ 1, 'bar' ] } }),
99   { -type => 'expr',
100     op => 'in',
101     args => [
102       $foo_id,
103       { -type => 'value', value => 1 },
104       { -type => 'value', value => 'bar' },
105     ]
106   },
107   "foo => { -in => [ 1, 'bar' ] }";
108
109 eq_or_diff
110   $visitor->recurse_where({ foo => { -not_in => [ 1, 'bar' ] } }),
111   { -type => 'expr',
112     op => 'not_in',
113     args => [
114       $foo_id,
115       { -type => 'value', value => 1 },
116       { -type => 'value', value => 'bar' },
117     ]
118   },
119   "foo => { -not_in => [ 1, 'bar' ] }";
120
121 eq_or_diff
122   $visitor->recurse_where({ foo => { -in => [ ] } }),
123   { -type => 'expr',
124     op => 'in',
125     args => [
126       $foo_id,
127     ]
128   },
129   "foo => { -in => [ ] }";
130
131 my $worker_eq = sub {
132   return { 
133     -type => 'expr',
134     op => '==',
135     args => [
136       { -type => 'name', args => ['worker'] },
137       { -type => 'value', value => $_[0] },
138     ],
139   }
140 };
141
142 eq_or_diff
143   $visitor->recurse_where( {
144     requestor => 'inna',
145     worker => ['nwiger', 'rcwe', 'sfz'],
146     status => { '!=', 'completed' }
147   } ),
148   { -type => 'expr',
149     op => 'and',
150     args => [
151       field_op_value(qw/requestor == inna/),
152       field_op_value(qw/status != completed/), 
153       { -type => 'expr',
154         op => 'or',
155         args => [
156           field_op_value(qw/worker == nwiger/), 
157           field_op_value(qw/worker == rcwe/), 
158           field_op_value(qw/worker == sfz/), 
159         ]
160       },
161     ]
162   },
163   "complex expr 1";
164
165
166 =for comment
167 $visitor->convert('UPPER');
168
169 eq_or_diff
170   $visitor->select_ast(
171     'test', '*', [ { ticket => [11, 12, 13] } ]
172   ),
173   { -type => 'select',
174     columns => [ { -type => 'name', args => ['*'] } ],
175     tablespec => { -type => 'name', args => ['test'] },
176     where =>
177       { -type => 'expr', op => 'or', args => [
178         field_op_value( upper(mk_name('ticket')), '==', upper(mk_value(11))),
179         field_op_value( upper(mk_name('ticket')), '==', upper(mk_value(12))),
180         field_op_value( upper(mk_name('ticket')), '==', upper(mk_value(13))),
181       ] }
182   },
183   "Complex AST with convert('UPPER')";
184
185 eq_or_diff
186   $visitor->select_ast(
187     'test', '*', [ { ticket => [11, 12, 13], 
188                      hostname => { in => ['ntf', 'avd', 'bvd', '123'] } },
189                   #{ tack => { between => [qw/tick tock/] } },
190                   #{ a => [qw/b c d/], 
191                   #  e => { '!=', [qw(f g)] }, 
192                   #  q => { 'not in', [14..20] } 
193                   #}
194                  ]
195   ),
196   { -type => 'select',
197     columns => [ { -type => 'name', args => ['*'] } ],
198     tablespec => { -type => 'name', args => ['test'] },
199     where =>
200       { -type => 'expr', op => 'or', args => [
201         { -type => 'expr', op => 'and', args => [
202           field_op_value( upper(mk_name('hostname')), 
203                           in => [ 
204                             upper(mk_value('nft')),
205                             upper(mk_value('avd')),
206                             upper(mk_value('bvd')),
207                             upper(mk_value('123')),
208                           ]
209                         ),
210           { -type => 'expr', op => 'or', args => [
211             field_op_value( upper(mk_name('ticket')), '==', upper(mk_value(11))),
212             field_op_value( upper(mk_name('ticket')), '==', upper(mk_value(12))),
213             field_op_value( upper(mk_name('ticket')), '==', upper(mk_value(13))),
214           ] }
215         ] }
216     ] }
217   },
218   "Complex AST with convert('UPPER')";
219 =cut
220
221 sub field_op_value {
222   my ($field, $op, $value) = @_;
223
224   $field = ref $field eq 'HASH'
225          ? $field
226          : ref $field eq 'ARRAY' 
227          ? { -type => 'name', args => $field } 
228          : { -type => 'name', args => [$field] };
229
230   my @value = ref $value eq 'HASH'
231             ? $value
232             : ref $value eq 'ARRAY'
233             ? @$value
234             : { -type => 'value', value => $value };
235
236   return {
237     -type => 'expr',
238     op => $op,
239     args => [
240       $field,
241       @value
242     ]
243   };
244 }
245
246 sub upper { expr(UPPER => @_) }
247
248 sub expr {
249   my ($op, @args) = @_;
250
251   return {
252     -type => 'expr',
253     op => $op,
254     args => [@args]
255   };
256 }
257
258 sub mk_name {
259   my ($field) = @_;
260   $field = ref $field eq 'HASH'
261          ? $field
262          : ref $field eq 'ARRAY' 
263          ? { -type => 'name', args => $field } 
264          : { -type => 'name', args => [$field] };
265   return $field;
266 }
267
268 sub mk_value {
269   return { -type => 'value', value => $_[0] }
270 }