|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package SQL::Abstract::FromQuery;
  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
3
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
41543
 | 
 use strict;
  | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
    | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
225
 | 
    | 
| 
4
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
27
 | 
 use warnings;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
193
 | 
    | 
| 
5
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
24
 | 
 use Scalar::Util     qw/refaddr reftype blessed/;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
619
 | 
    | 
| 
6
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
3025
 | 
 use List::MoreUtils  qw/mesh/;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
54749
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
44
 | 
    | 
| 
7
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
4331
 | 
 use Module::Load     qw/load/;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2034
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
    | 
| 
8
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
591
 | 
 use Params::Validate qw/validate SCALAR SCALARREF CODEREF ARRAYREF HASHREF
  | 
| 
9
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
3382
 | 
                                  UNDEF  BOOLEAN/;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43799
 | 
    | 
| 
10
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
2852
 | 
 use UNIVERSAL::DOES  qw/does/;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2619
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
351
 | 
    | 
| 
11
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
81
 | 
 use Digest::MD5      qw/md5_base64/;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
288
 | 
    | 
| 
12
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
2919
 | 
 use mro 'c3';
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3026
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
    | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
14
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
3078
 | 
 use namespace::clean;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48398
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
    | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $VERSION = '0.09';
  | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # root grammar (will be inherited by subclasses)
  | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $root_grammar = do {
  | 
| 
20
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
8962
 | 
   use Regexp::Grammars 1.038;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
132814
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
    | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   qr{
  | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # 
  | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      \A (?: 
  | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
            
  | 
| 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
          | 
  | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
          | 
  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
          | 
  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
          )
  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
          (?: \Z |  )
  | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        (*COMMIT) (?:  |  )
  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        (*COMMIT) (?:  |  )
  | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         <[value]>+ % ,
  | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       BETWEEN (*COMMIT) (?:  AND  |  )
  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        \<= | \< | \>= | \>
  | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         \<\> | -(?!\d) | != | !
  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         
  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
  | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | 
  | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # |  # removed from "standard" value because it might
  | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                        # interfere with other codes like gender M/F
  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       NULL
  | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         \.\.
  | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       | --
  | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
68
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       :(?::)?
  | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        Y(?:ES)?     (?{ $MATCH = 1 })
  | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | T(?:RUE)?    (?{ $MATCH = 1 })
  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | N(?:O)?      (?{ $MATCH = 0 })
  | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | F(?:ALSE)?   (?{ $MATCH = 0 })
  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        
  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
  | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        '(.*?)' (*COMMIT)  (?{ $MATCH = $CAPTURE })
  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | "(.*?)" (*COMMIT)  (?{ $MATCH = $CAPTURE })
  | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     
  | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      [^\s,]+(?:\s+[^\s,]+)*?
  | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }xms;
  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 };
  | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #======================================================================
  | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # CLASS METHODS
  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #======================================================================
  | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub sub_grammar {
  | 
| 
97
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
3
 | 
   my $class = shift;
  | 
| 
98
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
   return; # should redefine method in subclasses that refine the root grammar
  | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my %params_for_new = (
  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   -components    => {type => ARRAYREF, optional => 1  },
  | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   -fields        => {type => HASHREF,  default  => {} },
  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   -multicols_sep => {type => SCALAR,   optional => 1  },
  | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );
  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
107
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub new {
  | 
| 
108
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
  
1
  
 | 
1313
 | 
   my $class = shift;
  | 
| 
109
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
   my $self  = {};
  | 
| 
110
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
263
 | 
   my %args  = validate(@_, \%params_for_new);
  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # load optional components
  | 
| 
113
 | 
8
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
55
 | 
   if ($args{-components}) {
  | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # deactivate strict refs because we'll be playing with symbol tables
  | 
| 
115
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
63
 | 
     no strict 'refs';
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8963
 | 
    | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
117
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
     my @components;
  | 
| 
118
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
     foreach my $component (@{$args{-components}}) {
  | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
    | 
| 
119
 | 
6
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
87
 | 
       $component =~ s/^\+//
  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         or $component = __PACKAGE__ . "::$component";
  | 
| 
121
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
       load $component;
  | 
| 
122
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
88
 | 
       push @components, $component;
  | 
| 
123
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
       my @sub_grammar = $component->sub_grammar;
  | 
| 
124
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
33
 | 
       push @{$self->{grammar_ISA}}, @sub_grammar if @sub_grammar;
  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
    | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }
  | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # a new anonymous class will inherit from all components
  | 
| 
128
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
     $class .= "::_ANON_::" . md5_base64(join ",", @components);
  | 
| 
129
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
     unless (@{$class . "::ISA"}) {
  | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
52
 | 
    | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # dynamically create that class and use 'c3' inheritance in it
  | 
| 
131
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
       push @{$class . "::ISA"}, @components;
  | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
47
 | 
    | 
| 
132
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26
 | 
       mro::set_mro($class, 'c3');
  | 
| 
133
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }
  | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }
  | 
| 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
136
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # use root grammar if no derived grammar was installed by components
  | 
| 
137
 | 
8
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
41
 | 
   $self->{grammar_ISA} ||= [ 'SQL::Abstract::FromQuery' ];
  | 
| 
138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
139
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # setup fields info
  | 
| 
140
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
   foreach my $type (keys %{$args{-fields}}) {
  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
    | 
| 
141
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
12
 | 
     if ($type eq 'IGNORE') {
  | 
| 
142
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
       ref $args{-fields}{IGNORE} eq 'Regexp'
  | 
| 
143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         or die "IGNORE should be associated with a qr/.../ regular expression";
  | 
| 
144
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
       $self->{IGNORE} = $args{-fields}{IGNORE};
  | 
| 
145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }
  | 
| 
146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     else {
  | 
| 
147
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
       my $fields_aref = $args{-fields}{$type};
  | 
| 
148
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
14
 | 
       does($fields_aref, 'ARRAY')
  | 
| 
149
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         or die "list of fields for type $type should be an arrayref";
  | 
| 
150
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
67
 | 
       $self->{field}{$_} = $type foreach @$fields_aref;
  | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }
  | 
| 
152
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }
  | 
| 
153
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
154
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # other args are just copied into $self (at the moment, only one such arg)
  | 
| 
155
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
72
 | 
   $self->{$_} = $args{-$_} for qw/multicols_sep/;
  | 
| 
156
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
157
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # return the blessed object
  | 
| 
158
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
38
 | 
   bless $self, $class;
  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _error_handler {
  | 
| 
162
 | 
33
 | 
 
 | 
 
 | 
  
33
  
 | 
 
 | 
33
 | 
   my $class = shift;
  | 
| 
163
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   return 'INCORRECT INPUT', sub {
  | 
| 
164
 | 
44
 | 
 
 | 
 
 | 
  
44
  
 | 
 
 | 
154
 | 
     my ($error, $rule, $context)  = @_;
  | 
| 
165
 | 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
96
 | 
     my $msg = {
  | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       negated_values => 'Expected a value after negation',
  | 
| 
167
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       op_and_value   => 'Expected a value after comparison operator',
  | 
| 
168
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       between        => 'Expected min and max after "BETWEEN"',
  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       standard       => 'Unexpected input after initial value',
  | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }->{$rule};
  | 
| 
171
 | 
44
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
75
 | 
     $msg //= "Could not parse rule '$rule'";
  | 
| 
172
 | 
44
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
77
 | 
     $msg  .= " ('$context')" if $context;
  | 
| 
173
 | 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
84
 | 
     return $msg;
  | 
| 
174
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
107
 | 
   };
  | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #======================================================================
  | 
| 
179
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # INSTANCE METHODS
  | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #======================================================================
  | 
| 
181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _grammar {
  | 
| 
184
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
15
 | 
   my ($self, $rule) = @_;
  | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
186
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
   my $extends = join "", map {"\n"} @{$self->{grammar_ISA}};
  | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
40
 | 
    | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
    | 
| 
187
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26
 | 
   my $grammar = "<$rule>\n$extends";
  | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # compile this grammar. NOTE : since Regexp::Grammars uses a very
  | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # special form of operator overloading, we must go through an eval
  | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # so that qr/../ receives a string without variable interpolation;
  | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # do {use Regexp::Grammars; qr{$grammar}x;} would seem logical but won't work.
  | 
| 
193
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
   local $@;
  | 
| 
194
 | 
7
 | 
  
 50
  
 | 
 
 | 
  
7
  
 | 
 
 | 
36
 | 
   my $compiled_grammar = eval "use Regexp::Grammars; qr{$grammar}x"
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
    | 
| 
 
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
621
 | 
    | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     or die "INVALID GRAMMAR: $@";
  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
197
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
57
 | 
   return $compiled_grammar;
  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
199
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub parse {
  | 
| 
204
 | 
36
 | 
 
 | 
 
 | 
  
36
  
 | 
  
1
  
 | 
13429
 | 
   my ($self, $data) = @_;
  | 
| 
205
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
46
 | 
   my $class = ref $self;
  | 
| 
206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
207
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # if $data is an object with ->param() method, transform into plain hashref
  | 
| 
208
 | 
36
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
123
 | 
   $data = $self->_flatten_into_hashref($data) if blessed $data 
  | 
| 
209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                                               && $data->can('param');
  | 
| 
210
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
211
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # set error translator for grammars
  | 
| 
212
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
78
 | 
   my ($err_msg, $err_translator) = $self->_error_handler;
  | 
| 
213
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
94
 | 
   my $tmp = Regexp::Grammars::set_error_translator($err_translator);
  | 
| 
214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # parse each field within $data
  | 
| 
216
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
387
 | 
   my %result;
  | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   my %errors;
  | 
| 
218
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  FIELD:
  | 
| 
219
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
75
 | 
   foreach my $field (keys %$data) {
  | 
| 
220
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # ignore fields in exclusion list or fields without any data
  | 
| 
221
 | 
51
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
238
 | 
     !$self->{IGNORE} or $field !~ $self->{IGNORE} or next FIELD;
  | 
| 
222
 | 
50
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
101
 | 
     my $val = $data->{$field}                     or next FIELD;
  | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # decide which grammar to apply
  | 
| 
225
 | 
50
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
157
 | 
     my $rule    = $self->{field}{$field}  ||  'standard';
  | 
| 
226
 | 
50
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
161
 | 
     my $grammar = $self->{grammar}{$rule} ||= $self->_grammar($rule);
  | 
| 
227
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
228
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # invoke grammar on field content
  | 
| 
229
 | 
50
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
119
 | 
     if ($val =~ $grammar->with_actions($self)) {
  | 
| 
230
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
137
 | 
       $result{$field} = $/{$rule};
  | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }
  | 
| 
232
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     else {
  | 
| 
233
 | 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
231
 | 
       $errors{$field} = [@!];
  | 
| 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }
  | 
| 
235
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }
  | 
| 
236
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # report errors, if any
  | 
| 
238
 | 
36
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
101
 | 
   SQL::Abstract::FromQuery::_Exception->throw($err_msg, %errors) if %errors;
  | 
| 
239
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # otherwise fix multicolumns and then return result
  | 
| 
241
 | 
31
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
60
 | 
   $self->distribute_multicols_criteria(\%result) if $self->{multicols_sep};
  | 
| 
242
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
106
 | 
   return \%result;
  | 
| 
243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
244
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _flatten_into_hashref {
  | 
| 
247
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
   my ($self, $data) = @_;
  | 
| 
248
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   my %h;
  | 
| 
249
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   foreach my $field ($data->param()) {
  | 
| 
250
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my @vals = $data->param($field);
  | 
| 
251
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $val = join ",", @vals; # TOO simple-minded - should make it more abstract
  | 
| 
252
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     $h{$field} = $val;
  | 
| 
253
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }
  | 
| 
254
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   return \%h;
  | 
| 
255
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
256
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
257
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
258
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
259
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub distribute_multicols_criteria {
  | 
| 
261
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
3
 | 
   my ($self, $criteria) = @_;
  | 
| 
262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
263
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
   my $sep = qr[$self->{multicols_sep}];
  | 
| 
264
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1
 | 
   my @and_conditions;
  | 
| 
265
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
266
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # loop over keys that contain the multicol separator character
  | 
| 
267
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
   foreach my $multi_cols_key (grep /$sep/, sort keys %$criteria) {
  | 
| 
268
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # separate individual columns
  | 
| 
270
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
     my @cols       = split $sep, $multi_cols_key;
  | 
| 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # remove the entry from the hash and keep the multi-value
  | 
| 
273
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
     my $vals       = delete $criteria->{$multi_cols_key};
  | 
| 
274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # extract the distributed conditions
  | 
| 
276
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
     my $conditions = $self->_build_conditions(\@cols, $vals);
  | 
| 
277
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
278
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # add that to the list of conditions
  | 
| 
279
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
     my $new_cond = @$conditions > 1 ? {-or => $conditions} : $conditions->[0];
  | 
| 
280
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
     push @and_conditions, $new_cond;
  | 
| 
281
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }
  | 
| 
282
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # assemble conditions and put them back into the criteria hash
  | 
| 
284
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
   my $previous_and = $criteria->{-and};
  | 
| 
285
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
   push @and_conditions, $previous_and if $previous_and;
  | 
| 
286
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
9
 | 
   $criteria->{-and} = \@and_conditions if @and_conditions;
  | 
| 
287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
288
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
289
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
290
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _build_conditions {
  | 
| 
291
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
3
 | 
   my ($self, $cols, $val) = @_;
  | 
| 
292
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
293
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # is this a SQL::Abstract '-in' clause ?
  | 
| 
294
 | 
2
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
8
 | 
   my $ref   = ref $val || '';
  | 
| 
295
 | 
2
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
9
 | 
   my $is_in = $ref eq 'HASH' && join('', keys %$val) eq '-in';
  | 
| 
296
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
297
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # for easyness of the algorithm, an '-in' clause or a plain scalar
  | 
| 
298
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # are both treated as a list
  | 
| 
299
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
   my @vals = $is_in ? @{$val->{-in}}
  | 
| 
 
 | 
1
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
            : $ref   ? die "unexpected ref value for multi_cols_key"
  | 
| 
301
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
            :          ($val);
  | 
| 
302
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # for each multi-columns value, we build a "condition" (hashref col=>val)
  | 
| 
304
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
   my @conditions;
  | 
| 
305
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
   my $sep = qr[$self->{multicols_sep}];
  | 
| 
306
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
   foreach my $val (@vals) {
  | 
| 
307
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
     my @single_vals = split $sep, $val;
  | 
| 
308
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
     @$cols == @single_vals 
  | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       or die "inconsistent number of values for multi_cols_key";
  | 
| 
310
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
     my %condition = mesh @$cols, @single_vals;
  | 
| 
311
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
     push @conditions, \%condition;
  | 
| 
312
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }
  | 
| 
313
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # the result is a list of conditions that will be 'OR-ed' by the caller
  | 
| 
315
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
   return \@conditions;
  | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
318
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
319
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #======================================================================
  | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ACTIONS HOOKED TO THE GRAMMAR
  | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #======================================================================
  | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
324
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub negated_values {
  | 
| 
325
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
0
  
 | 
91
 | 
   my ($self, $h) = @_;
  | 
| 
326
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
   my $vals = $h->{values};
  | 
| 
327
 | 
5
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
   if (ref $vals) {
  | 
| 
328
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
9
 | 
     ref $vals eq 'HASH' or die 'unexpected reference in negation';
  | 
| 
329
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
     my ($op, $val, @others) = %$vals;
  | 
| 
330
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
7
 | 
     not @others         or die 'unexpected hash size in negation';
  | 
| 
331
 | 
3
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
     if    ($op eq '-in') {return {-not_in => $val}                   }
  | 
| 
 
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
    | 
| 
332
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     elsif ($op eq '='  ) {return {'<>'    => $val}                   }
  | 
| 
333
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     else                 {die "unexpected operator '$op' in negation"}
  | 
| 
334
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }
  | 
| 
335
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   else {
  | 
| 
336
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
     return {'<>' => $vals};
  | 
| 
337
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }
  | 
| 
338
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
339
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
340
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
341
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub null {
  | 
| 
342
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
0
  
 | 
76
 | 
   my ($self, $h) = @_;
  | 
| 
343
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
   return {'=' => undef};
  | 
| 
344
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # Note: unfortunately, we can't return just undef at this stage,
  | 
| 
345
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # because Regex::Grammars would interpret it as a parse failure.
  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
347
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
348
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
349
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub op_and_value {
  | 
| 
350
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
59
 | 
   my ($self, $h) = @_;
  | 
| 
351
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
   return {$h->{compare} => $h->{value}};
  | 
| 
352
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
353
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
354
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
355
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub between {
  | 
| 
356
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
  
0
  
 | 
106
 | 
   my ($self, $h) = @_;
  | 
| 
357
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
   return {-between => [$h->{min}, $h->{max}]};
  | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
359
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
360
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
361
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
362
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub values {
  | 
| 
363
 | 
34
 | 
 
 | 
 
 | 
  
34
  
 | 
  
0
  
 | 
613
 | 
   my ($self, $h) = @_;
  | 
| 
364
 | 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
31
 | 
   my $n_values = @{$h->{value}};
  | 
| 
 
 | 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
47
 | 
    | 
| 
365
 | 
34
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
104
 | 
   return $n_values > 1 ? {-in => $h->{value}}
  | 
| 
366
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                        : $h->{value}[0];
  | 
| 
367
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
368
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
369
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
370
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub date {
  | 
| 
371
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
  
0
  
 | 
69
 | 
   my ($self, $h) = @_;
  | 
| 
372
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
18
 | 
   $h->{year} += 2000 if length($h->{year}) < 3;
  | 
| 
373
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
   return sprintf "%04d-%02d-%02d", @{$h}{qw/year month day/};
  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
24
 | 
    | 
| 
374
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
375
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
376
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
377
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub time {
  | 
| 
378
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
31
 | 
   my ($self, $h) = @_;
  | 
| 
379
 | 
2
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
13
 | 
   $h->{seconds} ||= 0;
  | 
| 
380
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
   return sprintf "%02d:%02d:%02d", @{$h}{qw/hour minutes seconds/};
  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
    | 
| 
381
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
383
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
384
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub string {
  | 
| 
385
 | 
57
 | 
 
 | 
 
 | 
  
57
  
 | 
  
0
  
 | 
773
 | 
   my ($self, $s) = @_;
  | 
| 
386
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
387
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # if any '*', substitute by '%' and make it a "-like" operator
  | 
| 
388
 | 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
66
 | 
   my $is_pattern = $s =~ tr/*/%/;
  | 
| 
389
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # NOTE : a reentrant =~ s/../../ would core dump, but tr/../../ is OK
  | 
| 
390
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
391
 | 
57
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
128
 | 
   return $is_pattern ? {-like => $s} : $s;
  | 
| 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
393
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
394
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
395
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #======================================================================
  | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # PRIVATE CLASS FOR REPORTING PARSE EXCEPTIONS
  | 
| 
397
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #======================================================================
  | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package
  | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   SQL::Abstract::FromQuery::_Exception;
  | 
| 
401
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
32
 | 
 use strict;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
200
 | 
    | 
| 
402
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
26
 | 
 use warnings;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
    | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1381
 | 
    | 
| 
403
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
404
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 use overload 
  | 
| 
405
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   '""' => sub {
  | 
| 
406
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
633
 | 
     my $self = shift;
  | 
| 
407
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
     my $msg = $self->{err_msg};
  | 
| 
408
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
     for my $field (sort keys %{$self->{errors}}) {
  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
    | 
| 
409
 | 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
       my $field_errors = $self->{errors}{$field};
  | 
| 
410
 | 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36
 | 
       $msg .= "\n$field : " . join ", ", @$field_errors;
  | 
| 
411
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }
  | 
| 
412
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
413
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
65
 | 
     return $msg;
  | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   },
  | 
| 
415
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
81
 | 
   fallback => 1,
  | 
| 
416
 | 
7
 | 
 
 | 
 
 | 
  
7
  
 | 
 
 | 
728
 | 
   ;
  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
    | 
| 
417
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
418
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
419
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub throw {
  | 
| 
420
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
 
 | 
12
 | 
   my ($class, $err_msg, %errors) = @_;
  | 
| 
421
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
   my $self = bless {err_msg => $err_msg, errors => \%errors}, $class;
  | 
| 
422
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
   die $self;
  | 
| 
423
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }
  | 
| 
424
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
425
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
426
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #======================================================================
  | 
| 
427
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1; # End of SQL::Abstract::FromQuery
  | 
| 
428
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #======================================================================
  | 
| 
429
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
  | 
| 
430
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 __END__
  |