File Coverage

lib/CGI/OptimalQuery/FilterParser.pm
Criterion Covered Total %
statement 9 53 16.9
branch 0 24 0.0
condition 0 15 0.0
subroutine 3 4 75.0
pod 0 1 0.0
total 12 97 12.3


line stmt bran cond sub pod time code
1             package CGI::OptimalQuery::FilterParser;
2              
3 1     1   801 use strict;
  1         2  
  1         22  
4 1     1   3 use warnings;
  1         1  
  1         20  
5 1     1   2 no warnings qw( uninitialized );
  1         1  
  1         719  
6              
7             # arguments: ($CgiOptimalQueryObjecto, $filterString)
8             # return value: is an arrayref contain zero or more components that look like:
9             # # logic operator
10             # 'AND'|'OR', # logic operator
11             # # type 1 - (selectalias operator literal)
12             # [1,$numLeftParen,$leftExpSelectAlias,$op,$rightExpLiteral,$numRightParen],
13             # # type 2 - (namedfilter, arguments)
14             # [2,$numLeftParen,$namedFilter,$argArray,$numRightParen]
15             # # type 3 - (selectalias operator selectalias)
16             # [3,$numLeftParen,$leftExpSelectAlias,$op,$rightExpSelectAlias,$numRightParen],
17             # dies on bad filter string
18             sub parseFilter {
19             # $o is optimalquery object, $f is the filter string
20 0     0 0   my ($o, $f) = @_;
21 0           $f =~ /\G\s+/gc; # match all leading whitespace
22              
23             # initialize the return value which is an array of components
24 0           my @rv;
25 0 0         return \@rv if $f eq '';
26            
27 0           while (1) {
28 0           my $numLeftParenthesis = 0;
29 0           my $numRightParenthesis = 0;
30              
31             # parse opening parenthesis
32 0           while ($f =~ /\G\(\s*/gc) { $numLeftParenthesis++; }
  0            
33              
34             # if this looks like a named filter
35 0 0         if ($f=~/\G(\w+)\s*\(\s*/gc) {
36 0           my $namedFilter = $1;
37             die "Invalid named filter $namedFilter at: ".substr($f, 0, pos($f)).' <*> '.substr($f,pos($f))
38 0 0         unless exists $$o{schema}{named_filters}{$namedFilter};
39              
40             # parse named filter arguments
41 0           my @args;
42 0           while (1) {
43             # closing paren so end
44 0 0 0       if ($f=~/\G\)\s*/gc) {
    0 0        
    0          
45 0           last;
46             }
47              
48             # single quoted value OR double quoted value OR no whitespace literal
49             elsif ($f=~/\G\'([^\']*)\'\s*/gc || $f=~/\G\"([^\"]*)\"\s*/gc || $f=~/\G(\w+)\s*/gc) {
50 0           push @args, $1;
51             }
52              
53             # , => : separator so do nothing
54             elsif ($f =~ /\G(\,|\=\>|\:)\s*/gc) {
55             # noop
56             }
57             else {
58 0           die "Invalid named filter $namedFilter - missing right paren at: ".substr($f, 0, pos($f)).' <*> '.substr($f,pos($f));
59             }
60             }
61              
62             # parse closing parenthesis
63 0           while ($f =~ /\G\)\s*/gc) { $numRightParenthesis++; }
  0            
64 0           push @rv, [2,$numLeftParenthesis,$namedFilter,\@args,$numRightParenthesis];
65             }
66              
67             # else this is an expression
68             else {
69 0           my $lexp;
70 0           my $typeNum = 1;
71              
72             # grab select alias used on the left side of the expression
73 0 0 0       if ($f=~/\G\[([^\]]+)\]\s*/gc || $f=~/\G(\w+)\s*/gc) { $lexp = $1; }
  0            
74 0           else { die 'Missing left expression: '.substr($f, 0, pos($f)).' <*> '.substr($f,pos($f)); }
75              
76             # make sure the select alias is valid
77             die "Invalid field $lexp at: ".substr($f, 0, pos($f)).' <*> '.substr($f,pos($f))
78 0 0         unless exists $$o{schema}{select}{$lexp};
79              
80             # parse the operator
81 0           my $op;
82 0 0         if ($f =~ /\G(\!\=|\=|\<\=|\>\=|\<|\>|like|not\ like|contains|not\ contains)\s*/igc) { $op = $1; }
  0            
83 0           else { die 'Missing operator: '.substr($f, 0, pos($f)).' <*> '.substr($f,pos($f)); }
84              
85             # parse the right side of expression
86 0           my $rexp;
87              
88             # if rexp is a select alias
89 0 0 0       if ($f=~/\G\[([^\]]+)\]\s*/gc) {
    0 0        
90 0           $rexp = $1;
91 0           $typeNum = 3;
92             }
93              
94             # else if rexp is a literal
95             elsif ($f=~/\G\'([^\']*)\'\s*/gc || $f=~/\G\"([^\"]*)\"\s*/gc || $f=~/\G(\w+)\s*/gc) {
96 0           $rexp = $1;
97             }
98              
99 0           else { die 'Missing right expression: '.substr($f, 0, pos($f)).' <*> '.substr($f,pos($f)); }
100              
101             # parse closing parenthesis
102 0           while ($f =~ /\G\)\s*/gc) { $numRightParenthesis++; }
  0            
103              
104 0           push @rv, [$typeNum, $numLeftParenthesis, $lexp, $op, $rexp, $numRightParenthesis];
105             }
106              
107             # parse logic operator
108 0 0         if ($f =~ /(AND|OR)\s*/gci) { push @rv, uc($1); }
  0            
109 0           else { last; }
110             }
111 0           return \@rv;
112             }
113              
114             1;