File Coverage

blib/lib/HDB/Parser.pm
Criterion Covered Total %
statement 7 250 2.8
branch 0 120 0.0
condition 0 30 0.0
subroutine 3 15 20.0
pod 0 13 0.0
total 10 428 2.3


line stmt bran cond sub pod time code
1             #############################################################################
2             ## Name: Parser.pm
3             ## Purpose: HDB::Parser
4             ## Author: Graciliano M. P.
5             ## Modified by:
6             ## Created: 15/01/2003
7             ## RCS-ID:
8             ## Copyright: (c) 2002 Graciliano M. P.
9             ## Licence: This program is free software; you can redistribute it and/or
10             ## modify it under the same terms as Perl itself
11             #############################################################################
12              
13             package HDB::Parser ;
14             our $VERSION = '1.0' ;
15              
16 1     1   6 use strict qw(vars) ;
  1         1  
  1         27  
17 1     1   4 no warnings ;
  1         2  
  1         5805  
18              
19             my %CACHE ;
20              
21             my @STR_LYB = qw(a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) ;
22              
23             ###############
24             # PARSE_WHERE #
25             ###############
26              
27             #use HDB::CORE ;
28             #print Parse_Where(['id == ?' , \'or' , qw(1 2 3)] , {} , 1) ;
29              
30             sub Parse_Where {
31 0     0 0 0 my ( $where , $this , $nowhere ) = @_ ;
32            
33 0 0       0 if ($where eq '') { return ;}
  0         0  
34            
35 0         0 my @where = &HDB::CORE::parse_ref($where) ;
36            
37 0 0 0     0 if (ref($where) && $#where <= 1 && $where[1] eq '') { return( $nowhere ? $where[0] : "WHERE( $where[0] )" ) ;}
  0 0 0     0  
    0 0        
38             elsif (ref($where) eq 'ARRAY' && $#where >= 1) {
39 0         0 my $cond = shift @where ;
40            
41 0 0       0 my $op = ref $where[0] eq 'ARRAY' ? @{ shift(@where) }[0] : (ref $where[0] eq 'SCALAR' ? ${ shift(@where) } : 'OR' ) ;
  0 0       0  
  0         0  
42              
43 0         0 $op =~ s/\s+//gs ;
44 0         0 $op =~ s/^(?:&&?|and)$/AND/i ;
45 0         0 $op =~ s/^(?:\|\|?|or)$/OR/i ;
46 0 0       0 $op = 'OR' if $op !~ /^(?:AND|OR)$/ ;
47            
48 0         0 my $parser ;
49            
50 0 0       0 if ( $cond =~ /^\s*\(?\s*\?\s*\)?\s*$/s ) {
51 0         0 foreach my $where_i ( @where ) {
52 0         0 $where_i = Parse_Where($where_i,$this,1) ;
53             }
54 0         0 $parser = '(' . join(") $op (", @where) . ')' ;
55             }
56             else {
57 0         0 $cond = '('. &Parse_Where($cond,$this,1) . ')' ;
58            
59 0         0 foreach my $where_i ( @where ) {
60 0         0 my $val = &Value_Quote($where_i) ;
61 0 0       0 $parser .= " $op " if $parser ne '' ;
62 0         0 my $cond_new = $cond ;
63 0         0 $cond_new =~ s/["']\?["']/$val/gs ;
64 0         0 $parser .= $cond_new ;
65             }
66             }
67              
68 0 0       0 if ($nowhere) { return($parser) ;}
  0         0  
69 0         0 else { return( "WHERE( $parser )" ) ;}
70             }
71            
72 0 0       0 my $sql_id = $this ? "$this->{SQL}{REGEXP},$this->{SQL}{LIKE}" : '' ;
73 0         0 my $where_id = "$sql_id#$where" ;
74            
75 0 0       0 if ( defined $CACHE{$where_id} ) { return( $nowhere ? $CACHE{$where_id} : "WHERE( $CACHE{$where_id} )" ) ;}
  0 0       0  
76            
77 0         0 my ($syntax,@quotes) = &Parse_Quotes($where) ;
78            
79 0         0 my @blocks = &Parse_Blocks($syntax) ;
80 0         0 &Filter_Blocks( \@blocks , \@quotes , $this ) ;
81            
82 0         0 my ($parse,$lnk_last) ;
83            
84 0         0 foreach my $blocks_i ( @blocks ) {
85 0         0 my @cond = @$blocks_i ;
86 0 0       0 $parse .= " " if $parse =~ /\S$/s ;
87            
88 0 0       0 if ( $cond[0] =~ /^(?:AND|OR)$/ ) {
89 0         0 my $add = shift @cond ;
90 0         0 $parse .= $add ; $lnk_last = $add ;
  0         0  
91             }
92            
93 0         0 my $cond = join(" ", @cond) ;
94            
95 0 0 0     0 $parse .= " " if ($cond ne '' && $parse =~ /\S$/s) ;
96            
97 0 0       0 if ($cond =~ /^\s*(?:AND|OR)\s*$/i) { $parse .= $cond ; $lnk_last = $cond ;}
  0 0       0  
  0         0  
98             elsif ($cond =~ /\S/s) {
99 0 0 0     0 if ($lnk_last !~ /\w/ && $parse =~ /\S/) { $parse .= "AND " ;}
  0         0  
100 0 0       0 if ($cond =~ /\s(?:AND|OR)\s/) { $parse .= "($cond)" ;}
  0         0  
101 0         0 else { $parse .= $cond ;}
102 0         0 $lnk_last = undef ;
103             }
104             }
105              
106 0         0 $parse =~ s/%q_(\d+)%/$quotes[$1]/gs ;
107              
108 0         0 CLEAN_CACHE() ;
109              
110 0         0 $CACHE{$where_id} = $parse ;
111              
112 0 0       0 $parse = "WHERE( $parse )" if !$nowhere ;
113 0         0 return( $parse ) ;
114             }
115              
116             ####################
117             # FILTER_CONDITION #
118             ####################
119              
120             sub Filter_Condition {
121 0     0 0 0 my ( $string , $quotes , $this ) = @_ ;
122            
123 0         0 $string =~ s/\s+/ /gs ;
124 0         0 $string =~ s/^\s+//g ;
125 0         0 $string =~ s/\s+$//g ;
126            
127 0         0 my $split_mark = '%x%' ;
128 0         0 while($string =~ /\Q$split_mark\E/s) { substr($split_mark,-2,1) .= &Rand_Str ;}
  0         0  
129            
130 0         0 $string .= ' ' ;
131 0         0 $string =~ s/([^\w&]|^)(\|\||&&?|and|or)([^\w&])/$1$split_mark$2$split_mark$3/gi ;
132            
133 0         0 my @conds = split(/\Q$split_mark\E/s , $string) ;
134 0         0 my @conds_ok ;
135            
136 0         0 foreach my $conds_i ( @conds ) {
137 0 0       0 if ($conds_i !~ /\S/s) { next ;}
  0         0  
138 0 0       0 if ($conds_i =~ /^\s*(?:and|&&?)\s*$/s) { $conds_i = 'AND' ;}
  0 0       0  
139 0         0 elsif ($conds_i =~ /^\s*(?:or|\|\|)\s*$/s) { $conds_i = 'OR' ;}
140             else {
141 0         0 my ($col,$cond,$val) = ( $conds_i =~ /^\s*(.*?)\s*(<>|!=|!~|=~|<=|>=|=>|=<|==?|>|<|\s+(?:eq|ne))\s*(.*)/ ) ;
142 0         0 $cond =~ s/\s//s ;
143 0         0 $val =~ s/\s*$//s ;
144              
145 0 0       0 if ($cond =~ /^(?:!=|<>|ne)$/s) { $cond = '<>' ;}
  0 0       0  
    0          
    0          
    0          
    0          
    0          
    0          
146 0         0 elsif ($cond =~ /^(?:<=|=<)$/s) { $cond = '<=' ;}
147 0         0 elsif ($cond =~ /^(?:>=|=>)$/s) { $cond = '>=' ;}
148 0         0 elsif ($cond =~ /^>$/s) { $cond = '>' ;}
149 0         0 elsif ($cond =~ /^<$/s) { $cond = '<' ;}
150 0         0 elsif ($cond =~ /^=~$/s) { $cond = 'REGEXP' ;}
151 0         0 elsif ($cond =~ /^!~$/s) { $cond = 'NOT REGEXP' ;}
152 0         0 elsif ($cond =~ /^(?:==?|eq)$/s) { $cond = '=' ;}
153            
154 0 0 0     0 if ($cond =~ /REGEXP/ && $this && !$this->{SQL}{REGEXP} ) {
      0        
155 0 0       0 if ( $this->{SQL}{LIKE} ) {
156 0         0 ($cond , $val) = &Parse_REGEX_2_LIKE($cond , $val , $quotes) ;
157 0         0 $this->Error("Can't use REGEXP on SQL syntax on module $this->{name}!!! Changing 'REGEXP' to 'LIKE' on syntax." , 1) ;
158             }
159             else {
160 0         0 $this->Error("Can't use REGEXP on SQL syntax on module $this->{name}!!! Changing 'REGEXP' to '=' on syntax." , 1) ;
161 0         0 $cond = '=' ;
162             }
163             }
164            
165 0         0 $val = &Value_Quote($val,$quotes) ;
166            
167 0         0 $conds_i = "$col $cond $val" ;
168             }
169            
170 0         0 push(@conds_ok , $conds_i) ;
171             }
172            
173 0 0       0 if ( wantarray ) { return( @conds_ok ) ;}
  0         0  
174 0         0 else { return( join (" ", @conds_ok) ) ;}
175             }
176              
177             #################
178             # FILTER_BLOCKS #
179             #################
180              
181             sub Filter_Blocks {
182 0     0 0 0 my ( $blk_ref , $quotes , $this ) = @_ ;
183            
184 0         0 for my $i (0..$#$blk_ref) {
185 0 0       0 if ( ref( $$blk_ref[$i] ) eq 'ARRAY' ) { &Filter_Blocks( $$blk_ref[$i] ) ; next ;}
  0         0  
  0         0  
186 0         0 my @cond = Filter_Condition( $$blk_ref[$i] , $quotes , $this ) ;
187 0         0 $$blk_ref[$i] = \@cond ;
188             #print ">> $$blk_ref[$i]\n" ;
189             }
190            
191 0         0 return( $blk_ref ) ;
192             }
193              
194             #print Parse_Blocks(q`aaa (bbb) ccc ( ddd (eee) fff ) ggg `) ;
195             #@blks = Parse_Blocks(q`col = and (col = x && col != y)`) ;
196             #print join ("\n", @blks) ;
197              
198             ################
199             # PARSE_BLOCKS #
200             ################
201              
202             sub Parse_Blocks {
203 0     0 0 0 my ( $string ) = @_ ;
204              
205 0         0 my (@blocks,%b) ;
206            
207 0         0 while( $string =~ /(.*?)([\(\)])/gs ) {
208 0         0 my $init .= $1 ;
209 0         0 my $blk = $2 ;
210            
211 0 0       0 if ($blk eq '(') {
    0          
212 0 0       0 if (! $b{o}) {
213 0         0 my ($cond,$lnk) = ( $init =~ /(.*?[^\w&\|])\s*(\|\||&&?|and|or|)\s*$/gsi );
214 0         0 push(@blocks , $cond) ;
215 0         0 push(@blocks , $lnk) ;
216             }
217 0         0 $b{o}++ ;
218 0 0       0 if ($b{o} > 1) { $b{d} .= $init ;}
  0         0  
219 0         0 $b{d} .= $blk ;
220             }
221             elsif ($blk eq ')') {
222 0         0 $b{d} .= $init . $blk ;
223 0         0 $b{o}-- ;
224 0 0       0 if ($b{o} <= 0) {
225 0         0 $b{d} =~ s/^\(//gs ;
226 0         0 $b{d} =~ s/\)$//gs ;
227            
228 0         0 my $block ;
229 0 0       0 if ($b{d} =~ /\(.*?\)/s) {
230 0         0 $block = [&Parse_Blocks( $b{d} )] ;
231             }
232 0         0 else { $block = $b{d} ;}
233            
234 0         0 push(@blocks , $block) ;
235 0         0 $b{d} = undef ;
236             }
237             }
238             }
239            
240 0 0       0 if ( $string =~ /.*[\(\)](.*?)$/s ) { push(@blocks , $1) ;}
  0         0  
241 0         0 else { push(@blocks , $string) ;}
242              
243 0         0 return( @blocks ) ;
244             }
245              
246             #Parse_Quotes(q`aaa "b b b" ccc "\\\\" ddd \\\\ eee "f 'f' \"f\" f" ggg %bb`) ;
247             #Parse_Quotes(q`'x"x\''`) ;
248              
249             ################
250             # PARSE_QUOTES #
251             ################
252              
253             sub Parse_Quotes {
254 0     0 0 0 my $string = $_[0] ;
255            
256 0         0 my ($string_ok,@quotes,%q) ;
257              
258 0         0 my $bb_mark = '%bb' ;
259 0         0 while($string =~ /\Q$bb_mark\E/s) { $bb_mark .= &Rand_Str ;}
  0         0  
260              
261 0         0 $string =~ s/\\\\/$bb_mark/gs ;
262            
263 0         0 while( $string =~ /^(.*?(?:(?!\\).|))(['"])(.*)/s ) {
264 0         0 my $init .= $1 ;
265 0         0 my $quote = $2 ;
266 0         0 $string = $3 ;
267            
268 0 0       0 if ($init =~ /\\$/) {
269 0         0 $init .= $quote ;
270 0         0 $quote = '' ;
271             }
272            
273 0 0       0 if (! $q{o}) {
274 0         0 $q{o}++ ;
275 0         0 $q{q} = $quote ;
276 0         0 $q{d} = undef ;
277 0         0 $string_ok .= $init ;
278            
279 0 0       0 if (substr($string,0,1) eq $quote) {
280 0         0 $q{o} = 0 ;
281 0         0 push(@quotes , "$q{q}$q{q}") ;
282 0         0 $string_ok .= "%q_$#quotes%" ;
283 0         0 substr($string,0,1) = '' ;
284             }
285             }
286             else {
287 0         0 $q{d} .= $init ;
288 0 0       0 if ($quote eq $q{q}) {
289 0         0 $q{o} = 0 ;
290 0         0 push(@quotes , "$q{q}$q{d}$q{q}") ;
291 0         0 $string_ok .= "%q_$#quotes%" ;
292             }
293 0         0 else { $q{d} .= $quote ;}
294             }
295             }
296            
297 0         0 $string_ok .= $string ;
298 0         0 $string_ok =~ s/$bb_mark/\\\\/gs ;
299            
300             #substr($string_ok,0,1) = '' ;
301             #substr($string_ok,-1) = '' ;
302            
303 0         0 foreach my $quotes_i ( @quotes ) { $quotes_i =~ s/$bb_mark/\\\\/gs ;}
  0         0  
304            
305             #$string_ok =~ s/%q_(\d+)%/$quotes[$1]/gs ;
306            
307             #print "$string_ok <<@quotes>>\n" ;
308            
309 0         0 return( $string_ok , @quotes ) ;
310             }
311              
312             ###############
313             # VALUE_QUOTE #
314             ###############
315              
316             sub Value_Quote {
317 0     0 0 0 my ( $val , $quotes ) = @_ ;
318            
319 0         0 $val =~ s/^\s+//gs ;
320 0         0 $val =~ s/\s+$//gs ;
321            
322 0 0 0     0 if ($val !~ /^[\-\+]?(\d+|\d+\.\d+)$/s && (!$quotes || $val !~ /^%q_\d+%$/s) && $val !~ /^(?:NULL)$/si && $val ne '') {
      0        
323 0 0       0 $val =~ s/%q_(\d+)%/$$quotes[$1]/gs if $quotes ;
324              
325 0         0 substr($val , 0 , 0) = ' ' ;
326            
327 0         0 $val = &Parse_REGEXP($val) ;
328              
329 0         0 $val =~ s/(?!\\)(.)"/$1\\"/gs ;
330 0         0 substr($val , 0 , 1) = '' ;
331            
332 0         0 $val = qq`"$val"` ;
333             }
334            
335 0 0       0 if ($val eq '') { $val = 'NULL' ;}
  0         0  
336              
337 0         0 return( $val ) ;
338             }
339              
340             ################
341             # PARSE_REGEXP #
342             ################
343              
344             sub Parse_REGEXP {
345 0     0 0 0 my ( $string ) = @_ ;
346            
347 0         0 my $mark1 = '%box_o%' ;
348 0         0 while($string =~ /\Q$mark1\E/s) { substr($mark1,-2,1) .= &Rand_Str ;}
  0         0  
349            
350 0         0 my $mark2 = '%box_c%' ;
351 0         0 while($string =~ /\Q$mark2\E/s) { substr($mark2,-2,1) .= &Rand_Str ;}
  0         0  
352            
353 0         0 $string =~ s/\\\[/$mark1/gs ;
354 0         0 $string =~ s/\\\]/$mark2/gs ;
355            
356 0         0 $string =~ s/(?!\\)(.)\\w/$1\[a-zA-Z0-9]/gs ;
357 0         0 $string =~ s/(?!\\)(.)\\W/$1\[^a-zA-Z0-9]/gs ;
358 0         0 $string =~ s/(?!\\)(.)\\d/$1\[0-9]/gs ;
359 0         0 $string =~ s/(?!\\)(.)\\D/$1\[^0-9]/gs ;
360 0         0 $string =~ s/(?!\\)(.)\\s/$1\[ \t\n\r]/gs ;
361 0         0 $string =~ s/(?!\\)(.)\\S/$1\[^ \t\n\r]/gs ;
362              
363 0         0 while($string =~ /\[([^\[]*)\[([^\]]*)\]/gs) { $string =~ s/\[([^\[]*)\[([^\]]*)\]/\[$1$2/gs ;}
  0         0  
364              
365 0         0 $string =~ s/$mark1/\\\[/gs ;
366 0         0 $string =~ s/$mark2/\\\]/gs ;
367              
368 0         0 return( $string ) ;
369             }
370              
371             ######################
372             # PARSE_REGEX_2_LIKE #
373             ######################
374              
375             sub Parse_REGEX_2_LIKE {
376 0     0 0 0 my ( $cond , $regex , $quotes) = @_ ;
377            
378 0 0       0 $regex =~ s/%q_(\d+)%/$$quotes[$1]/gs if $quotes ;
379            
380 0 0       0 if ($regex =~ /^"(.*?)"$/) { $regex = $1 ;}
  0 0       0  
381 0         0 elsif ($regex =~ /^'(.*?)'$/) { $regex = $1 ;}
382            
383 0 0       0 if ($cond =~ /not/i ) { $cond = 'NOT LIKE' ;}
  0         0  
384 0         0 else { $cond = 'LIKE' ;}
385            
386 0 0 0     0 if ( $regex =~ /^\^/ && $regex =~ /\$$/) {
    0          
    0          
387 0         0 $regex =~ s/^\^// ;
388 0         0 $regex =~ s/\$$// ;
389             }
390             elsif ( $regex =~ /^\^/) {
391 0         0 $regex =~ s/^\^// ;
392 0         0 $regex .= '%' ;
393             }
394             elsif ( $regex =~ /\$$/) {
395 0         0 $regex =~ s/\$$// ;
396 0         0 $regex = "%$regex" ;
397             }
398 0         0 else { $regex = "%$regex%" ;}
399            
400 0         0 $regex =~ s/\./_/gs ;
401            
402 0         0 return( $cond , $regex ) ;
403             }
404              
405             ############
406             # RAND_STR #
407             ############
408              
409             sub Rand_Str {
410 0     0 0 0 return( @STR_LYB[rand(@STR_LYB)] ) ;
411             }
412              
413             #####################
414             # FILTER_NULL_BYTES #
415             #####################
416              
417             sub filter_null_bytes {
418 0 0   0 0 0 return if $_[0] !~ /\0/s ;
419            
420 0         0 my $place_holder = "\1\2\1" ;
421 0         0 my $x = 1 ;
422 0         0 while( $_[0] =~ /\Q$place_holder\E/s ) {
423 0         0 $place_holder = "\1" . ("\2" x ++$x) . "\1" ;
424             }
425            
426 0         0 $_[0] =~ s/\0/$place_holder/gs ;
427 0         0 $_[0] =~ s/^/$place_holder:/s ;
428             }
429              
430             #######################
431             # UNFILTER_NULL_BYTES #
432             #######################
433              
434             sub unfilter_null_bytes {
435 0     0 0 0 my $b1 = "\1" ;
436 0         0 my $b2 = "\2" ;
437 0 0       0 return if $_[0] !~ /^($b1$b2+$b1):/s ;
438 0         0 my $place_holder = $1 ;
439            
440 0         0 $_[0] =~ s/^\Q$place_holder\E://s ;
441 0         0 $_[0] =~ s/\Q$place_holder\E/\0/gs ;
442             }
443              
444             ###############
445             # CLEAN_CACHE #
446             ###############
447              
448             sub CLEAN_CACHE {
449 0     0 0 0 my @keys = keys %CACHE ;
450            
451 0 0       0 if ( @keys > 1000 ) {
452 0         0 while( @keys > 500 ) {
453 0         0 for(1..100) {
454 0         0 delete $CACHE{ $keys[ rand(@keys) ] } ;
455             }
456 0         0 @keys = keys %CACHE ;
457             }
458             }
459            
460             }
461              
462             #########
463             # RESET #
464             #########
465              
466             sub RESET {
467 1     1 0 4 %CACHE = () ;
468             }
469              
470             #######
471             # END #
472             #######
473              
474             1;
475              
476