File Coverage

blib/lib/DBIx/Perlish/Parse.pm
Criterion Covered Total %
statement 1095 1328 82.4
branch 691 1008 68.5
condition 219 382 57.3
subroutine 97 103 94.1
pod 0 89 0.0
total 2102 2910 72.2


line stmt bran cond sub pod time code
1             package DBIx::Perlish::Parse;
2 26     26   625 use 5.014;
  26         93  
3 26     26   138 use warnings;
  26         49  
  26         780  
4 26     26   130 use strict;
  26         47  
  26         1032  
5              
6             our $DEVEL;
7             our $_cover;
8              
9 26     26   146 use B;
  26         44  
  26         1184  
10 26     26   143 use Carp;
  26         48  
  26         1402  
11 26     26   10589 use Devel::Caller qw(caller_cv);
  26         68824  
  26         447664  
12              
13 0 0   0   0 sub _o($) { ref($_[0]) . ( $_[0]->can('name') ? ("." . $_[0]->name) : '' ) }
14              
15             sub bailout
16             {
17 57     57 0 143 my ($S, @rest) = @_;
18 57 50       114 if ($DEVEL) {
19 0         0 confess @rest;
20             } else {
21 57         150 my $args = join '', @rest;
22 57 50       111 $args = "Something's wrong" unless $args;
23 57         91 my $file = $S->{file};
24 57         96 my $line = $S->{line};
25 57 100       245 $args .= " at $file line $line.\n"
26             unless substr($args, length($args) -1) eq "\n";
27 57         953 CORE::die($args);
28             }
29             }
30              
31             # "is" checks
32              
33             sub is
34             {
35 52870     52870 0 92501 my ($optype, $op, $name) = @_;
36 52870 100       623679 return 0 unless ref($op) eq $optype;
37 16372 100       45788 return 1 unless $name;
38 14501         153771 return $op->name eq $name;
39             }
40              
41             sub gen_is
42             {
43 364     364 0 779 my ($optype) = @_;
44 364         835 my $pkg = "B::" . uc($optype);
45 364 50   5225 0 14278 eval qq[ sub is_$optype { is("$pkg", \@_) } ] unless __PACKAGE__->can("is_$optype");
  5225     1268 0 13179  
  1268     7270 0 3369  
  7270     3935 0 18985  
  3935     8 0 9918  
  8     806 0 27  
  806     3349 0 2363  
  3349     13276 0 8695  
  13276     808 0 33542  
  808     136 0 2099  
  136     52 0 407  
  52     2548 0 144  
  2548     13899 0 6664  
  13899     290 0 34477  
  290         826  
46             }
47              
48             gen_is("binop");
49             gen_is("pvop");
50             gen_is("cop");
51             gen_is("listop");
52             gen_is("logop");
53             gen_is("loop");
54             gen_is("null");
55             gen_is("op");
56             gen_is("padop");
57             gen_is("svop");
58             gen_is("unop");
59             gen_is("pmop");
60             gen_is("methop");
61             gen_is("unop_aux");
62              
63             sub is_const
64             {
65 1543     1543 0 2740 my ($S, $op) = @_;
66 1543 100       28759 return () unless is_svop($op, "const");
67 970         3056 my $sv = $op->sv;
68 970 50       2238 if (!$$sv) {
69 0         0 $sv = $S->{padlist}->[1]->ARRAYelt($op->targ);
70             }
71 970 100       1905 if (wantarray) {
72 124         205 return (${$sv->object_2svref}, $sv);
  124         632  
73             } else {
74 846         1118 return ${$sv->object_2svref};
  846         3029  
75             }
76             }
77              
78             # "want" helpers
79              
80             sub gen_want
81             {
82 130     130 0 438 my ($optype, $return) = @_;
83 130 100       714 if (!$return) {
    100          
84 52         441 $return = '$op';
85             } elsif ($return =~ /^\w+$/) {
86 52         140 $return = '$op->' . $return;
87             }
88 130 50   6 0 11676 eval <can("want_$optype");
  6 0   378 0 27  
  6 50   0 0 102  
  0 0   8 0 0  
  0 50   1027 0 0  
  6 0       19  
  378 0       974  
  378 0       6615  
  0 50       0  
  0 0       0  
  378 50       921  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  8         30  
  8         166  
  0         0  
  0         0  
  8         39  
  1027         2566  
  1027         18019  
  0         0  
  0         0  
  1027         4573  
89             sub want_$optype {
90             my (\$S, \$op, \$n) = \@_;
91             unless (is_$optype(\$op, \$n)) {
92             bailout \$S, "want $optype" unless \$n;
93             bailout \$S, "want $optype \$n";
94             }
95             $return;
96             }
97             EOF
98             }
99              
100             gen_want("op");
101             gen_want("unop", "first");
102             gen_want("listop", 'get_all_children($op)');
103             gen_want("svop", "sv");
104             gen_want("null");
105              
106             sub is_pushmark_or_padrange
107             {
108 3392     3392 0 5484 my $op = shift;
109 3392   100     60405 return is_op($op, "pushmark") || is_op($op, "padrange");
110             }
111              
112             sub want_pushmark_or_padrange
113             {
114 378     378 0 745 my ($S, $op) = @_;
115 378 50       732 unless (is_pushmark_or_padrange($op)) {
116 0         0 bailout $S, "want op pushmark or op padrange, got " . _o $op;
117             }
118             }
119              
120             sub want_const
121             {
122 8     8 0 21 my ($S, $op) = @_;
123 8         141 my $sv = want_svop($S, $op, "const");
124 8 50       25 if (!$$sv) {
125 0         0 $sv = $S->{padlist}->[1]->ARRAYelt($op->targ);
126             }
127 8         12 ${$sv->object_2svref};
  8         30  
128             }
129              
130             sub want_variable_method
131             {
132 2     2 0 5 my ($S, $op) = @_;
133 2 50 33     35 return unless is_unop($op, "method") || is_methop($op, "method");
134 2         9 $op = $op->first;
135 2 50       42 return unless is_null($op->sibling);
136 2         8 my ($name, $ok) = get_value($S, $op, soft => 1);
137 2 50       7 return unless $ok;
138 2         5 return $name;
139             }
140              
141             sub want_method
142             {
143 591     591 0 1224 my ($S, $op) = @_;
144 591         814 my $sv;
145 591 100       11741 if ( is_methop($op, "method_named")) {
    50          
146 589         1880 $sv = $op->meth_sv;
147             } elsif ( is_svop($op, "method_named")) {
148 0         0 $sv = $op->sv;
149             } else {
150 2         9 my $r = want_variable_method($S, $op);
151 2 50       5 bailout $S, "method call syntax expected" unless $r;
152 2         13 return $r;
153             }
154 589 50       1418 if (!$$sv) {
155 0         0 $sv = $S->{padlist}->[1]->ARRAYelt($op->targ);
156             }
157 589         797 ${$sv->object_2svref};
  589         2003  
158             }
159              
160             # getters
161              
162             sub get_all_children
163             {
164 551     551 0 961 my ($op) = @_;
165 551         1833 my $c = $op->children;
166 551         929 my @op;
167 551 50       1149 return @op unless $c;
168 551         1880 push @op, $op->first;
169 551         1850 while (--$c) {
170 1281         4740 push @op, $op[-1]->sibling;
171             }
172 551         1443 @op;
173             }
174              
175             sub padname
176             {
177 591     591 0 1175 my ($S, $op, %p) = @_;
178              
179 591         2730 my $padname = $S->{padlist}->[0]->ARRAYelt($op->targ);
180 591 50 33     3788 if ($padname && !$padname->isa("B::SPECIAL")) {
181 591 100 100     1558 return if $p{no_fakes} && $padname->FLAGS & B::SVf_FAKE;
182 590 100       1654 return unless defined $padname->PVX;
183 588         2443 return "my " . $padname->PVX;
184             } else {
185 0         0 return "my #" . $op->targ;
186             }
187             }
188              
189             sub get_padlist_scalar_by_name
190             {
191 6     6 0 18 my ($S, $n) = @_;
192 6         13 my $padlist = $S->{padlist};
193 6         38 my @n = $padlist->[0]->ARRAY;
194 6         22 for (my $k = 0; $k < @n; $k++) {
195 34 50       179 next if $n[$k]->isa("B::SPECIAL");
196 34 50       88 next if $n[$k]->isa("B::NULL");
197 34 100 100     148 if (($n[$k]->PVX // '') eq $n) {
198 6         31 my $v = $padlist->[1]->ARRAYelt($k);
199 6 100       27 if (!$v->isa("B::SPECIAL")) {
200 3         19 return $v;
201             }
202 3 50       16 if ($n[$k]->FLAGS & B::SVf_FAKE) {
203             bailout $S, "internal error: cannot retrieve value of $n: no more scopes to check"
204 3 50       19 unless $S->{gen_args}->{prev_S};
205 3         17 return get_padlist_scalar_by_name($S->{gen_args}->{prev_S}, $n);
206             }
207 0         0 bailout $S, "internal error: cannot retrieve value of $n: it's an in-scope SPECIAL";
208             }
209             }
210 0         0 bailout $S, "internal error: cannot retrieve value of $n: not found in outer scope";
211             }
212              
213             sub get_padlist_scalar
214             {
215 51     51 0 124 my ($S, $i, $ref_only) = @_;
216 51         103 my $padlist = $S->{padlist};
217 51         181 my $v = $padlist->[1]->ARRAYelt($i);
218 51 50       182 bailout $S, "internal error: no such pad element" unless $v;
219 51 100       307 if ($v->isa("B::SPECIAL")) {
220 3         26 my $n = $padlist->[0]->ARRAYelt($i);
221 3 50       16 if ($n->FLAGS & B::SVf_FAKE) {
222 3         18 $v = get_padlist_scalar_by_name($S, $n->PVX);
223             } else {
224 0         0 bailout $S, "internal error: cannot retrieve in-scope SPECIAL";
225             }
226             }
227 51         169 $v = $v->object_2svref;
228 51 100       133 return $v if $ref_only;
229 44         110 return $$v;
230             }
231              
232             sub bailout_multiref_vivify($)
233             {
234 5     5 0 9 my $S = shift;
235 5         29 bailout $S,
236             "accessing fields syntax is not supported anymore; for tables use methods instead, ".
237             "for arrayrefs and hashrefs don't leave them unassigned"
238             }
239              
240             sub aux_init_padsv
241             {
242 50     50 0 94 my ( $S ) = @_;
243              
244 50         165 my $inner = $S->{curr_cv}->PADLIST;
245             return {
246 50         659 S => $S,
247             inner => $inner,
248             orig_pads => [ $inner->ARRAY ]->[1],
249             names => [ $inner->NAMES->ARRAY ],
250             outer_padlist => undef,
251             outer_padlist_array => undef,
252             padlist => [],
253             };
254             }
255              
256             sub aux_get_padsv
257             {
258 58     58 0 124 my ( $store, $index ) = @_;
259              
260 58 50       175 unless ( defined $store->{padlist}->[$index] ) {
261 58         96 my $padname = $store->{names}->[$index];
262 58 100 100     343 if ( $padname->FLAGS & B::SVf_FAKE && $store->{inner}->outid > 1) {
263 3 50       12 unless ($store->{outer_padlist}) {
264 3         23 $store->{outer_padlist} = $store->{S}->{padlists}->{$store->{inner}->outid};
265 3 100       12 unless ($store->{outer_padlist}) {
266             # hacky hacky - look up the caller stack to get their padlists, maybe?
267 1         2 my $id = 0;
268 1         3 while ( 1 ) {
269 14 50       33 my $sub = caller_cv($id++) or last;
270 14         128 my $padlist = B::svref_2object($sub)->PADLIST;
271 14   100     110 $store->{S}->{padlists}->{$padlist->id} //= [$padlist->ARRAY];
272 14 100       57 next unless $padlist->id == $store->{inner}->outid;
273 1         5 $store->{outer_padlist} = $store->{S}->{padlists}->{$store->{inner}->outid};
274 1         3 last;
275             }
276             }
277 3 50       10 goto DEFAULT_PADLIST unless $store->{outer_padlist};
278 3         41 $store->{outer_padlist_array} = [$store->{outer_padlist}->[1]->ARRAY];
279             }
280 3         18 $store->{padlist}->[$index] = $store->{outer_padlist_array}->[ $padname->PARENT_PAD_INDEX ];
281             } else {
282             DEFAULT_PADLIST:
283 55         211 $store->{padlist}->[$index] = $store->{orig_pads}->ARRAYelt($index);
284             }
285             }
286              
287 58         238 return $store->{padlist}->[$index];
288             }
289              
290             sub parse_multideref
291             {
292 50     50 0 101 my ( $S, $aux ) = @_;
293 50         216 my @items = $aux->aux_list($S->{curr_cv});
294 50         79 my @ret;
295              
296 50         124 my $AUX = aux_init_padsv($S);
297              
298 50         152 ITEMS: while ( @items ) {
299 50         92 my $actions = shift @items;
300              
301 50         82 my ($ref, $reftype);
302 50 50       134 my $sv = shift(@items) or return undef;
303              
304 50         108 while ( @items ) {
305 69         123 my $ptr = shift @items;
306 69         128 my $access = $actions & B::MDEREF_ACTION_MASK();
307 69 50       163 if ( $access == B::MDEREF_reload() ) { # XXX
308 0         0 $actions = $sv;
309 0         0 next;
310             }
311 69 100       150 unless ($ref) {
312 51 100 66     355 if (
    100 100        
    50 100        
    0 66        
      100        
      33        
      33        
      0        
313             $access == B::MDEREF_HV_padhv_helem() ||
314             $access == B::MDEREF_AV_padav_aelem() ||
315             $access == B::MDEREF_HV_padsv_vivify_rv2hv_helem() ||
316             $access == B::MDEREF_AV_padsv_vivify_rv2av_aelem()
317             ) {
318 46         172 $ref = aux_get_padsv($AUX, $sv)->object_2svref;
319 46 100 66     269 bailout_multiref_vivify $S
      66        
320             if !$ref || ((ref($ref) eq 'SCALAR') && !$$ref);
321             } elsif (
322             $access == B::MDEREF_HV_pop_rv2hv_helem() ||
323             $access == B::MDEREF_HV_vivify_rv2hv_helem() ||
324             $access == B::MDEREF_HV_gvhv_helem()
325             ) {
326 3 100       19 bailout_multiref_vivify $S unless ref($sv);
327 2         10 $ref = $sv->HV->object_2svref;
328             } elsif (
329             $access == B::MDEREF_AV_pop_rv2av_aelem() ||
330             $access == B::MDEREF_AV_vivify_rv2av_aelem() ||
331             $access == B::MDEREF_AV_gvav_aelem()
332             ) {
333 2 50       9 bailout_multiref_vivify $S unless ref($sv);
334 2         15 $ref = $sv->AV->object_2svref;
335             } elsif (
336             $access == B::MDEREF_AV_gvsv_vivify_rv2av_aelem() ||
337             $access == B::MDEREF_HV_gvsv_vivify_rv2hv_helem()
338             ) {
339 0         0 $ref = $sv->object_2svref;
340 0 0 0     0 bailout_multiref_vivify $S
      0        
341             if !$ref || ((ref($ref) eq 'SCALAR') && !$$ref);
342 0         0 $ref = $$ref;
343             } else {
344 0         0 bailout $S, "don't quite know what to do with multideref access=$access";
345             }
346             }
347              
348             $reftype = (
349 64 100 100     453 $access == B::MDEREF_HV_padhv_helem() ||
350             $access == B::MDEREF_HV_gvsv_vivify_rv2hv_helem() ||
351             $access == B::MDEREF_HV_padsv_vivify_rv2hv_helem() ||
352             $access == B::MDEREF_HV_pop_rv2hv_helem() ||
353             $access == B::MDEREF_HV_vivify_rv2hv_helem() ||
354             $access == B::MDEREF_HV_gvhv_helem()
355             ) ? 'HASH' : 'ARRAY';
356            
357 64         90 my $key;
358 64         104 my $index = $actions & B::MDEREF_INDEX_MASK();
359              
360 64 50       122 if ( $index != B::MDEREF_INDEX_none() ) {
361 64 100       133 if ( $index == B::MDEREF_INDEX_const() ) {
    50          
    0          
362 52 100       149 $key = ref($ptr) ? $ptr->object_2svref : $ptr;
363             } elsif ( $index == B::MDEREF_INDEX_padsv() ) {
364 12         25 $key = aux_get_padsv($AUX, $ptr)->object_2svref;
365             } elsif ( $index == B::MDEREF_INDEX_gvsv() ) {
366 0 0       0 $key = ref($ptr) ? $ptr->object_2svref : $ptr;
367             }
368              
369 64 100       369 $ref = $$ref if ref($ref) =~ /REF|SCALAR/;
370 64 100       315 $key = $$key if ref($key) =~ /REF|SCALAR/;
371              
372 64 50       149 bailout $S, "Can't use value (\"$ref\") as an $reftype ref"
373             unless $reftype eq ref($ref);
374 64 100       129 if ( $reftype eq 'ARRAY') {
375 18         41 $ref = $ref->[$key];
376             } else {
377 46         97 $ref = $ref->{$key};
378             }
379              
380 64 100 66     184 if (!defined($ref) && (
      66        
381             $access == B::MDEREF_AV_gvsv_vivify_rv2av_aelem () ||
382             $access == B::MDEREF_AV_padsv_vivify_rv2av_aelem() ||
383             $access == B::MDEREF_AV_vivify_rv2av_aelem () ||
384             $access == B::MDEREF_HV_gvsv_vivify_rv2hv_helem () ||
385             $access == B::MDEREF_HV_padsv_vivify_rv2hv_helem() ||
386             $access == B::MDEREF_HV_vivify_rv2hv_helem ()
387             )) {
388 2         4 push @ret, undef;
389 2         6 last ITEMS;
390             }
391             }
392 62 50 33     224 if ($index == B::MDEREF_INDEX_none() || $index & B::MDEREF_FLAG_last()) {
393 0         0 push @ret, $ref;
394 0         0 last;
395             }
396 62         173 $actions >>= B::MDEREF_SHIFT();
397             }
398              
399 43 50       155 push @ret, $ref unless @ret;
400             }
401              
402 45 50       100 bailout $S, "cannot infer single multideref value" unless 1 == @ret;
403              
404 45         214 return $ret[0];
405             }
406              
407             sub get_concat_value
408             {
409 2     2 0 6 my ( $S, @args) = @_;
410 2         4 my $val = '';
411 2         5 for my $op ( @args ) {
412 4         7 my ($rv, $ok);
413 4 100       7 if ( $rv = is_const($S, $op)) {
414             } else {
415 2         43 ($rv, $ok) = get_value($S, $op, soft => 1, eval => 1);
416 2 50       8 bailout $S, "cannot parse expression (near $val)" unless $ok;
417             }
418 4         11 $val .= $rv;
419             }
420 2         5 return $val;
421             }
422              
423             sub get_value
424             {
425 276     276 0 804 my ($S, $op, %p) = @_;
426              
427 276         425 my $val;
428 276 100 66     5015 if (is_op($op, "padsv")) {
    50 66        
    50 66        
    100 100        
    100 66        
    100 33        
    100 66        
    50          
    50          
    100          
429 44 50       282 if (find_aliased_tab($S, $op)) {
430 0         0 bailout $S, "cannot use a table variable as a value";
431             }
432 44         204 $val = get_padlist_scalar($S, $op->targ);
433             } elsif (is_binop($op, "helem")) {
434 0         0 my @key = is_const($S, $op->last);
435 0         0 my $key = $key[0];
436 0 0       0 unless ( @key ) {
437 0         0 my $xop = $op->last;
438 0 0       0 if (is_op($xop, "padsv")) {
439 0 0       0 if (find_aliased_tab($S, $xop)) {
440 0         0 bailout $S, "cannot use a table variable as a value";
441             }
442 0         0 $key = get_padlist_scalar($S, $xop->targ);
443             } else {
444 0         0 bailout $S, "hash key not understood";
445             }
446             }
447 0         0 $op = $op->first;
448              
449 0         0 my $vv;
450 0 0       0 if (is_op($op, "padhv")) {
    0          
451 0         0 $vv = get_padlist_scalar($S, $op->targ, "ref only");
452             } elsif (is_unop($op, "rv2hv")) {
453 0         0 $op = $op->first;
454 0 0 0     0 if (is_op($op, "padsv")) {
    0 0        
    0          
455 0 0       0 if (find_aliased_tab($S, $op)) {
456 0         0 bailout $S, "cannot use a table variable as a value";
457             }
458 0         0 $vv = get_padlist_scalar($S, $op->targ);
459             } elsif (is_svop($op, "gv") || is_padop($op, "gv")) {
460 0         0 my $gv = get_gv($S, $op, bailout => 1);
461 0         0 $vv = $gv->HV->object_2svref;
462             } elsif (is_binop($op, "helem") || is_binop($op, "aelem")) {
463 0         0 my ($nv, $ok) = get_value($S, $op, %p);
464 0 0       0 $vv = $nv if $ok;
465             }
466             }
467 0         0 $val = $vv->{$key};
468             } elsif (is_binop($op, "aelem")) {
469 0         0 my @key = is_const($S, $op->last);
470 0         0 my $key = $key[0];
471 0 0       0 unless ( @key ) {
472 0         0 my $xop = $op->last;
473 0 0       0 if (is_op($xop, "padsv")) {
474 0 0       0 if (find_aliased_tab($S, $xop)) {
475 0         0 bailout $S, "cannot use a table variable as a value";
476             }
477 0         0 $key = get_padlist_scalar($S, $xop->targ);
478             } else {
479 0         0 bailout $S, "array index not understood";
480             }
481             }
482 0         0 $op = $op->first;
483              
484 0         0 my $vv;
485 0 0       0 if (is_op($op, "padav")) {
    0          
486 0         0 $vv = get_padlist_scalar($S, $op->targ, "ref only");
487             } elsif (is_unop($op, "rv2av")) {
488 0         0 $op = $op->first;
489 0 0 0     0 if (is_op($op, "padsv")) {
    0 0        
    0          
490 0 0       0 if (find_aliased_tab($S, $op)) {
491 0         0 bailout $S, "cannot use a table variable as a value";
492             }
493 0         0 $vv = get_padlist_scalar($S, $op->targ);
494             } elsif (is_svop($op, "gv") || is_padop($op, "gv")) {
495 0         0 my $gv = get_gv($S, $op, bailout => 1);
496 0         0 $vv = $gv->AV->object_2svref;
497             } elsif (is_binop($op, "helem") || is_binop($op, "aelem")) {
498 0         0 my ($nv, $ok) = get_value($S, $op, %p);
499 0 0       0 $vv = $nv if $ok;
500             }
501             }
502 0         0 $val = $vv->[$key];
503             } elsif (is_svop($op, "gvsv") || is_padop($op, "gvsv")) {
504 1         7 my $gv = get_gv($S, $op, bailout => 1);
505 1         8 $val = ${$gv->SV->object_2svref};
  1         11  
506             } elsif (is_unop($op, "null") && (is_svop($op->first, "gvsv") || is_padop($op->first, "gvsv"))) {
507 1         7 my $gv = get_gv($S, $op->first, bailout => 1);
508 1         3 $val = ${$gv->SV->object_2svref};
  1         6  
509             } elsif (is_unop($op, "null") && is_unop_aux($op->first, "multideref")) {
510 11         61 $val = parse_multideref($S, $op->first);
511             } elsif ( $p{eval} && is_binop($op, "concat")) {
512 2         10 my @args = ($op->first);
513 2   66     40 push @args, $args[-1]->sibling while !is_null($args[-1]) && !is_null($args[-1]->sibling);
514 2         11 $val = get_concat_value($S, @args);
515             } elsif ( $p{eval} && is_unop_aux($op, "multiconcat")) {
516 0 0       0 my @terms = parse_multiconcat($S, $op, eval => 1) or goto BAILOUT;
517 0         0 $val = join('', map { $_->{str} } @terms);
  0         0  
518             } elsif (is_op($op, "aelemfast_lex")) {
519 0         0 my $sv = $S->{padlist}->[1]->ARRAYelt($op->targ);
520 0 0       0 goto BAILOUT unless $sv;
521 0         0 $sv = $sv->object_2svref;
522 0         0 $val = $sv->[$op->private];
523             } elsif (is_svop($op, "aelemfast") || is_padop($op, "aelemfast")) {
524             my $sv = is_padop($op, "aelemfast") ?
525 2 50       39 $S->{padlist}->[1]->ARRAYelt($op->padix) :
    50          
526             $op->sv or goto BAILOUT;
527 2 50       19 $sv = $sv->object_2svref or goto BAILOUT;
528 2 50       5 $sv = ${$sv} or goto BAILOUT;
  2         10  
529 2         14 $val = $sv->[$op->private];
530             } else {
531             BAILOUT:
532 215 50       4295 return () if $p{soft};
533 0         0 bailout $S, "cannot parse \"", $op->name, "\" op as a value or value reference";
534             }
535 61         275 return ($val, 1);
536             }
537              
538             sub get_var
539             {
540 5     5 0 21 my ($S, $op) = @_;
541 5 50       93 if (is_op($op, "padsv")) {
    0          
542 5         25 return padname($S, $op);
543             } elsif (is_unop($op, "null")) {
544 0         0 $op = $op->first;
545 0         0 want_svop($S, $op, "gvsv");
546 0         0 return "*" . $op->gv->NAME;
547             } else {
548             # XXX
549 0         0 print "$op\n";
550 0         0 print "type: ", $op->type, "\n";
551 0         0 print "name: ", $op->name, "\n";
552 0         0 print "desc: ", $op->desc, "\n";
553 0         0 print "targ: ", $op->targ, "\n";
554 0         0 bailout $S, "cannot get var";
555             }
556             }
557              
558             sub find_aliased_tab
559             {
560 369     369 0 662 my ($S, $op) = @_;
561 369         801 my $var = padname($S, $op);
562 369 100       933 return "" unless defined $var;
563              
564 367         583 my $ss = $S;
565 367         758 while ($ss) {
566 415         585 my $tab;
567 415 50       895 if ($ss->{operation} eq "select") {
568 415         985 $tab = $ss->{var_alias}{$var};
569             } else {
570 0         0 $tab = $ss->{vars}{$var};
571             }
572 415 100       1240 return $tab if $tab;
573 96         254 $ss = $ss->{gen_args}->{prev_S};
574             }
575 48         138 return "";
576             }
577              
578             sub get_tab_field
579             {
580 378     378 0 831 my ($S, $unop, %p) = @_;
581 378         7001 my $op = want_unop($S, $unop, "entersub");
582 378         1089 want_pushmark_or_padrange($S, $op);
583 378         1289 $op = $op->sibling;
584 378         854 my $tab = is_const($S, $op);
585 378 100       4676 if ($tab) {
    50          
586 150         376 $tab = new_tab($S, $tab);
587             } elsif (is_op($op, "padsv")) {
588 228         543 $tab = find_aliased_tab($S, $op);
589             }
590 378 50       858 unless ($tab) {
591 0         0 bailout $S, "cannot get a table";
592             }
593 378         1294 $op = $op->sibling;
594 378         896 my $field = want_method($S, $op);
595 378         1190 $op = $op->sibling;
596 378 50 66     1422 if ($p{lvalue} && is_unop($op, "rv2cv")) {
597 0         0 want_unop($S, $op, "rv2cv");
598 0         0 $op = $op->sibling;
599             }
600 378         7478 want_null($S, $op);
601 378 100 100     1198 if ($S->{parsing_return} && !$S->{inside_aggregate}) {
602 86 50       335 my $ff = $S->{operation} eq "select" ? "$tab.$field" : $field;
603 86 100       332 push @{$S->{autogroup_by}}, $ff unless $S->{autogroup_fields}{$ff}++;
  85         235  
604             }
605 378         1374 ($tab, $field);
606             }
607              
608             # helpers
609              
610             sub maybe_one_table_only
611             {
612 353     353 0 603 my ($S) = @_;
613 353 100       1015 return if $S->{operation} eq "select";
614 27 50 66     99 if ($S->{tabs} && keys %{$S->{tabs}} or $S->{vars} && keys %{$S->{vars}}) {
  22   33     139  
  0   33     0  
615 0         0 bailout $S, "a $S->{operation}'s query sub can only refer to a single table";
616             }
617             }
618              
619             sub incr_string
620             {
621 353     353 0 647 my ($s) = @_;
622 353         1985 my ($prefix, $suffix) = $s =~ /^(.*_)?(.*)$/;
623 353   100     1654 $prefix ||= "";
624 353         603 $suffix++;
625 353         1019 return "$prefix$suffix";
626             }
627              
628             sub new_tab
629             {
630 150     150 0 328 my ($S, $tab) = @_;
631 150 100       489 unless ($S->{tabs}{$tab}) {
632 137         349 maybe_one_table_only($S);
633 137         312 $S->{tabs}{$tab} = 1;
634 137         364 $S->{tab_alias}{$tab} = $S->{alias};
635 137         291 $S->{alias} = incr_string($S->{alias});
636             }
637 150         366 $S->{tab_alias}{$tab};
638             }
639              
640             sub new_var
641             {
642 216     216 0 473 my ($S, $var, $tab) = @_;
643 216         622 maybe_one_table_only($S);
644             bailout $S, "cannot reuse $var for table $tab, it's already used by $S->{vars}{$var}"
645 216 50       676 if $S->{vars}{$var};
646 216         700 $S->{vars}{$var} = $tab;
647 216         563 $S->{var_alias}{$var} = $S->{alias};
648 216         567 $S->{alias} = incr_string($S->{alias});
649             }
650              
651             # parsers
652              
653             sub try_parse_attr_assignment
654             {
655 223     223 0 493 my ($S, $op, $realname, %opt) = @_;
656 223 50       4151 return unless is_unop($op, "entersub");
657 223         4250 $op = want_unop($S, $op);
658 223 100       592 return unless is_pushmark_or_padrange($op);
659 214         955 $op = $op->sibling;
660 214         536 my $c = is_const($S, $op);
661 214 100 66     1010 return unless $c && $c eq "attributes";
662 213         781 $op = $op->sibling;
663 213 50       486 return unless is_const($S, $op);
664 213         751 $op = $op->sibling;
665 213 50       3997 return unless is_unop($op, "srefgen");
666 213         3954 my $op1 = want_unop($S, $op);
667 213 50       3838 $op1 = want_unop($S, $op1) if is_unop($op1, "null");
668 213 50       3896 return unless is_op($op1, "padsv");
669 213         591 my $varn = padname($S, $op1);
670 213         757 $op = $op->sibling;
671 213         491 my $attr = is_const($S, $op);
672 213 50       505 return unless $attr;
673 213         829 my @attr = grep { length($_) } split /(?:[\(\)])/, $attr;
  216         737  
674 213 50       566 return unless @attr;
675 213         770 $op = $op->sibling;
676 213 50 33     4275 return unless is_methop($op, "method_named") || is_svop($op, "method_named");
677 213 50       581 return unless want_method($S, $op, "import");
678 213 100       473 if ($realname) {
679 10 50       42 if (lc $attr[0] eq "table") {
680 10         31 @attr = ($realname);
681             } else {
682 0         0 bailout $S, "cannot decide whether you refer to $realname table or to @attr table";
683             }
684             } else {
685 203 100 100     745 shift @attr if lc $attr[0] eq "table" && @attr > 1;
686             }
687 213         549 $attr = join ".", @attr;
688 213         571 new_var($S, $varn, $attr);
689 213         788 return $varn;
690             }
691              
692             sub parse_list
693             {
694 386     386 0 811 my ($S, $op) = @_;
695 386         857 my @op = get_all_children($op);
696 386         824 for $op (@op) {
697 1369         2926 parse_op($S, $op);
698             }
699             }
700              
701             sub parse_return
702             {
703 103     103 0 211 my ($S, $op) = @_;
704 103         241 my @op = get_all_children($op);
705             bailout $S, "there should be no \"return\" statements in $S->{operation}'s query sub"
706 103 50       316 unless $S->{operation} eq "select";
707 103 100       280 bailout $S, "there should be at most one return statement" if $S->{returns};
708 100         229 $S->{returns} = [];
709 100         206 my $last_alias;
710 100         203 for $op (@op) {
711 251         550 my %rv = parse_return_value($S, $op);
712 240 100       872 if (exists $rv{table}) {
    100          
    100          
713 15 50       45 bailout $S, "cannot alias the whole table"
714             if defined $last_alias;
715 15         31 push @{$S->{returns}}, "$rv{table}.*";
  15         58  
716 15         126 $S->{no_autogroup} = 1;
717             } elsif (exists $rv{field}) {
718 109 100       249 if (defined $last_alias) {
719 9 100       53 bailout $S, "a key field cannot be aliased" if $rv{key_field};
720 8         14 push @{$S->{returns}}, "$rv{field} as $last_alias";
  8         30  
721 8         48 undef $last_alias;
722             } else {
723 100 100       231 if ($rv{key_field}) {
724 11         30 my $kf = '$kf-' . $S->{key_field};
725 11 50       26 if ($S->{gen_args}{kf_convert}) {
726 0         0 $kf = $S->{gen_args}{kf_convert}->($kf);
727             }
728 11         17 $S->{key_field}++;
729 11         30 push @{$S->{returns}}, "$rv{field} as \"$kf\"";
  11         37  
730 11         19 push @{$S->{key_fields}}, $kf;
  11         48  
731             } else {
732 89         129 push @{$S->{returns}}, $rv{field};
  89         601  
733             }
734             }
735             } elsif (exists $rv{alias}) {
736 16 100       46 if (defined $last_alias) {
737             # XXX maybe check whether it is a number and inline it?
738 3         5 push @{$S->{ret_values}}, $rv{alias};
  3         8  
739 3         5 push @{$S->{returns}}, "? as $last_alias";
  3         9  
740 3         6 undef $last_alias;
741 3         21 next;
742             }
743             bailout $S, "bad alias name \"$rv{alias}\""
744 13 50       68 unless $rv{alias} =~ /^\w+$/;
745 13 100       53 if (lc $rv{alias} eq "distinct") {
746 1 50       3 bailout $S, "\"$rv{alias}\" is not a valid alias name" if @{$S->{returns}};
  1         5  
747 1         3 $S->{distinct}++;
748 1         3 next;
749             }
750 12         33 $last_alias = $rv{alias};
751             }
752             }
753             }
754              
755             sub parse_return_value
756             {
757 265     265 0 486 my ($S, $op) = @_;
758              
759 265 100       5136 if (is_op($op, "padsv")) {
    100          
    100          
    100          
760 16         58 return table => find_aliased_tab($S, $op);
761             } elsif (my $const = is_const($S, $op)) {
762 17         55 return alias => $const;
763             } elsif (is_pushmark_or_padrange($op)) {
764 100         331 return ();
765             } elsif (is_unop($op, "ftsvtx")) {
766 14         60 my %r = parse_return_value($S, $op->first);
767             bailout $S, "only a single value return specification can be a key field"
768 14 100       44 unless $r{field};
769 12 50       31 $r{key_field} = 1 unless $S->{gen_args}->{prev_S};
770 12         55 return %r;
771             } else {
772 118         245 my $saved_values = $S->{values};
773 118         228 $S->{values} = [];
774 118         229 $S->{parsing_return} = 1;
775 118         286 my $ret = parse_term($S, $op);
776 109         216 $S->{parsing_return} = 0;
777 109         151 push @{$S->{ret_values}}, @{$S->{values}};
  109         197  
  109         217  
778 109         199 $S->{values} = $saved_values;
779 109         403 return field => $ret;
780             }
781             }
782              
783             sub parse_term
784             {
785 688     688 0 1413 my ($S, $op, %p) = @_;
786              
787 688         961 my $placeholder;
788 688         1623 local $S->{in_term} = 1;
789 688 100       13337 if (is_unop($op, "entersub")) {
    50          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
790 381         1100 my $funcall = try_funcall($S, $op);
791 380 100       1018 return $funcall if defined $funcall;
792 345         872 my ($t, $f) = get_tab_field($S, $op);
793 345 100 66     1595 if ($S->{operation} eq "delete" || $S->{operation} eq "update") {
794 7         24 return $f;
795             } else {
796 338         1538 return "$t.$f";
797             }
798             } elsif (is_unop($op, "lc")) {
799 0         0 my $term = parse_term($S, $op->first);
800 0         0 return "lower($term)";
801             } elsif (is_unop($op, "uc")) {
802 0         0 my $term = parse_term($S, $op->first);
803 0         0 return "upper($term)";
804             } elsif (is_unop($op, "abs")) {
805 1         27 my $term = parse_term($S, $op->first);
806 1         5 return "abs($term)";
807             } elsif (is_unop($op, "null")) {
808 53         309 return parse_term($S, $op->first, %p);
809             } elsif (is_op($op, "null")) {
810 8         170 return parse_term($S, $op->sibling, %p);
811             } elsif (is_op($op, "undef")) {
812 4         15 return "null";
813             } elsif (is_unop($op, "not")) {
814 15         61 my $subop = $op-> first;
815 15 100       340 if (is_pmop($subop, "match")) {
816 2         7 return parse_regex( $S, $subop, 1);
817             } else {
818 13         63 my ($term, $with_not) = parse_term($S, $subop);
819 13 100       64 if ($p{not_after}) {
    100          
820 7         38 return "$term not";
821             } elsif ($with_not) {
822 2         8 return $with_not;
823             } else {
824 4         29 return "not $term";
825             }
826             }
827             } elsif (is_unop($op, "defined")) {
828 4         26 my $term = parse_term($S, $op->first);
829             return wantarray ?
830 4 100       30 ("$term is not null", "$term is null") :
831             "$term is not null";
832             } elsif (($placeholder) = get_value($S, $op, soft => 1)) {
833 28 50 66     117 return 'null' if !defined($placeholder) && $S->{gen_args}->{inline};
834 24         855 goto PLACEHOLDER;
835             } elsif (is_unop($op, "backtick")) {
836 4         18 my $fop = $op->first;
837 4         74 $fop = $fop->sibling while is_op($fop, "null");
838 4         12 my $sql = is_const($S, $fop);
839 4 50       36 return $sql if $sql;
840             } elsif (is_binop($op)) {
841 36         170 my $expr = parse_expr($S, $op);
842 32         157 return "($expr)";
843             } elsif (is_logop($op, "or")) {
844 2         12 my $or = parse_or($S, $op);
845 2 50       11 bailout $S, "looks like a limiting range or a conditional inside an expression\n"
846             unless $or;
847 2         12 return "($or)";
848             } elsif (is_logop($op, "and")) {
849 2         16 my $and = parse_and($S, $op);
850 2 50       7 bailout $S, "looks like a conditional inside an expression\n"
851             unless $and;
852 2         12 return "($and)";
853             } elsif (my ($const,$sv) = is_const($S, $op)) {
854 98 100 66     998 if (($sv->isa("B::IV") && !$sv->isa("B::PVIV")) ||
      33        
      66        
855             ($sv->isa("B::NV") && !$sv->isa("B::PVNV")))
856             {
857             # This is surely a number, so we can
858             # safely inline it in the SQL.
859 55         223 return $const;
860             } else {
861             # This will probably be represented by a string,
862             # we'll let DBI to handle the quoting of a bound
863             # value.
864 43         80 $placeholder = $const;
865 43         1360 goto PLACEHOLDER;
866             }
867             } elsif (is_pvop($op, "next")) {
868 12         30 my $seq = $op->pv;
869 12   50     31 my $flavor = $S->{gen_args}->{flavor}||"";
870 12 100 66     31 if ($flavor eq "oracle") {
    100          
871 5 100       21 bailout $S, "Sequence name looks wrong" unless $seq =~ /^\w+$/;
872 4         14 return "$seq.nextval";
873             } elsif ($flavor eq "pg" || $flavor eq "pglite") {
874 6 100       17 bailout $S, "Sequence name looks wrong" if $seq =~ /'/; # XXX well, I am lazy
875 5         20 return "nextval('$seq')";
876             } else {
877 1         3 bailout $S, "Sequences do not seem to be supported for this DBI flavor";
878             }
879             } elsif (is_pmop($op, "match")) {
880 2         12 return parse_regex($S, $op, 0);
881             } elsif (is_unop_aux($op, "multideref")) {
882 37         99 $placeholder = parse_multideref($S, $op);
883 34         1016 goto PLACEHOLDER;
884             } elsif (is_unop_aux($op, "multiconcat")) {
885 0         0 my ($c, $v) = try_special_concat($S, $op);
886 0 0       0 if ($c) {
887 0         0 push @{$S->{values}}, @$v;
  0         0  
888 0         0 return "($c)";
889             }
890 0         0 bailout $S, "unsupported multiconcat";
891             } else {
892 1         6 BAILOUT:
893             bailout $S, "cannot reconstruct term from operation \"",
894             $op->name, '"';
895             }
896              
897             PLACEHOLDER:
898 101 100       306 if ( $p{inline_placeholder}) {
899 4 50       11 bailout $S, "cannot inline undefined value" unless defined $placeholder;
900 4         15 return $placeholder;
901             } else {
902 97         260 return placeholder_value($S, $placeholder);
903             }
904             }
905              
906             sub placeholder_value
907             {
908 97     97 0 209 my ($S, $val) = @_;
909 97         154 my $pos = @{$S->{values}};
  97         219  
910 97         185 push @{$S->{values}}, $val;
  97         254  
911 97         425 return DBIx::Perlish::Placeholder->new($S, $pos);
912             }
913              
914             ## XXX above this point 80.parse_bad.t did not go
915              
916             sub parse_simple_term
917             {
918 16     16 0 38 my ($S, $op) = @_;
919 16 100       35 if (my ($const,$sv) = is_const($S, $op)) {
    100          
920 9         125 return $const;
921             } elsif (my ($val, $ok) = get_value($S, $op, soft => 1)) {
922 4         14 return $val;
923             } else {
924 3         11 bailout $S, "cannot reconstruct simple term from operation \"",
925             $op->name, '"';
926             }
927             }
928              
929             sub parse_simple_eval
930             {
931 6     6 0 15 my ($S, $op) = @_;
932 6 100       14 if (my ($const,$sv) = is_const($S, $op)) {
    50          
933 5         18 return $const;
934             } elsif (my ($val, $ok) = get_value($S, $op, eval => 1)) {
935 1         5 return $val;
936             } else {
937 0         0 bailout $S, "cannot reconstruct simple term from operation \"",
938             $op->name, '"';
939             }
940             }
941              
942             sub get_gv
943             {
944 83     83 0 266 my ($S, $op, %p) = @_;
945              
946 83         155 my ($gv_on_pad, $gv_idx);
947 83 50 66     1529 if (is_svop($op, "gv") || is_svop($op, "gvsv")) {
    0 0        
948 83         289 $gv_idx = $op->targ;
949             } elsif (is_padop($op, "gv") || is_padop($op, "gvsv")) {
950 0         0 $gv_idx = $op->padix;
951 0         0 $gv_on_pad = 1;
952             } else {
953 0         0 goto BAIL_OUT;
954             }
955 83 50       1763 goto BAIL_OUT unless is_null($op->sibling);
956              
957 83 50       400 my $gv = $gv_on_pad ? "" : $op->sv;
958 83 50 33     381 if (!$gv || !$$gv) {
959 0         0 $gv = $S->{padlist}->[1]->ARRAYelt($gv_idx);
960             }
961 83 100 66     539 if ( $p{get_name} && $gv->isa("B::IV")) {
962 81         315 my $subref = $gv->object_2svref;
963 81 50 33     282 if (ref($subref) eq 'REF' && ref($$subref) eq 'CODE') {
964 0         0 my $cv = B::svref_2object($$subref);
965 0         0 return $cv->NAME_HEK;
966             }
967             }
968 83 50       259 goto BAIL_OUT unless $gv->isa("B::GV");
969 83 100       499 return $p{get_name} ? $gv->NAME : $gv;
970             BAIL_OUT:
971 0 0       0 bailout $S, "unable to get GV from \"", $op->name, "\"" if $p{bailout};
972 0         0 return;
973             }
974              
975 81     81 0 254 sub get_gv_name { get_gv(@_, get_name => 1) }
976              
977             sub try_get_subselect
978             {
979 44     44 0 86 my ($S, $sub) = @_;
980              
981 44 50       900 return unless is_unop($sub, "entersub");
982 44 50       868 $sub = $sub->first if is_unop($sub->first, "null");
983 44 50       191 return unless is_pushmark_or_padrange($sub->first);
984              
985 44         218 my $rg = $sub->first->sibling;
986 44 50       853 return if is_null($rg);
987 44         161 my $dbfetch = $rg->sibling;
988 44 100       876 return if is_null($dbfetch);
989 43 50       845 return unless is_null($dbfetch->sibling);
990              
991 43 100 66     797 return unless is_unop($rg, "refgen") || is_unop($rg, "srefgen");
992 37 50       714 $rg = $rg->first if is_unop($rg->first, "null");
993 37         142 my $codeop = $rg->first;
994 37 50       84 $codeop = $codeop->sibling if is_pushmark_or_padrange($codeop);
995 37 50       672 return unless is_svop($codeop, "anoncode");
996              
997 37 50       739 $dbfetch = $dbfetch->first if is_unop($dbfetch->first, "null");
998 37         140 $dbfetch = $dbfetch->first;
999 37 50 50     95 return unless (get_gv_name($S, $dbfetch) // '') eq 'subselect';
1000              
1001 37         111 return $codeop;
1002             }
1003              
1004             sub try_parse_funcall
1005             {
1006 6     6 0 27 my ($S, $sub, %opt) = @_;
1007 6   100     30 $opt{select} //= 1;
1008 6         10 my $fn;
1009 6         21 my $sql = try_funcall($S, $sub, only_normal_funcs => 1, func_name_return => \$fn);
1010 6 50       18 return unless $sql;
1011 6 100 50     29 if (($S->{gen_args}->{flavor}||"") eq "oracle") {
    100          
1012 3         4 my $cast;
1013 3 50       20 if ($cast = $S->{gen_args}{quirks}{oracle_table_func_cast}{$fn}) {
1014 0         0 $sql = "cast($sql as $cast)";
1015             }
1016 3         17 $sql = "table($sql)";
1017 3 100       12 $sql = "select * from $sql" if $opt{select};
1018             } elsif ($opt{select}) {
1019             # XXX we know this works in postgres, what about the rest?
1020 2         6 $sql = "select $sql";
1021             }
1022 6         24 return $sql;
1023             }
1024              
1025             sub in_list
1026             {
1027 10     10 0 23 my ( $S, $sop, $list ) = @_;
1028 10   50     24 $list //= [];
1029              
1030 10         17 my $sql = '';
1031 10         56 my $left = parse_term($S, $sop->first, not_after => 1);
1032 10 50       31 return '1=0' unless @$list;
1033              
1034             my $arg_limit =
1035             $S->{gen_args}->{in_arg_limit} //
1036 10 50 50     61 ((($S->{gen_args}->{flavor}||"") eq "oracle") ? 500 : 2_000_000_000);
      33        
1037 10         25 my @args = @$list;
1038 10         27 while ( @args ) {
1039 10         15 my @placeholders;
1040 10         29 for my $val ( splice(@args, 0, $arg_limit) ) {
1041 30 100 50     85 if (( ref($val) // '') =~ /SCALAR/) {
1042 7         15 push @placeholders, $$val;
1043             } else {
1044 23         36 push @placeholders, '?';
1045 23         29 push @{$S->{values}}, $val;
  23         52  
1046             }
1047             }
1048 10 0       26 $sql .= $left =~ / not$/ ? ' and ' : ' or ' if length $sql;
    50          
1049 10         58 $sql .= "$left in (" . join(',', @placeholders) . ")";
1050             }
1051              
1052 10         42 return $sql;
1053             }
1054              
1055             sub try_parse_subselect
1056             {
1057 19     19 0 43 my ($S, $sop) = @_;
1058 19         38 my $sql;
1059             my @vals;
1060              
1061 19         89 my $sub = $sop->last->first;
1062              
1063 19 100 66     416 if (is_op($sub, "padav")) {
    100 33        
    100 33        
      66        
1064 6         27 my $ary = get_padlist_scalar($S, $sub->targ, "ref only");
1065 6         20 return in_list( $S, $sop, $ary);
1066             } elsif (is_unop($sub, "rv2av") && is_op($sub->first, "padsv")) {
1067 1         9 my $ary = get_padlist_scalar($S, $sub->first->targ, "ref only");
1068 1   50     4 return in_list( $S, $sop, ${ $ary // \[] });
  1         6  
1069             } elsif (is_listop($sub, "anonlist") or
1070             is_unop($sub, "srefgen") &&
1071             is_unop($sub->first, "null") &&
1072             is_listop($sub->first->first, "anonlist"))
1073             {
1074 3         7 my @what;
1075 3 50       54 my $alist = is_listop($sub, "anonlist") ? $sub : $sub->first->first;
1076 3         9 for my $v (get_all_children($alist)) {
1077 12 100       23 next if is_pushmark_or_padrange($v);
1078 9 100       28 if (my ($const,$sv) = is_const($S, $v)) {
1079 7 50 33     54 if (
      0        
      33        
1080             ($sv->isa("B::IV") && !$sv->isa("B::PVIV")) ||
1081             ($sv->isa("B::NV") && !$sv->isa("B::PVNV"))
1082             ) {
1083 7         21 push @what, \$const;
1084             } else {
1085 0         0 push @what, $const;
1086             }
1087             } else {
1088 2         8 my ($val, $ok) = get_value($S, $v);
1089 2         6 push @what, $val;
1090             }
1091             }
1092 3         10 return in_list($S, $sop, \@what);
1093             } else {
1094 9         30 my $codeop = try_get_subselect( $S, $sub);
1095 9 100       35 if ($codeop) {
1096 5         17 $sql = handle_subselect($S, $codeop);
1097             } else {
1098 4         14 $sql = try_parse_funcall($S, $sub);
1099             }
1100 7 50       22 bailout $S, "unsupported syntax in subselect" unless $sql;
1101             }
1102              
1103 7         52 my $left = parse_term($S, $sop->first, not_after => 1);
1104 7         15 push @{$S->{values}}, @vals;
  7         22  
1105 7         31 return "$left in ($sql)";
1106             }
1107              
1108             sub handle_subselect
1109             {
1110 10     10 0 36 my ($S, $codeop, %p) = @_;
1111              
1112 10         40 my $cv = $codeop->sv;
1113 10 50       28 if (!$$cv) {
1114 10         60 $cv = $S->{padlist}->[1]->ARRAYelt($codeop->targ);
1115             }
1116 10         26 my $subref = $cv->object_2svref;
1117              
1118 10         19 my %gen_args = %{$S->{gen_args}};
  10         56  
1119 10         27 $gen_args{prev_S} = $S;
1120 10 50       28 if ($gen_args{prefix}) {
1121 0         0 $gen_args{prefix} = "$gen_args{prefix}_$S->{subselect}";
1122             } else {
1123 10         25 $gen_args{prefix} = $S->{subselect};
1124             }
1125 10         26 $S->{subselect}++;
1126 10         111 my ($sql, $vals, $nret, %flags) = DBIx::Perlish::gen_sql($subref, "select",
1127             %gen_args);
1128 10 100 100     66 if ($nret != 1 && !$p{returns_dont_care} && !$flags{returns_dont_care}) {
      66        
1129 2         6 bailout $S, "subselect query sub must return exactly one value\n";
1130             }
1131              
1132 8         48 push @{$S->{values}}, @$vals;
  8         22  
1133 8         35 return $sql;
1134             }
1135              
1136             sub parse_assign
1137             {
1138 25     25 0 87 my ($S, $op) = @_;
1139 25 50 66     529 if (is_listop($op->last, "list") &&
      66        
1140             is_pushmark_or_padrange($op->last->first) &&
1141             is_unop($op->last->first->sibling, "entersub"))
1142             {
1143 10         53 my ($val, $ok) = get_value($S, $op->first, soft => 1);
1144 10 100 33     55 if ($ok) {
    100          
    100          
    50          
1145 4         29 my $tab = try_parse_attr_assignment($S,
1146             $op->last->first->sibling, $val);
1147 4 50       15 return if $tab;
1148             } elsif ($val = is_const($S, $op->first)) {
1149 1         10 my $tab = try_parse_attr_assignment($S,
1150             $op->last->first->sibling, $val);
1151 1 50       5 return if $tab;
1152             } elsif (my $codeop = try_get_subselect($S, $op->first)) {
1153 3         10 my $sql = handle_subselect($S, $codeop, returns_dont_care => 1);
1154 3         31 my $tab = try_parse_attr_assignment($S,
1155             $op->last->first->sibling, "($sql)");
1156 3 50       12 return if $tab;
1157             } elsif (
1158             is_unop( $op->first, "entersub")
1159             && ( my $sql = try_parse_funcall($S, $op->first, select => 0))
1160             ) {
1161             # my $p : table = function(1,2,3);
1162 2         22 my $tab = try_parse_attr_assignment($S,
1163             $op->last->first->sibling, $sql);
1164 2 50       9 if ( $tab ) {
1165 2         6 my $alias = $S->{var_alias}->{$tab};
1166 2         6 $S->{returns_dont_care}->{$alias} = 1;
1167 2         6 return;
1168             }
1169             }
1170             }
1171             bailout $S, "assignments are not understood in $S->{operation}'s query sub"
1172 15 100       53 unless $S->{operation} eq "update";
1173 13 100 66     296 if (is_unop($op->first, "srefgen") || is_listop($op->first, "anonhash")) {
1174 6         21 parse_multi_assign($S, $op);
1175             } else {
1176 7         24 parse_simple_assign($S, $op);
1177             }
1178             }
1179              
1180             sub parse_simple_assign
1181             {
1182 7     7 0 13 my ($S, $op) = @_;
1183              
1184 7         28 my ($tab, $f) = get_tab_field($S, $op->last, lvalue => 1);
1185 7         18 my $saved_values = $S->{values};
1186 7         37 $S->{values} = [];
1187 7         30 my $set = parse_term($S, $op->first);
1188 7         15 push @{$S->{set_values}}, @{$S->{values}};
  7         15  
  7         14  
1189 7         14 $S->{values} = $saved_values;
1190 7         9 push @{$S->{sets}}, "$f = $set";
  7         28  
1191             }
1192              
1193             sub callarg
1194             {
1195 1169     1169 0 1939 my ($S, $op) = @_;
1196 1169 100       20726 $op = $op->first if is_unop($op, "null");
1197 1169 100 66     20349 $op = $op->sibling if !is_null($op) && is_op($op, "null");
1198 1169 100       2484 return () if is_pushmark_or_padrange($op);
1199 780         1559 return $op;
1200             }
1201              
1202             sub try_funcall
1203             {
1204 389     389 0 751 my ($S, $op, %p) = @_;
1205 389         652 my @args;
1206 389 50       7089 if (is_unop($op, "entersub")) {
1207 389         1338 $op = $op->first;
1208 389 100       7201 $op = $op->first if is_unop($op, "null");
1209 389         654 while (1) {
1210 1558 100       28399 last if is_null($op);
1211 1169         2533 push @args, callarg($S, $op);
1212 1169         4232 $op = $op->sibling;
1213             }
1214 389 50       991 return unless @args;
1215 389         1127 $op = pop @args;
1216 389 100 66     7116 return unless is_svop($op, "gv") || is_padop($op, "gv");
1217 44         127 my $func = get_gv_name( $S, $op);
1218 44 50       171 return unless defined $func;
1219 44 100       136 ${$p{func_name_return}} = $func if $p{func_name_return};
  6         24  
1220 44 100       177 if ($func =~ /^(union|intersect|except)$/) {
1221 9 50       41 return if $p{only_normal_funcs};
1222 9 50 66     36 return unless @args == 1 || @args == 2;
1223 9         17 my $rg = $args[0];
1224 9 50 33     187 return unless is_unop($rg, "refgen") || is_unop($rg, "srefgen");
1225 9 50       179 $rg = $rg->first if is_unop($rg->first, "null");
1226 9         124 my $codeop = $rg->first;
1227 9 50       33 $codeop = $codeop->sibling if is_pushmark_or_padrange($codeop);
1228 9 50       165 return unless is_svop($codeop, "anoncode");
1229 9 50       29 return unless $S->{operation} eq "select";
1230 9         29 my $cv = $codeop->sv;
1231 9 50       24 if (!$$cv) {
1232 9         46 $cv = $S->{padlist}->[1]->ARRAYelt($codeop->targ);
1233             }
1234 9         24 my $subref = $cv->object_2svref;
1235 9         16 my %gen_args = %{$S->{gen_args}};
  9         60  
1236 9         27 $gen_args{prev_S} = $S; # XXX maybe different key than prevS?
1237 9         74 my ($sql, $vals, $nret) = DBIx::Perlish::gen_sql($subref, "select",
1238             %gen_args);
1239             # XXX maybe check for nret validity
1240 9         70 push @{$S->{additions}}, {
1241 9 50 33     24 type => ((($S->{gen_args}->{flavor}||"") eq "oracle" && $func eq 'except') ?
1242             'minus' : $func),
1243             sql => $sql,
1244             vals => $vals,
1245             };
1246 9 100       30 if (@args > 1) {
1247             # must be another union|intersect|except
1248 2         7 my $r = try_funcall($S, $args[1], union_or_friends => $func);
1249             # something went wrong if it is not ""
1250 1 50 33     9 return unless defined $r && $r eq "";
1251             }
1252 8         40 return "";
1253             }
1254 35 100       95 if ($p{union_or_friends}) {
1255 1         7 bailout $S, "missing semicolon after $p{union_or_friends} sub";
1256             }
1257 34 100       136 if ($func eq 'subselect') {
    100          
1258 2 50       5 return if $p{only_normal_funcs};
1259 2 50       5 return unless @args == 1;
1260 2         4 my $rg = $args[0];
1261 2 50 33     43 return unless is_unop($rg, "refgen") || is_unop($rg, "srefgen");
1262 2 50       41 $rg = $rg->first if is_unop($rg->first, "null");
1263 2         8 my $codeop = $rg->first;
1264 2 50       15 $codeop = $codeop->sibling if is_pushmark_or_padrange($codeop);
1265 2 50       37 return unless is_svop($codeop, "anoncode");
1266 2         6 my $sql = handle_subselect($S, $codeop, returns_dont_care => 1);
1267 2         9 return "exists ($sql)";
1268             } elsif ($func eq "sql") {
1269 6 50       15 return if $p{only_normal_funcs};
1270 6 50       22 return unless @args == 1;
1271             # XXX understand more complex expressions here
1272 6         11 my $sql;
1273 6 50       26 return unless $sql = parse_simple_eval($S, $args[0]);
1274 6         20 return $sql;
1275             }
1276 26 100 100     119 if ($S->{parsing_return} && $S->{aggregates}{lc $func}) {
1277 4         22 $S->{autogroup_needed} = 1;
1278 4         17 $S->{inside_aggregate} = 1;
1279             }
1280 26 100 100     119 if (!$S->{parsing_return} && $S->{aggregates}{lc $func}) {
1281 1         2 $S->{this_is_having} = 1;
1282 1         2 $S->{autogroup_needed} = 1;
1283             }
1284              
1285 26         57 my @terms = map { scalar parse_term($S, $_) } @args;
  26         60  
1286              
1287 26 100 100     124 if ($S->{parsing_return} && $S->{aggregates}{lc $func}) {
1288 4         9 $S->{inside_aggregate} = 0;
1289             }
1290              
1291             return "sysdate"
1292 26 100 50     127 if ($S->{gen_args}->{flavor}||"") eq "oracle" &&
      100        
      66        
1293             lc $func eq "sysdate" && !@terms;
1294 25 100 100     83 if (lc $func eq "extract" && @terms == 2) {
1295 3 100       30 if (UNIVERSAL::isa($terms[0], "DBIx::Perlish::Placeholder")) {
1296 2         10 my $val = $terms[0]->undo;
1297 2         24 @terms = ("$val from $terms[1]");
1298             }
1299             }
1300 25 100 66     83 if (lc($func) eq 'cast' && @terms == 2) {
1301 2 50       10 $terms[1] = $terms[1]->undo if UNIVERSAL::isa($terms[1], "DBIx::Perlish::Placeholder");
1302 2         15 return "cast($terms[0] as $terms[1])";
1303             }
1304 23         172 return "$func(" . join(", ", @terms) . ")";
1305             }
1306             }
1307              
1308             sub parse_multi_assign
1309             {
1310 6     6 0 13 my ($S, $op) = @_;
1311              
1312 6         19 my $hashop = $op->first;
1313 6 50       140 unless (is_listop($hashop, "anonhash")) {
1314 0         0 want_unop($S, $hashop, "srefgen");
1315 0         0 $hashop = $hashop->first;
1316 0         0 $hashop = $hashop->first while is_unop($hashop, "null");
1317             }
1318 6         129 want_listop($S, $hashop, "anonhash");
1319              
1320 6         21 my $saved_values = $S->{values};
1321 6         11 $S->{values} = [];
1322              
1323 6         11 my $want_const = 1;
1324 6         8 my $field;
1325 6         13 for my $c (get_all_children($hashop)) {
1326 22 100       47 next if is_pushmark_or_padrange($c);
1327              
1328 16 100       32 if ($want_const) {
1329 10         16 my $hash;
1330 10 100 66     166 if (is_op($c, "padhv")) {
    100          
1331 2         17 $hash = $S->{padlist}->[1]->ARRAYelt($c->targ)->object_2svref;
1332             } elsif (is_unop($c, "rv2hv") && is_op($c->first, "padsv")) {
1333 2         16 $hash = $S->{padlist}->[1]->ARRAYelt($c->first->targ)->object_2svref;
1334 2         5 $hash = $$hash;
1335             }
1336 10 100       26 if ($hash) {
1337 4         26 while (my ($k, $v) = each %$hash) {
1338 8         15 push @{$S->{set_values}}, $v;
  8         16  
1339 8         11 push @{$S->{sets}}, "$k = ?";
  8         36  
1340             }
1341             } else {
1342 6         21 $field = want_const($S, $c);
1343 6         42 $want_const = 0;
1344             }
1345             } else {
1346 6         18 my $set = parse_term($S, $c);
1347 6         10 push @{$S->{set_values}}, @{$S->{values}};
  6         12  
  6         15  
1348 6         19 push @{$S->{sets}}, "$field = $set";
  6         21  
1349 6         14 $S->{values} = [];
1350 6         9 $want_const = 1;
1351 6         12 $field = undef;
1352             }
1353             }
1354              
1355 6         17 $S->{values} = $saved_values;
1356              
1357 6         27 $op = $op->last;
1358              
1359 6         8 my $tab;
1360 6 100       137 if (is_op($op, "padsv")) {
    50          
1361 5         44 my $var = get_var($S, $op);
1362 5         24 $tab = $S->{vars}{$var};
1363             } elsif (is_unop($op, "entersub")) {
1364 0         0 $op = $op->first;
1365 0 0       0 $op = $op->first if is_unop($op, "null");
1366 0 0       0 $op = $op->sibling if is_pushmark_or_padrange($op);
1367 0 0       0 $op = $op->first if is_unop($op, "rv2cv");
1368 0         0 my $gv = get_gv_name($S, $op);
1369 0 0       0 $tab = $gv if defined $gv;
1370             }
1371 6 100       28 bailout $S, "cannot get a table to update" unless $tab;
1372             }
1373              
1374             my %binop_map = (
1375             eq => "=",
1376             seq => "=",
1377             ne => "<>",
1378             sne => "<>",
1379             slt => "<",
1380             gt => ">",
1381             sgt => ">",
1382             le => "<=",
1383             sle => "<=",
1384             ge => ">=",
1385             sge => ">=",
1386             add => "+",
1387             subtract => "-",
1388             multiply => "*",
1389             divide => "/",
1390             concat => "||",
1391             pow => "^",
1392             );
1393             my %binop2_map = (
1394             add => "+",
1395             subtract => "-",
1396             multiply => "*",
1397             divide => "/",
1398             concat => "||",
1399             multiconcat => "||",
1400             pow => "^",
1401             );
1402              
1403             sub parse_expr
1404             {
1405 247     247 0 500 my ($S, $op) = @_;
1406 247         344 my $sqlop;
1407 247 100 100     1652 if (($op->flags & B::OPf_STACKED) &&
      100        
      66        
1408             !$S->{parsing_return} &&
1409             $binop2_map{$op->name} &&
1410             is_unop($op->first, "entersub"))
1411             {
1412             #printf STDERR "entersub flags: %08x\n", $op->first->flags;
1413             #printf STDERR "entersub private flags: %08x\n", $op->first->private;
1414 11         23 my $is_lvalue;
1415 11 50       69 if ($op->first->private & 128) {
1416 11         20 $is_lvalue = 1;
1417             } else {
1418 0         0 my $lc = $op->first->first;
1419 0         0 $lc = $lc->sibling until is_null($lc->sibling);
1420 0         0 $is_lvalue = is_unop($lc, "rv2cv");
1421             }
1422 11 50       28 if ($is_lvalue) {
1423 11         45 my ($tab, $f) = get_tab_field($S, $op->first, lvalue => 1);
1424             bailout $S, "self-modifications are not understood in $S->{operation}'s query sub"
1425 11 100       41 unless $S->{operation} eq "update";
1426             bailout $S, "self-modifications inside an expression is illegal"
1427 10 100       31 if $S->{in_term};
1428 9         38 my $saved_values = $S->{values};
1429 9         21 $S->{values} = [];
1430 9         15 my $set;
1431 9 50       183 if ( is_unop_aux($op, 'multiconcat')) {
1432 0         0 my $v;
1433 0         0 ($set, $v) = try_special_concat($S, $op, multiconcat => { skip_first => 1 });
1434 0 0       0 bailout $S, "unsupported multiconcat" unless $set;
1435 0         0 push @{$S->{values}}, @$v;
  0         0  
1436             } else {
1437 9         61 $set = parse_term($S, $op->last);
1438             }
1439 9         18 push @{$S->{set_values}}, @{$S->{values}};
  9         20  
  9         23  
1440 9         19 $S->{values} = $saved_values;
1441 9 50       37 if ($op->name eq "pow") {
1442 0   0     0 my $flavor = lc($S->{gen_args}->{flavor} || '');
1443 0 0 0     0 if ($flavor eq "pg" || $flavor eq "pglite") {
1444 0         0 push @{$S->{sets}}, "$f = pow($f, $set)";
  0         0  
1445             } else {
1446 0         0 bailout $S, "exponentiation is not supported for $flavor DB driver";
1447             }
1448             } else {
1449 9         16 push @{$S->{sets}}, "$f = $f $binop2_map{$op->name} $set";
  9         110  
1450             }
1451 9         45 return ();
1452             }
1453             }
1454 236 100 66     4427 if (is_binop($op, "concat") || is_unop_aux($op, "multiconcat")) {
1455 13         40 my ($c, $v) = try_special_concat($S, $op);
1456 11 50       29 if ($c) {
1457 11         18 push @{$S->{values}}, @$v;
  11         39  
1458 11         36 return $c;
1459             }
1460             }
1461 223 100       1238 if ($sqlop = $binop_map{$op->name}) {
    100          
    100          
1462 173         875 my $left = parse_term($S, $op->first);
1463 172         836 my $right = parse_term($S, $op->last);
1464 171 100 100     691 if ($sqlop eq "=" || $sqlop eq "<>") {
1465 144 100       355 my $not = $sqlop eq "<>" ? " not" : "";
1466 144 100       713 if ($right eq "null") {
    100          
1467 4         17 return "$left is$not null";
1468             } elsif ($left eq "null") {
1469 4         17 return "$right is$not null";
1470             }
1471             }
1472 163 100       753 if ($op->name eq "pow") {
1473 2   50     10 my $flavor = lc($S->{gen_args}->{flavor} || '');
1474 2 100 66     18 if ($flavor eq "pg" || $flavor eq "pglite") {
1475 1         25 return "pow($left, $right)";
1476             } else {
1477 1         6 bailout $S, "exponentiation is not supported for $flavor DB driver";
1478             }
1479             }
1480 161         663 return "$left $sqlop $right";
1481             } elsif ($op->name eq "lt") {
1482 24 100       562 if (is_unop($op->last, "negate")) {
1483 19         78 my $r = try_parse_subselect($S, $op);
1484 17 50       83 return $r if $r;
1485             }
1486             # if the "subselect theory" fails, try a normal binop
1487 5         31 my $left = parse_term($S, $op->first);
1488 5         30 my $right = parse_term($S, $op->last);
1489 5         27 return "$left < $right";
1490             } elsif ($op->name eq "sassign") {
1491 25         103 parse_assign($S, $op);
1492 21         86 return ();
1493             } else {
1494 1         5 BAILOUT:
1495             bailout $S, "unsupported binop " . $op->name;
1496             }
1497             }
1498              
1499             sub parse_multiconcat
1500             {
1501 0     0 0 0 my ( $S, $aux, %opt) = @_;
1502 0         0 my @concats;
1503              
1504 0         0 my @args = ($aux->first);
1505 0   0     0 push @args, $args[-1]->sibling while !is_null($args[-1]) && !is_null($args[-1]->sibling);
1506 0   0     0 shift @args while @args && is_op($args[0], 'null');
1507 0 0       0 shift @args if $opt{skip_first};
1508              
1509 0         0 my ($nargs, $pv, @lengths) = $aux->aux_list($S->{curr_cv});
1510 0         0 while ( defined (my $l = shift @lengths )) {
1511 0 0       0 if ( $l >= 0 ) {
1512 0         0 my $str = substr( $pv, 0, $l, '');
1513 0         0 push @concats, { str => $str };
1514             }
1515 0 0       0 my $op = shift(@args) or last;
1516 0 0       0 if ( $opt{eval}) {
1517 0         0 my ($rv, $ok) = get_value($S, $op, soft => 1, eval => 1);
1518 0 0       0 bailout $S, "cannot parse expression (near $pv)" unless $ok;
1519 0         0 push @concats, { str => $rv };
1520             } else {
1521 0         0 push @concats, { op => $op };
1522             }
1523             }
1524              
1525 0         0 return @concats;
1526             }
1527              
1528             sub try_special_concat
1529             {
1530 83     83 0 216 my ($S, $op, %opt) = @_;
1531 83         138 my @terms;
1532             my $str;
1533 83 100       1618 if (is_binop($op, "concat")) {
    100          
    100          
    100          
    50          
    100          
    100          
    50          
    50          
1534 33         274 my @t = try_special_concat($S, $op->first, terms_only => 1);
1535 31 50       83 return () unless @t;
1536 31         59 push @terms, @t;
1537 31         131 @t = try_special_concat($S, $op->last, terms_only => 1);
1538 29 50       76 return () unless @t;
1539 29         55 push @terms, @t;
1540             } elsif (($str = is_const($S, $op))) {
1541 26         79 push @terms, {str => $str};
1542             } elsif (is_unop($op, "null")) {
1543 4         19 $op = $op->first;
1544 4         82 while (!is_null($op)) {
1545 6         45 my @t = try_special_concat($S, $op, terms_only => 1);
1546 4 50       14 return () unless @t;
1547 4         8 push @terms, @t;
1548 4         89 $op = $op->sibling;
1549             }
1550             } elsif (is_op($op, "null")) {
1551 2         11 return {skip => 1};
1552             } elsif (is_binop($op, "helem")) {
1553 0         0 my $f = is_const($S, $op->last);
1554 0 0       0 return () unless $f;
1555 0         0 $op = $op->first;
1556 0 0       0 return () unless is_unop($op, "rv2hv");
1557 0         0 $op = $op->first;
1558 0 0       0 return () unless is_op($op, "padsv");
1559 0         0 my $tab = find_aliased_tab($S, $op);
1560 0 0       0 return () unless $tab;
1561 0         0 push @terms, {tab => $tab, field => $f};
1562             } elsif (is_unop($op, "entersub")) {
1563 6         12 my ($t, $f) = eval { get_tab_field($S, $op) };
  6         17  
1564 6 50       17 return () unless $f;
1565 6         23 push @terms, {tab => $t, field => $f};
1566             } elsif (is_op($op, "padsv")) {
1567 10         38 my $tab = find_aliased_tab($S, $op);
1568 10 50       33 return () unless $tab;
1569 10         40 push @terms, {tab => $tab};
1570             } elsif (is_unop_aux($op, "multiconcat")) {
1571 0   0     0 my @subterms = parse_multiconcat($S, $op, %{ $opt{multiconcat} // {}});
  0         0  
1572 0 0       0 return () unless @subterms;
1573 0         0 for my $st (@subterms) {
1574 0 0       0 if ( defined $st->{str} ) {
1575 0         0 push @terms, $st;
1576             } else {
1577 0         0 my @t = try_special_concat($S, $st->{op}, terms_only => 1);
1578 0 0       0 return () unless @t;
1579 0         0 push @terms, @t;
1580             }
1581             }
1582             } elsif (is_unop_aux($op, "multideref")) {
1583 2         13 push @terms, { str => parse_multideref($S, $op ) };
1584             } else {
1585 0         0 return ();
1586             }
1587 73 100       254 return @terms if $opt{terms_only};
1588 11         20 $str = "";
1589 11         21 my @sql;
1590             my @v;
1591 11         26 @terms = grep { !$_->{skip} } @terms;
  42         91  
1592 11         30 while (@terms) {
1593 30         56 my $t = shift @terms;
1594 30 100       104 if (exists $t->{str}) {
    100          
1595 14         48 $str .= $t->{str};
1596             } elsif (exists $t->{field}) {
1597 6 100       19 if (length($str)) {
1598 4         10 push @v, $str;
1599 4         9 push @sql, '?';
1600             }
1601 6 50 33     31 if ($S->{operation} eq "delete" || $S->{operation} eq "update") {
1602 0         0 push @sql, $t->{field};
1603             } else {
1604 6         21 push @sql, "$t->{tab}.$t->{field}";
1605             }
1606 6         19 $str = "";
1607             } else {
1608 10         21 my $t2 = shift @terms;
1609 10 50       27 return () unless $t2;
1610 10 50       24 return () unless defined $t2->{str};
1611 10 50       81 return () unless $t2->{str} =~ s/^->(\w+)//;
1612 10         36 my $f = $1;
1613 10 100       28 if (length($str)) {
1614 9         19 push @v, $str;
1615 9         23 push @sql, '?';
1616             }
1617 10 50 33     52 if ($S->{operation} eq "delete" || $S->{operation} eq "update") {
1618 0         0 push @sql, $f;
1619             } else {
1620 10         34 push @sql, "$t->{tab}.$f";
1621             }
1622 10         44 $str = $t2->{str};
1623             }
1624             }
1625 11 100       33 if (length($str)) {
1626 10         21 push @v, $str;
1627 10         17 push @sql, '?';
1628             }
1629 11         17 my $sql;
1630 11 100 50     53 if (lc($S-> {gen_args}-> {flavor} || '') eq "mysql") {
1631 5         25 $sql = "concat(" . join(", ", @sql) . ")";
1632             } else {
1633 6         20 $sql = join " || ", @sql;
1634             }
1635 11         47 return ($sql, \@v);
1636             }
1637              
1638             sub parse_entersub
1639             {
1640 213     213 0 476 my ($S, $op) = @_;
1641 213         514 my $tab = try_parse_attr_assignment($S, $op);
1642 213 100       818 return () if $tab;
1643 10         37 return scalar parse_term($S, $op);
1644             }
1645              
1646             sub parse_complex_regex
1647             {
1648 8     8 0 19 my ($S, $op) = @_;
1649              
1650 8 50       143 if (is_unop($op, "regcreset")) {
    50          
    100          
    100          
    50          
1651 0 0       0 if (is_unop($op->first, "null")) {
1652 0         0 my $rx = "";
1653 0         0 my $rxop = $op->first->first;
1654 0         0 while (!is_null($rxop)) {
1655 0 0       0 $rx .= parse_complex_regex($S, $rxop)
1656             unless is_pushmark_or_padrange($rxop);
1657 0         0 $rxop = $rxop->sibling;
1658             }
1659 0         0 return $rx;
1660             } else {
1661 0         0 return parse_complex_regex( $S, $op-> first);
1662             }
1663             } elsif ( is_binop( $op, 'concat')) {
1664 0         0 $op = $op-> first;
1665             return
1666 0         0 parse_complex_regex( $S, $op) .
1667             parse_complex_regex( $S, $op-> sibling)
1668             ;
1669             } elsif ( is_svop( $op, 'const')) {
1670 2         8 return want_const( $S, $op);
1671             } elsif (my ($rx, $ok) = get_value($S, $op, soft => 1)) {
1672 4 100       12 return undef unless $rx;
1673 3         6 $rx =~ s/^\(\?\-\w*\:(.*)\)$/$1/; # (?-xism:moo) -> moo
1674 3         9 return $rx;
1675             } elsif (is_unop($op, "null")) {
1676 2         4 my $rx = "";
1677 2         7 my $rxop = $op->first;
1678 2         38 while (!is_null($rxop)) {
1679 6 100       17 $rx .= parse_complex_regex($S, $rxop)
1680             unless is_pushmark_or_padrange($rxop);
1681 6         134 $rxop = $rxop->sibling;
1682             }
1683 2         7 return $rx;
1684             } else {
1685 0         0 bailout $S, "unsupported op " . ref($op) . '/' . $op->name;
1686             }
1687             }
1688              
1689             sub parse_regex
1690             {
1691 20     20 0 42 my ( $S, $op, $neg) = @_;
1692 20         172 my ( $like, $case) = ( $op->precomp, $op-> pmflags & B::PMf_FOLD);
1693              
1694 20 100       85 unless ( defined $like) {
1695 4         21 my $logop = $op-> first-> sibling;
1696 4 50 33     90 bailout $S, "strange regex " . $op->name
1697             unless $logop and is_logop( $logop, 'regcomp');
1698 4         27 $like = parse_complex_regex( $S, $logop-> first);
1699 4 100       19 return "" unless defined $like; # explicitly nulled like
1700             }
1701              
1702 19         118 my $lhs = parse_term($S, $op->first);
1703              
1704 19   50     96 my $flavor = lc($S-> {gen_args}-> {flavor} || '');
1705 19         35 my $what = 'like';
1706              
1707 19         50 $like =~ s/\(\?\^\w+\:((?:[^\\]|\\.)*)\)/$1/g; # ignore ?^flags:
1708              
1709 19         95 my $can_like = $like =~ /^\^?(?:[-!%\s\w]|\\.)*\$?$/; # like that begins with non-% can use indexes
1710              
1711 19 50 66     128 if ( $flavor eq 'mysql') {
    100          
    50          
1712             # mysql LIKE is case-insensitive
1713 0 0 0     0 goto LIKE if not $case and $can_like;
1714 0         0 $like =~ s/'/''/g;
1715              
1716             return
1717 0 0       0 "$lhs ".
    0          
1718             ( $neg ? 'not ' : '') .
1719             'regexp ' .
1720             ( $case ? '' : 'binary ') .
1721             "'$like'"
1722             ;
1723             } elsif ( $flavor eq 'pg' || $flavor eq "pglite") {
1724             # LIKE is case-sensitive
1725 18 50       44 if ( $can_like) {
1726 18 100       36 $what = 'ilike' if $case;
1727 18         175 goto LIKE;
1728             }
1729 0         0 $like =~ s/'/''/g;
1730             return
1731 0 0       0 "$lhs ".
    0          
1732             ( $neg ? '!' : '') .
1733             '~' .
1734             ( $case ? '*' : '') .
1735             " '$like'"
1736             ;
1737             } elsif ($flavor eq "sqlite") {
1738             # SQLite as it is now is a bit tricky:
1739             # - there is support for REGEXP with a func provided the user
1740             # supplies his own function;
1741             # - LIKE is case-insensitive (for ASCII, anyway, there's a bug there);
1742             # - GLOB is case-sensitive;
1743             # - there is also support for MATCH - with a user func
1744             # - except that in recent version it is used for FTS
1745             # Since it does not appear that SQLite can use indices
1746             # for prefix matches with simple LIKE statements, we
1747             # just use user-defined functions PRE_N and PRE_I for
1748             # case-sensitive and case-insensitive cases.
1749             # If I am wrong on that, or if SQLite gets and ability to
1750             # do index-based prefix matching, this logic can be
1751             # modified accordingly in at a future date.
1752 0 0       0 if ($case) {
1753 0         0 $what = "pre_i";
1754             $S->{gen_args}->{dbh}->func($what, 2, sub {
1755 0     0   0 return scalar $_[1] =~ /$_[0]/i;
1756 0         0 }, "create_function");
1757             } else {
1758 0         0 $what = "pre_n";
1759             $S->{gen_args}->{dbh}->func($what, 2, sub {
1760 0     0   0 return scalar $_[1] =~ /$_[0]/;
1761 0         0 }, "create_function");
1762             }
1763 0         0 push @{$S->{values}}, $like;
  0         0  
1764             # $what = $neg ? "not $what" : $what;
1765             # return "$lhs $what ?";
1766 0 0       0 return ($neg ? "not " : "") . "$what(?, $lhs)";
1767             } else {
1768             # XXX is SQL-standard LIKE case-sensitive or not?
1769 1 50       3 if ($case) {
1770 1         4 $lhs = "lower($lhs)";
1771 1         2 $like = lc $like;
1772             }
1773 1 50       5 bailout $S, "Regex too complex for implementation using LIKE keyword: $like"
1774             if $like =~ /(?
1775 19         40 LIKE:
1776             $like =~ s/'/''/g;
1777 19         67 $like =~ s/\\([^A-Za-z_0-9])/$1/g; # de-quotemeta
1778 19         36 my $escape = "";
1779 19 50 66     62 if ($flavor eq "pg" || $flavor eq "oracle") {
1780             # XXX it is possible that more flavors support like...escape
1781 19         27 my $need_esc;
1782 19 100       47 $need_esc = 1 if $like =~ s/!/!!/g;
1783 19 100       48 $need_esc = 1 if $like =~ s/%/!%/g;
1784 19 100       42 $need_esc = 1 if $like =~ s/_/!_/g;
1785 19 100       46 $escape = " escape '!'" if $need_esc;
1786             } else {
1787 0         0 $like =~ s/%/\\%/g;
1788 0         0 $like =~ s/_/\\_/g;
1789             }
1790 19         40 $like =~ s/\.\*/%/g;
1791 19         30 $like =~ s/(?
1792 19         32 $like =~ s/\\\././g;
1793 19 100       69 $like = "%$like" unless $like =~ s|^\^||;
1794 19 100       59 $like = "$like%" unless $like =~ s|\$$||;
1795 19 100       111 return "$lhs " .
1796             ( $neg ? 'not ' : '') .
1797             "$what '$like'$escape"
1798             ;
1799             }
1800             }
1801              
1802             my %join_map = (
1803             bit_and => "inner",
1804             multiply => "inner",
1805             repeat => "inner",
1806             bit_or => "full outer",
1807             add => "full outer",
1808             lt => "left outer",
1809             gt => "right outer",
1810             );
1811              
1812             sub parse_join
1813             {
1814 40     40 0 88 my ($S, $op) = @_;
1815 40         90 my @op = get_all_children( $op);
1816              
1817              
1818             # allow 2-arg syntax for cross joins:
1819             # join $a * $b
1820             # and 3-arg syntax for all other joins:
1821             # join $a * $b => subselect { ... }
1822 40 50 66     237 bailout $S, "not a valid join() syntax"
      66        
      66        
1823             unless 2 <= @op and 3 >= @op and
1824             is_pushmark_or_padrange($op[0]) and
1825             is_binop( $op[1]);
1826 37         83 my $jointype;
1827            
1828 37 100       194 if ($op[1]-> name eq 'le') {
1829             # support <= as well as =>
1830 6 100       32 bailout $S, "not a valid join() syntax"
1831             unless @op == 2;
1832 5         38 @op[1,2] = ( $op[1]-> first, $op[1]-> last);
1833 5 50       97 bailout $S, "not a valid join() syntax"
1834             unless is_binop( $op[1]);
1835             }
1836              
1837 36 100       104 if ( 2 == @op) {
1838             bailout $S, "not a valid join() syntax: one of &,*,x is expected"
1839             unless
1840             exists $join_map{ $op[1]-> name } and
1841 4 100 66     36 $join_map{ $op[1]-> name } eq 'inner';
1842 3         6 $jointype = 'cross';
1843             } else {
1844             bailout $S, "not a valid join() syntax: one of &,|,x,+,*,<,> is expected"
1845 32 100       152 unless exists $join_map{ $op[1]-> name };
1846 31 100 66     611 bailout $S, "not a valid join() syntax"
1847             unless is_unop( $op[2]) and $op[2]-> name eq 'entersub';
1848 30         136 $jointype = $join_map{ $op[1]-> name };
1849             }
1850              
1851             # table names
1852 33         62 my @tab;
1853 33 100       149 $tab[0] = find_aliased_tab($S, $op[1]-> first) or
1854             bailout $S, "first argument of join() is not a table";
1855 32 100       163 $tab[1] = find_aliased_tab($S, $op[1]-> last) or
1856             bailout $S, "second argument of join() is not a table";
1857            
1858             # subselect
1859 31         63 my ( $condition, $codeop);
1860 31 100       77 if ( $op[2]) {
1861 30         89 $codeop = try_get_subselect( $S, $op[2]);
1862 30 100       72 bailout $S, "third argument to join is not a subselect expression"
1863             unless $codeop;
1864              
1865 29         103 my $cv = $codeop->sv;
1866 29 50       71 if (!$$cv) {
1867 29         134 $cv = $S->{padlist}->[1]->ARRAYelt($codeop->targ);
1868             }
1869 29         93 my $subref = $cv->object_2svref;
1870             my $S2 = init(
1871 29         41 %{$S->{gen_args}},
  29         168  
1872             operation => 'select',
1873             values => [],
1874             join_values => [],
1875             prev_S => $S,
1876             );
1877 29         76 $S2-> {alias} = $S-> {alias};
1878 29         99 parse_sub($S2, $subref);
1879             bailout $S,
1880             "join() subselect expression cannot contain anything ".
1881             "but conditional expressions on already declared tables"
1882 145         204 if scalar( grep { @{ $S2-> {$_} } } qw(
  145         434  
1883             group_by order_by autogroup_by ret_values joins
1884 29 100 100     103 )) or $S2->{alias} ne $S->{alias};
1885              
1886 25 50       40 unless ( @{ $S2->{where}||[] }) {
  25 100       90  
1887 3 100       13 bailout $S,
1888             "join() subselect expression must contain ".
1889             "at least one conditional expression"
1890             unless $jointype eq 'inner';
1891 2         19 $jointype = 'cross';
1892             } else {
1893 22         49 $condition = join(' and ', @{ $S2-> {where} });
  22         68  
1894 22         35 push @{$S->{join_values}}, @{$S2->{values}};
  22         46  
  22         188  
1895             }
1896             }
1897              
1898 25         221 return [ $jointype, @tab, $condition ];
1899             }
1900              
1901             sub try_parse_range
1902             {
1903 13     13 0 30 my ($S, $op) = @_;
1904 13 100       255 return try_parse_range($S, $op->first) if is_unop($op, "null");
1905 7 100       136 return unless is_unop($op, "flop");
1906 6         27 $op = $op->first;
1907 6 50       109 return unless is_unop($op, "flip");
1908 6         41 $op = $op->first;
1909 6 50       115 return unless is_logop($op, "range");
1910 6         40 return (parse_simple_term($S, $op->first),
1911             parse_simple_term($S, $op->first->sibling));
1912             }
1913              
1914             sub parse_or
1915             {
1916 19     19 0 42 my ($S, $op) = @_;
1917 19 100       410 if (is_op($op->first->sibling, "last")) {
    100          
1918             bailout $S, "there should be no \"last\" statements in $S->{operation}'s query sub"
1919 9 100       50 unless $S->{operation} eq "select";
1920 7         44 my ($from, $to) = try_parse_range($S, $op->first);
1921 5 100       22 bailout $S, "range operator expected" unless defined $to;
1922 4         12 $S->{offset} = $from;
1923 4         13 $S->{limit} = $to-$from+1;
1924 4         12 return;
1925             } elsif (my ($val, $ok) = get_value($S, $op->first, soft => 1)) {
1926 4         11 return compile_conditionally($S, $op, !$val);
1927             } else {
1928 6         29 my $left = parse_term($S, $op->first);
1929 6         55 my $right = parse_term($S, $op->first->sibling);
1930 6         36 return "$left or $right";
1931             }
1932             }
1933              
1934             sub parse_and
1935             {
1936 14     14 0 34 my ($S, $op) = @_;
1937 14 100       62 if (my ($val, $ok) = get_value($S, $op->first, soft => 1)) {
1938 10         28 return compile_conditionally($S, $op, $val);
1939             } else {
1940 4         26 my $left = parse_term($S, $op->first);
1941 4         31 my $right = parse_term($S, $op->first->sibling);
1942 4         22 return "$left and $right";
1943             }
1944             }
1945              
1946             sub compile_conditionally
1947             {
1948 14     14 0 30 my ($S, $op, $val) = @_;
1949 14 100       26 if ($val) {
1950 9         41 $op = $op->first->sibling;
1951             # This strangeness is for suppressing () when parsing
1952             # expr via parse_term. There must be a better way.
1953 9 100 66     188 if (is_binop($op) || $op->name eq "sassign") {
    100 33        
    50          
1954 1         4 return parse_expr($S, $op);
1955             } elsif (is_listop($op, "return")) {
1956             # conditional returns are nice
1957 6         24 parse_return($S, $op);
1958 4         14 return ();
1959             } elsif (is_listop($op, "leave") || is_listop($op, "scope")) {
1960 2         8 parse_list($S, $op);
1961 2         7 return ();
1962             } else {
1963 0         0 return scalar parse_term($S, $op);
1964             }
1965             } else {
1966 5         14 return ();
1967             }
1968             }
1969              
1970             sub parse_fieldlist_label
1971             {
1972 3     3 0 12 my ($S, $label, $lop, $op) = @_;
1973              
1974 3         6 my @op;
1975 3 100       79 if (is_listop($op, "list")) {
1976 1         5 @op = get_all_children($op);
1977             } else {
1978 2         6 push @op, $op;
1979             }
1980 3         9 for $op (@op) {
1981 5 100       25 next if is_pushmark_or_padrange($op);
1982 4         17 my ($t, $f) = get_tab_field($S, $op);
1983 4         61 push @{$S->{$label->{key}}},
1984 4 50 33     10 ($S->{operation} eq "delete" || $S->{operation} eq "update") ?
1985             $f : "$t.$f";
1986             }
1987 3         18 $S->{skipnext} = 1;
1988             }
1989              
1990             sub parse_sort
1991             {
1992 3     3 0 8 my ($S, $op) = @_;
1993 3         8 parse_orderby_label($S, "order_by", undef, $op);
1994 3         21 delete $S->{skipnext};
1995             }
1996              
1997             sub parse_orderby_label
1998             {
1999 9     9 0 25 my ($S, $label, $lop, $op) = @_;
2000              
2001 9 100       32 my $key = ref $label ? $label->{key} : $label;
2002              
2003 9         16 my @op;
2004 9 100 100     181 if (is_listop($op, "list") || is_listop($op, "sort")) {
    100          
2005 6         16 @op = get_all_children($op);
2006             } elsif ( is_unop($op, "null")) {
2007 1         5 $op = $op->first;
2008 1   66     23 while ( $op && !is_null($op)) {
2009 3         16 push @op, $op;
2010 3         71 $op = $op->sibling;
2011             }
2012             } else {
2013 2         8 push @op, $op;
2014             }
2015 9         23 my $order = "";
2016 9         36 for $op (@op) {
2017 26 100       48 next if is_pushmark_or_padrange($op);
2018             # XXX next if is_op($op, "null");
2019 20         32 my $term;
2020 20 100       40 $term = parse_term($S, $op, inline_placeholder => 1)
2021             unless $term = is_const($S, $op);
2022 20 100       100 if ($term =~ /^asc/i) {
    100          
2023 2         5 next; # skip "ascending"
2024             } elsif ($term =~ /^desc/i) {
2025 7         17 $order = "desc";
2026 7         17 next;
2027             } else {
2028 11 100       25 if ($order) {
2029 6         9 push @{$S->{$key}}, "$term $order";
  6         20  
2030 6         17 $order = "";
2031             } else {
2032 5         10 push @{$S->{$key}}, $term;
  5         22  
2033             }
2034             }
2035             }
2036 9         39 $S->{skipnext} = 1;
2037             }
2038              
2039             sub parse_numassign_label
2040             {
2041 11     11 0 25 my ($S, $label, $lop, $op) = @_;
2042              
2043             # TODO more generic values
2044 11         32 my ($const,$sv) = is_const($S, $op);
2045 11 100 66     138 if (!$sv && is_op($op, "padsv")) {
2046 6 100       16 if (find_aliased_tab($S, $op)) {
2047 2         8 bailout $S, "cannot use table variable after ", $lop->label;
2048             }
2049 4         29 $sv = $S->{padlist}->[1]->ARRAYelt($op->targ);
2050 4         9 $const = ${$sv->object_2svref};
  4         13  
2051             }
2052 9 100 33     103 bailout $S, "label ", $lop->label, " must be followed by an integer or integer variable"
      66        
      66        
2053             unless $sv && $const && $const =~ /^\d+$/ && $const > 0;
2054 7         24 $S->{$label->{key}} = $const;
2055 7         36 $S->{skipnext} = 1;
2056             }
2057              
2058             sub parse_notice_label
2059             {
2060 1     1 0 4 my ($S, $label, $lop, $op) = @_;
2061 1         6 $S->{$label->{key}}++;
2062             }
2063              
2064             sub parse_table_label
2065             {
2066 6     6 0 12 my ($S, $label, $lop, $op) = @_;
2067              
2068 6 100       30 bailout $S, "label ", $lop->label, " must be followed by an assignment"
2069             unless $op->name eq "sassign";
2070 5         25 my $attr = parse_simple_term($S, $op->first);
2071 4         12 my $varn;
2072 4 100 66     112 bailout $S, "label ", $lop->label, " must be followed by a lexical variable declaration"
2073             unless is_op($op->last, "padsv") && ($varn = padname($S, $op->last, no_fakes => 1));
2074 3         12 new_var($S, $varn, $attr);
2075 3         22 $S->{skipnext} = 1;
2076             }
2077              
2078             my $action_orderby = {
2079             kind => 'termlist',
2080             key => 'order_by',
2081             handler => \&parse_orderby_label,
2082             };
2083             my $action_groupby = {
2084             kind => 'fieldlist',
2085             key => 'group_by',
2086             handler => \&parse_fieldlist_label,
2087             };
2088             my $action_limit = {
2089             kind => 'numassign',
2090             key => 'limit',
2091             handler => \&parse_numassign_label,
2092             };
2093             my $action_offset = {
2094             kind => 'numassign',
2095             key => 'offset',
2096             handler => \&parse_numassign_label,
2097             };
2098             my $action_distinct = {
2099             kind => 'notice',
2100             key => 'distinct',
2101             handler => \&parse_notice_label,
2102             };
2103             my %labelmap = (
2104             select => {
2105             orderby => $action_orderby,
2106             order_by => $action_orderby,
2107             order => $action_orderby,
2108             sortby => $action_orderby,
2109             sort_by => $action_orderby,
2110             sort => $action_orderby,
2111              
2112             groupby => $action_groupby,
2113             group_by => $action_groupby,
2114             group => $action_groupby,
2115              
2116             limit => $action_limit,
2117              
2118             offset => $action_offset,
2119              
2120             distinct => $action_distinct,
2121             },
2122             );
2123              
2124             sub parse_labels
2125             {
2126 28     28 0 60 my ($S, $lop) = @_;
2127 28         144 my $label = $labelmap{$S->{operation}}->{lc $lop->label};
2128 28 100 100     498 if (!$label && lc $lop->label eq "table") {
2129 6         24 $label = { kind => 'table', handler => \&parse_table_label };
2130             }
2131 28 100       93 bailout $S, "label ", $lop->label, " is not understood"
2132             unless $label;
2133 27         109 my $op = $lop->sibling;
2134 27 50       93 if ($label->{handler}) {
2135 27         85 $label->{handler}->($S, $label, $lop, $op);
2136             } else {
2137 0         0 bailout $S, "internal error parsing label ", $op->label;
2138             }
2139             }
2140              
2141             sub parse_selfmod
2142             {
2143 5     5 0 13 my ($S, $op, $oper) = @_;
2144              
2145 5         11 my ($tab, $f) = get_tab_field($S, $op, lvalue => 1);
2146             bailout $S, "self-modifications are not understood in $S->{operation}'s query sub"
2147 5 100       25 unless $S->{operation} eq "update";
2148 4         24 return "$f = $f $oper";
2149             }
2150              
2151             sub where_or_having
2152             {
2153 443     443 0 1029 my ($S, @what) = @_;
2154 443 100       662 push @{$S->{$S->{this_is_having} ? "having" : "where"}}, @what;
  443         1450  
2155 443         2437 $S->{this_is_having} = 0;
2156             }
2157              
2158             sub parse_op
2159             {
2160 2534     2534 0 4326 my ($S, $op) = @_;
2161              
2162 2534 50       9848 return if $S->{seen}->{$$op}++;
2163              
2164 2534 100       5015 if ($S->{skipnext}) {
2165 19         62 delete $S->{skipnext};
2166 19         90 return;
2167             }
2168 2515 100 66     52187 if (is_listop($op, "list")) {
    100 66        
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    50          
    0          
2169 9         51 parse_list($S, $op);
2170             } elsif (is_listop($op, "lineseq")) {
2171 367         980 parse_list($S, $op);
2172             } elsif (is_binop($op, "leaveloop") &&
2173             is_loop($op->first, "enterloop") &&
2174             is_listop($op->last, "lineseq"))
2175             {
2176 8         36 parse_list($S, $op->last);
2177             } elsif (is_listop($op, "return")) {
2178 97         295 parse_return($S, $op);
2179             } elsif (is_binop($op)) {
2180 210         640 where_or_having($S, parse_expr($S, $op));
2181             } elsif (is_unop($op, "not")) {
2182 6         18 where_or_having($S, scalar parse_term($S, $op));
2183             } elsif (is_logop($op, "or")) {
2184 17         70 my $or = parse_or($S, $op);
2185 11 100       53 where_or_having($S, "($or)") if $or;
2186 11         127 $S->{this_is_having} = 0;
2187             } elsif (is_logop($op, "and")) {
2188 12         46 my $and = parse_and($S, $op);
2189 11 100       38 where_or_having($S, $and) if $and;
2190 11         81 $S->{this_is_having} = 0;
2191             } elsif (is_unop($op, "leavesub")) {
2192 373         1724 parse_op($S, $op->first);
2193             } elsif (is_unop($op, "null")) {
2194 224         1018 parse_op($S, $op->first);
2195             } elsif (is_unop($op, "defined")) {
2196 2         5 where_or_having($S, scalar parse_term($S, $op));
2197             } elsif (is_op($op, "padsv")) {
2198             # XXX Skip for now, it is either a variable
2199             # that does not represent a table, or else
2200             # it is already associated with a table in $S.
2201             } elsif (is_op($op, "last")) {
2202             bailout $S, "there should be no \"last\" statements in $S->{operation}'s query sub"
2203 2 50       11 unless $S->{operation} eq "select";
2204 0         0 $S->{limit} = 1;
2205             } elsif (is_pushmark_or_padrange($op)) {
2206             # skip
2207             } elsif (is_op($op, "enter")) {
2208             # skip
2209             } elsif (is_op($op, "null")) {
2210             # skip
2211 195         945 parse_op($S, $op->sibling);
2212             } elsif (is_cop($op, "nextstate")) {
2213 680         2327 $S->{file} = $op->file;
2214 680         1989 $S->{line} = $op->line;
2215 680         2299 $_cover->($op);
2216 680 100       3310 if ($op->label) {
2217 28         91 parse_labels($S, $op);
2218             }
2219             } elsif (is_cop($op)) {
2220             # XXX any other things?
2221 0         0 $S->{file} = $op->file;
2222 0         0 $S->{line} = $op->line;
2223             # skip
2224             } elsif (is_unop($op, "entersub")) {
2225 213         633 where_or_having($S, parse_entersub($S, $op));
2226             } elsif (is_pmop($op, "match")) {
2227 16         48 where_or_having($S, parse_regex($S, $op, 0));
2228             } elsif ( $op->name eq 'join') {
2229 40         73 push @{$S->{joins}}, parse_join($S, $op);
  40         134  
2230             } elsif ($op->name eq 'sort') {
2231 3         8 parse_sort($S, $op);
2232             } elsif (is_unop($op, "postinc")) {
2233 1         2 push @{$S->{sets}}, parse_selfmod($S, $op->first, "+ 1");
  1         6  
2234             } elsif (is_unop($op, "postdec")) {
2235 0         0 push @{$S->{sets}}, parse_selfmod($S, $op->first, "- 1");
  0         0  
2236             } elsif (is_unop($op, "preinc")) {
2237 2         4 push @{$S->{sets}}, parse_selfmod($S, $op->first, "+ 1");
  2         35  
2238             } elsif (is_unop($op, "predec")) {
2239 2         5 push @{$S->{sets}}, parse_selfmod($S, $op->first, "- 1");
  2         11  
2240             } elsif (is_listop($op, "exec")) {
2241 17         122 $S->{seen_exec}++;
2242             } elsif (is_unop_aux($op, "multiconcat")) {
2243 0         0 where_or_having($S, parse_expr($S, $op));
2244             } else {
2245 0         0 bailout $S, "don't quite know what to do with op \"" . $op->name . "\"";
2246             }
2247             }
2248              
2249             sub parse_sub
2250             {
2251 373     373 0 743 my ($S, $sub) = @_;
2252 373 50       872 if ($DEVEL) {
2253 0         0 $Carp::Verbose = 1;
2254 0         0 require B::Concise;
2255             #my $walker = B::Concise::compile('-terse', $sub);
2256 0         0 my $walker = B::Concise::compile('-concise', $sub);
2257 0         0 print "CODE DUMP:\n";
2258 0         0 $walker->();
2259 0         0 print "\n\n";
2260             }
2261 373         1359 my $root = B::svref_2object($sub);
2262 373         2112 $S->{padlist} = [$root->PADLIST->ARRAY];
2263 373         900 $S->{curr_cv} = $root;
2264 373 50       2820 $S->{padlists}->{ $root->PADLIST->id } = $S->{padlist} if $root->PADLIST->can('id');
2265 373         1434 $root = $root->ROOT;
2266 373         951 parse_op($S, $root);
2267             }
2268              
2269             sub init
2270             {
2271 373     373 0 1364 my %args = @_;
2272             my $S = {
2273             gen_args => \%args,
2274             file => '??',
2275             line => '??',
2276             subselect => 's01',
2277             operation => $args{operation},
2278             values => [],
2279             join_values => [],
2280             sets => [],
2281             set_values => [],
2282             ret_values => [],
2283             where => [],
2284             order_by => [],
2285             group_by => [],
2286             additions => [],
2287             joins => [],
2288             key_field => 1,
2289             aggregates => { avg => 1, count => 1, max => 1, min => 1, sum => 1 },
2290             autogroup_by => [],
2291             autogroup_fields => {},
2292             seen => {},
2293             padlists => $args{prev_S} ? $args{prev_S}->{padlists} : {},
2294 373 100       5629 };
2295 373 100       1275 $S->{alias} = $args{prefix} ? "$args{prefix}_t01" : "t01";
2296 373         916 $S;
2297             }
2298              
2299             # Borrowed from IO::All by Ingy döt Net.
2300             my $old_warn_handler = $SIG{__WARN__};
2301             $SIG{__WARN__} = sub {
2302             if ($_[0] !~ /^Useless use of .+ in void context/) {
2303             goto &$old_warn_handler if $old_warn_handler;
2304             warn(@_);
2305             }
2306             };
2307              
2308             $_cover = sub {};
2309             if (*Devel::Cover::coverage{CODE}) {
2310             my $Coverage = Devel::Cover::coverage(0);
2311             $_cover = sub { $Coverage->{statement}{Devel::Cover::get_key($_[0])} ||= 1 };
2312             }
2313              
2314             package
2315             DBIx::Perlish::Placeholder;
2316              
2317 26     26   35860 use overload '""' => sub { "?" }, eq => sub { "$_[0]" eq "$_[1]" };
  26     88   27007  
  26         290  
  162         831  
  70         184  
2318              
2319             sub new
2320             {
2321 97     97   222 my ($class, $S, $pos) = @_;
2322 97         678 bless { S => $S, position => $pos }, $class;
2323             }
2324              
2325             sub value
2326             {
2327 0     0   0 my $me = shift;
2328 0         0 return $me->{S}{values}[$me->{position}];
2329             }
2330              
2331             sub undo
2332             {
2333 4     4   9 my $me = shift;
2334 4         6 splice @{$me->{S}{values}}, $me->{position}, 1;
  4         44  
2335             }
2336              
2337             "the magic stops here; welcome to the real world";