| 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; |