File Coverage

blib/lib/PQL/Cache.pm
Criterion Covered Total %
statement 350 387 90.4
branch 93 140 66.4
condition 9 16 56.2
subroutine 38 44 86.3
pod 0 20 0.0
total 490 607 80.7


line stmt bran cond sub pod time code
1             #==============================================================================
2             #
3             # PQL::Cache
4             # A cache using Perl Query Language, similar to SQL and DBIx::Class
5             #
6             # Ralf Peine, Sun Dec 21 13:51:53 2014
7             #
8             #==============================================================================
9              
10             package PQL::Cache;
11              
12 1     1   25681 use strict;
  1         2  
  1         31  
13 1     1   4 use vars qw($VERSION);
  1         2  
  1         51  
14             $VERSION ='0.800';
15              
16             # standards
17 1     1   4 use warnings;
  1         4  
  1         23  
18 1     1   4 use Carp;
  1         1  
  1         2985  
19             # use Data::Dumper;
20              
21             # --- Create Instance -----------------
22             sub new
23             {
24 1     1 0 6 my $caller = $_[0];
25 1   33     5 my $class = ref($caller) || $caller;
26              
27             # let the class go
28 1         1 my $self = {};
29 1         2 bless $self, $class;
30              
31 1         3 $self->_init();
32              
33 1         5 return $self;
34             }
35              
36             # --- _init ------------------------------------------------------------------
37             sub _init
38             {
39 1     1   2 my ($self # instance_ref
40             ) = @_;
41              
42 1         2 $self->clear();
43 1         1 $self->{_table_def} = {};
44 1         3 $self->_implement_compare_subs();
45             }
46              
47             # --- clear the stored data ---------------------------------
48             sub clear {
49 1     1 0 2 my ($self, # instance_ref
50             # $cache_to_clear # optional: cache to clear. clear all if unset
51             ) = @_;
52 1         4 $self->{_cache} = {};
53 1         2 $self->{_index} = {};
54 1         2 $self->{_next_free_index_list} = {};
55             }
56              
57             # --- table_definition ---------------------------------------------------------
58              
59             sub set_table_definition {
60 2     2 0 421 my ($self, # instance_ref
61             $table_name, # cache (table) to store data...
62             $table_def # definitions for table
63             ) = @_;
64 2 50       5 croak "no table set" unless defined $table_name;
65              
66 2         4 $self->{_table_def}->{$table_name} = $table_def;
67 2         4 $self->_prepare_cache($table_name);
68 2         7 return $table_name;
69             }
70              
71             sub get_table_definition {
72 45     45 0 42 my ($self, # instance_ref
73             $table_name, # cache (table) to store data...
74             ) = @_;
75 45 50       73 croak "no table set " unless defined $table_name;
76              
77 45         49 my $table_def = $self->{_table_def}->{$table_name};
78              
79 45 50       60 unless ($table_def) {
80 0         0 my $known_tables = "known tables: ".join ("\n", sort keys (%{$self->{_table_def}}), '');
  0         0  
81             # print "$known_tables\n";
82 0         0 croak "No definition found for table [$table_name], $known_tables";
83             }
84              
85 45         57 return $table_def;
86             }
87              
88             # --- table cache --------------------------------------------
89              
90             sub get_table_cache {
91 69     69 0 60 my ($self, # instance_ref
92             $table_name, # cache (table) to store data...
93             ) = @_;
94 69 50       96 croak "no table set" unless defined $table_name;
95              
96 69         71 my $table = $self->{_cache}->{$table_name};
97              
98 69 50       85 croak "table [$table_name] not found" unless $table;
99              
100 69         84 return $table;
101             }
102              
103             # --- table_index --------------------------------------------
104              
105             sub get_table_index {
106 39     39 0 37 my ($self, # instance_ref
107             $table_name, # index (table) to store data...
108             ) = @_;
109 39 50       54 croak "no table set" unless defined $table_name;
110              
111 39         46 my $index = $self->{_index}->{$table_name};
112            
113 39 50       55 croak "index of table [$table_name] not found" unless $index;
114              
115 39         39 return $index;
116             }
117              
118             # --- table_keys --------------------------------------------
119              
120             sub get_table_keys {
121 33     33 0 31 my ($self, # instance_ref
122             $table_name, # table
123             $column, # column
124             ) = @_;
125 33 50       54 croak "no table set" unless defined $table_name;
126              
127 33         39 my $keys = $self->{_keys}->{$table_name};
128              
129 33 100       66 return $keys unless defined $column;
130            
131 6         8 return $keys->{$column};
132             }
133              
134             # --- get index of next free field in table -----------------
135             sub get_next_free_idx {
136 6     6 0 7 my ($self, # instance_ref
137             $table_name, # cache (table) to store data...
138             ) = @_;
139              
140 6         7 my $cache = $self->get_table_cache($table_name);
141              
142 6         9 my $index = $self->{_next_free_index_list}->{$table_name};
143 6 100       9 $index = 0 unless defined $index;
144 6         13 while ($cache->[$index]) {
145             # print "#### search next free index $index\n";
146 0         0 $index++;
147             }
148 6         7 $self->{_next_free_index_list}->{$table_name} = $index + 1;
149 6         8 return $index;
150             }
151              
152             # --- get compare subs ---
153              
154             sub get_compare_subs {
155 23     23 0 20 my ($self, # instance_ref
156             ) = @_;
157              
158 23   50     49 return $self->{_compare_subs} || {};
159             }
160              
161             # --- get combine subs ---
162              
163             sub get_combine_subs {
164 23     23 0 21 my ($self, # instance_ref
165             ) = @_;
166              
167 23   50     42 return $self->{_combine_subs} || {};
168             }
169              
170             # --- get step2 subs ---
171              
172             sub get_step2_subs {
173 23     23 0 16 my ($self, # instance_ref
174             ) = @_;
175              
176 23   50     38 return $self->{_step2_subs} || {};
177             }
178              
179             # --- implement compare subs ----------------------------------------------------------------
180              
181             sub _implement_compare_subs {
182 1     1   3 my ($self, # instance_ref
183             ) = @_;
184              
185 1         2 my $step2_subs = $self->{_step2_subs};
186              
187 1 50       4 $step2_subs = $self->{_step2_subs} = {} unless $step2_subs;
188              
189             $step2_subs->{ge} = sub {
190 1         3 my ($column, $compare_value,
191 1     1   1 ) = @{$_[0]};
192            
193 1 50       3 croak '"ge" operation needs SCALAR as parameter' if ref $compare_value;
194 1         5 return sub { $_->{$column} ge $compare_value };
  4         10  
195 1         5 };
196              
197             $step2_subs->{le} = sub {
198 2         3 my ($column, $compare_value,
199 2     2   2 ) = @{$_[0]};
200              
201 2 50       5 croak '"ge" operation needs SCALAR as parameter' if ref $compare_value;
202 2         7 return sub { $_->{$column} le $compare_value };
  7         15  
203 1         5 };
204              
205             $step2_subs->{obj} = sub {
206 4         11 my ($operation, $matching_sub,
207 4     4   3 ) = @{$_[0]};
208            
209 4 50       8 croak '"$operation" operation needs CODE_REF as parameter' unless ref $matching_sub eq "CODE";
210 4         5 return $matching_sub;
211 1         27 };
212              
213             $step2_subs->{data} = sub {
214 1         2 my ($operation, $matching_sub,
215 1     1   1 ) = @{$_[0]};
216 1 50       3 croak '"$operation" operation needs CODE_REF as parameter' unless ref $matching_sub eq "CODE";
217 1         8 return $matching_sub;
218 1         4 };
219              
220             $step2_subs->{sub_value} = sub {
221 1         2 my ($key,
222             $matching_sub
223 1     1   1 ) = @{$_[0]};
224              
225 1 50       4 croak '"data" operation needs CODE_REF as parameter' unless ref $matching_sub eq "CODE";
226 1         12 return sub { my $obj = $_; { local $_ = $obj->{$key}; return $matching_sub->() }};
  4         3  
  4         3  
  4         5  
  4         5  
227 1         4 };
228              
229 1         2 my $combine_subs = $self->{_combine_subs};
230              
231 1 50       2 $combine_subs = $self->{_combine_subs} = {} unless $combine_subs;
232              
233             $combine_subs->{and} = sub {
234 0     0   0 my ($conditions,
235             ) = @_;
236              
237 0 0       0 croak '"and" operation needs ARRAY_REF as parameter' unless ref $conditions eq "ARRAY";
238              
239 0         0 return $self->_handle_where(@_);
240 1         4 };
241            
242 1         2 my $compare_subs = $self->{_compare_subs};
243              
244 1 50       3 $compare_subs = $self->{_compare_subs} = {} unless $compare_subs;
245              
246             $compare_subs->{in} = sub {
247 2         4 my ($key,
248             $array_ref
249 2     2   2 ) = @{$_[0]};
250              
251 2 50       6 croak '"in" operation needs ARRAY_REF as parameter' unless ref $array_ref eq "ARRAY";
252              
253 2         3 my $search_key = $self->escape_regular_expression_special_chars($key);
254 2         2 my @in_select_arr;
255 2         3 foreach my $in_value (@$array_ref) {
256 7         9 $in_value = $self->escape_regular_expression_special_chars($in_value);
257 7         8 push (@in_select_arr, $in_value);
258             }
259 2         5 my $values_str = join ("|", @in_select_arr);
260 2         13 return ("#\\|$search_key=($values_str)\\|#", $values_str);
261            
262 1         4 };
263            
264             $compare_subs->{is} = sub {
265 8         12 my ($key,
266             $value
267 8     8   5 ) = @{$_[0]};
268              
269 8 50       13 croak '"is" operation needs SCALAR as parameter'.ref $value if ref $value;
270              
271 8         38 my $search_key = $self->escape_regular_expression_special_chars($key);
272 8 50       14 $value = '' unless defined $value;
273 8         11 $value = $self->escape_regular_expression_special_chars($value);
274 8         49 return ("#\\|$search_key=$value\\|#", $value);
275 1         4 };
276              
277             $compare_subs->{like} = sub {
278 5         6 my ($key,
279             $value
280 5     5   4 ) = @{$_[0]};
281            
282 5 50       9 croak '"like" operation needs SCALAR as parameter'.ref $value if ref $value;
283            
284 5         4 my $pattern = $value;
285              
286 5         6 my $search_key = $self->escape_regular_expression_special_chars($key);
287 5 100       10 if ($pattern =~ /^\^/o) {
288 1         4 $pattern =~ s/^\^//o;
289             }
290             else {
291 4         5 $pattern = "[^|]*".$pattern;
292             }
293            
294 5 100       11 if ($pattern =~ /\$$/o) {
295 1         3 $pattern =~ s/\$$//o;
296             }
297             else {
298 4         5 $pattern .= '[^|]*'
299             }
300             # print "like $pattern\n";
301 5         24 return ("#\\|$search_key=$pattern\\|#", $value);
302 1         6 };
303              
304             $compare_subs->{or} = sub {
305 0     0   0 carp "or is currently not supported";
306 1         4 };
307             }
308              
309             # --- prepare cache, if not already done ------------------
310              
311             sub _prepare_cache {
312 8     8   9 my ($self, # instance_ref
313             $table_name, # cache (table) to store data...
314             ) = @_;
315              
316 8 100       21 $self->{_cache}->{$table_name} = [] unless $self->{_cache}->{$table_name};
317 8 100       23 $self->{_index}->{$table_name} = [] unless $self->{_index}->{$table_name};
318              
319 8 100       23 unless ($self->{_keys}->{$table_name}) {
320 2         5 my $table_def = $self->get_table_definition($table_name);
321             # print "# Define key columns for table $table_name: ";
322 2         2 foreach my $key_column (@{$table_def->{keys}}) {
  2         4  
323             # print "$key_column, ";
324 2         6 $self->{_keys}->{$table_name}->{$key_column} = {};
325             }
326             # print "\n";
327             }
328             }
329              
330             # --- add data ----------------------------------------------
331             sub insert {
332 6     6 0 357 my ($self, # instance_ref
333             $table_name, # cache (table) to store data...
334             $data,
335             ) = @_;
336              
337 6 50       14 croak "no table set" unless defined $table_name;
338 6 50       10 croak "no data given" unless defined $data;
339              
340 6         9 $self->_prepare_cache($table_name);
341              
342 6         14 my $count = 0;
343 6 50       14 if (ref $data eq 'ARRAY') {
344 0         0 foreach my $single_data (@$data) {
345 0         0 $count = $self->_insert($table_name, $single_data);
346             }
347             }
348             else {
349 6         12 $count = $self->_insert($table_name, $data);
350             }
351 6         20 return $count;
352             }
353              
354             # --- add data ----------------------------------------------
355             sub _insert {
356 6     6   6 my ($self, # instance_ref
357             $table_name, # cache (table) to store data...
358             $data,
359             ) = @_;
360              
361 6         12 my $cache = $self->get_table_cache($table_name);
362 6         10 my $index = $self->get_table_index($table_name);
363              
364 6         9 my $next_idx = $self->get_next_free_idx($table_name);
365              
366 6         15 my $index_str = $self->build_index_string($table_name, $data, $next_idx);
367              
368 6         8 my $table_def = $self->get_table_definition($table_name);
369              
370 6         8 foreach my $key_column (@{$table_def->{keys}}) {
  6         9  
371 6         30 my $key_index_hash = $self->get_table_keys($table_name, $key_column);
372 6         7 my $value = $data->{$key_column}; # TODO: later use sub_ref to get value
373 6         6 my $key_index = $key_index_hash->{$value};
374 6 50       14 $key_index_hash->{$value} = $key_index = [] unless $key_index;
375 6         12 push (@$key_index, $next_idx);
376             }
377              
378 6         8 $cache->[$next_idx] = $data;
379 6         6 $index->[$next_idx] = $index_str;
380 6         13 return scalar @$cache;
381             }
382              
383             # --- update the index key(s) ----------------------------------------------
384             sub update_index {
385 1     1 0 569 my ($self, # instance_ref
386             $table_name, # cache (table) to store data...
387             $data, # optional: later update single data
388             ) = @_;
389              
390 1 50       4 croak "no table set" unless defined $table_name;
391              
392 1         2 my $cache_arr = $self->get_table_cache($table_name);
393 1         2 my $index_arr = $self->get_table_index($table_name);
394              
395 1 50 33     7 croak "No data found for table '$table_name'"
396             unless $cache_arr && $index_arr;
397              
398             # print "### Update index for cache table $table_name\n";
399              
400 1         3 foreach my $index (0..$#$cache_arr) {
401 5         7 $self->_update_index_key($table_name, $index);
402             }
403             }
404              
405             # --- update the index key ----------------------------------------------
406             sub _update_index_key {
407 5     5   4 my ($self, # instance_ref
408             $table_name, # cache (table) to store data...
409             $index # internal index, where data is stored
410             ) = @_;
411              
412 5         6 my $cache_arr = $self->get_table_cache($table_name);
413 5         6 my $index_arr = $self->get_table_index($table_name);
414              
415 5         4 my $data = $cache_arr->[$index];
416              
417 5 50       11 return unless $data;
418              
419 5         6 my $index_str = $self->build_index_string($table_name, $data, $index);
420              
421             # print "$index_str\n";
422              
423 5         9 $index_arr->[$index] = $index_str;
424             }
425              
426             # --- build the string to add to the index for searching -------------------
427             sub build_index_string {
428 11     11 0 13 my ($self, # instance_ref
429             $table_name, # cache (table) to store data...
430             $data,
431             $index
432             ) = @_;
433              
434 11         14 my $table_def = $self->get_table_definition($table_name);
435              
436 11         24 my $index_str = "#|$index|#";
437              
438 11         9 my $value;
439 11         8 foreach my $key (@{$table_def->{keys}}, @{$table_def->{columns}}) {
  11         13  
  11         15  
440 66         58 $value = $data->{$key};
441 66 50       78 next unless defined $value;
442 66         74 $index_str .= "#|$key=$value|#"
443             }
444              
445 11         18 return $index_str;
446             }
447              
448             # --- Same like SQL DELETE ---------------------------------
449              
450             sub delete {
451 1     1 0 8 my ($self, # instance_ref
452             %query, # perl query
453             ) = @_;
454              
455 1         4 my $tid_list = $self->_find_matches(%query);
456              
457 1         2 my $table_name = $query{from};
458 1         2 my $string_index_field = $self->get_table_index($table_name);
459 1         6 my $cache = $self->get_table_cache($table_name);
460              
461 1         1 my $deleted_rows_count = 0;
462 1         2 my $next_free_idx = $self->{_next_free_index_list}->{$table_name};
463 1 50       3 $next_free_idx = scalar @$cache unless defined $next_free_idx;
464            
465 1         2 foreach my $index (@$tid_list) {
466 1         2 my $object = $cache->[$index];
467 1 50       4 $next_free_idx = $index if $index < $next_free_idx;
468 1         2 $cache->[$index] = '';
469 1         1 $string_index_field->[$index] = '';
470            
471 1         2 my $table_key_indexes = $self->get_table_keys($table_name);
472 1         2 foreach my $column_key (keys(%$table_key_indexes)) {
473 1         2 my $values_hash = $table_key_indexes->{$column_key};
474 1         1 my $value = $object->{$column_key}; #TODO get value
475 1         1 my $index_arr_ref = $values_hash->{$value};
476 1         2 my $new_index_arr_ref = $values_hash->{$value} = [];
477 1         2 foreach my $other_index (@$index_arr_ref) {
478 1 50       9 push (@$new_index_arr_ref, $other_index) if $index != $other_index;
479             }
480             }
481 1         2 $deleted_rows_count++;
482             }
483              
484 1         1 $self->{_next_free_index_list}->{$table_name} = $next_free_idx;
485              
486 1         3 return $deleted_rows_count;
487             }
488              
489             # --- extract touple ids from selection list ---------------------------------
490             sub extract_tid_from_selection {
491 23     23 0 22 my ($self, # instance_ref
492             $selection, # perl query
493             ) = @_;
494              
495             my @result = map {
496 23 100       25 if (/^#\|(\d+)\|#/) {
  62         141  
497 61         104 $1;
498             } else {
499             ()
500 1         3 }
501             } @$selection;
502              
503 23         46 return \@result;
504             }
505              
506             # --- Same like SQL SELECT ---------------------------------
507             sub select {
508 25     25 0 8877 my ($self, # instance_ref
509             %query, # perl query
510             ) = @_;
511              
512 25         60 my $tids = $self->_find_matches(%query);
513              
514 24         36 my $table_name = $query{from};
515 24         35 my $cache = $self->get_table_cache($table_name);
516              
517 42         64 my @result = map {
518 24         28 $cache->[$_];
519             } @$tids;
520              
521 24         24 my $result_ref = \@result;
522            
523 24         24 my $columns = $query{what};
524              
525 24 50       51 $result_ref = $self->_select_columns($columns, $result_ref)
526             if $columns;
527              
528 24         66 return $result_ref;
529             }
530              
531             # --- select columns ----------------------
532              
533             sub _select_columns {
534 24     24   28 my ($self, # instance_ref
535             $columns, # string 'all' or ArrayRef with column names
536             $results, # ArrayRef of results
537             ) = @_;
538              
539 24         23 my $value_type = ref ($columns);
540 24 100       48 if ($value_type) {
    50          
541 1 50       3 if ($value_type eq 'ARRAY') {
542 3         4 my @what_list = map {
543 1         2 my $result_row = {};
544 3         5 foreach my $key (@$columns) {
545 6         7 $result_row->{$key} = $_->{$key};
546             }
547 3         5 $result_row;
548             } @$results;
549 1         2 $results = \@what_list;
550             }
551             else {
552 0         0 croak "unknown column selection by what => $value_type ";
553             }
554             }
555             elsif ($columns eq 'all') {
556             }
557             else {
558 0         0 croak "unknown column selection by what => '$columns'";
559             }
560 24         26 return $results;
561             }
562              
563             # --- Find matching elements --- used for selection and removing ------
564             sub _find_matches {
565 26     26   35 my ($self, # instance_ref
566             %query, # perl query
567             ) = @_;
568              
569 26         27 my $table_name = $query{from};
570 26         36 my $table_def = $self->get_table_definition($table_name);
571 26         35 my $string_index_field = $self->get_table_index($table_name);
572 26         30 my $key_index_field = $self->get_table_keys($table_name);
573 26         32 my $table_objs = $self->get_table_cache($table_name);
574              
575 26         25 my $search_str = "";
576              
577             # my %conditions = $self->_handle_where($query{where});
578 26         45 my $query = $self->_handle_where($query{where}, $key_index_field);
579 26         27 my $index_keys = $query->{indexes};
580 26         24 my $conditions = $query->{conditions};
581 26         25 my $values = $query->{values};
582 26         20 my $step2_operations = $query->{step2_operations};
583              
584 26         18 my @selection;
585 26         22 my $first_search = 1;
586 26         18 my $tids_first_run;
587              
588 26 100 100     70 if ($index_keys && scalar @$index_keys) {
589 3         3 $first_search = 0;
590 3         3 my ($column, $value);
591              
592 3         8 while (defined ($column = shift @$index_keys)) {
593 3         4 $value = shift @$index_keys;
594 3         6 my $matches = $key_index_field->{$column}->{$value};
595 3 50       6 if ($tids_first_run) {
596 0 0       0 if (scalar @$tids_first_run < scalar @$matches) {
597 0         0 $tids_first_run = $matches;
598             }
599             }
600             else {
601 3         7 $tids_first_run = $matches;
602             }
603             }
604             }
605              
606 26 100       36 if ($first_search) {
607 23         20 $search_str = "";
608              
609 23         20 foreach my $key (@{$table_def->{keys}}, @{$table_def->{columns}}) {
  23         24  
  23         38  
610            
611             # print "#### '$key'\n";
612            
613 138 100       207 if ($conditions->{$key}) {
614 14 100       22 $search_str .= ".*" if $search_str;
615 14         27 $search_str .= $conditions->{$key};
616             }
617             }
618             # print "##### search string: $search_str\n";
619 23 100       28 if ($search_str) {
620 12         249 @selection = grep (/$search_str/, @$string_index_field);
621             }
622             else {
623             # --- empty condition, select all -------
624 11         20 @selection = @$string_index_field;
625             }
626            
627 23         42 $tids_first_run = $self->extract_tid_from_selection(\@selection);
628             }
629             else {
630 3         3 my @step1_operations;
631 3         9 foreach my $key (keys(%$conditions)) {
632 0         0 my $pattern = $values->{$key};
633             # print "$key => $pattern\n";
634 0     0   0 push (@step1_operations, sub { $_->{$key} =~ /$pattern/ });
  0         0  
635             }
636 3         6 $step2_operations = [@step1_operations, @$step2_operations];
637             }
638              
639             # print Dumper($tids_first_run);
640              
641 26         22 my $tids = $tids_first_run;
642              
643 26 100 100     65 if ($step2_operations && scalar @$step2_operations) {
644 7         7 $tids = [];
645 7         8 foreach my $tid (@$tids_first_run) {
646 25         26 local $_ = $table_objs->[$tid];
647 25         15 my $selected = 1;
648             # print "$tid\n";
649 25         25 foreach my $selection_sub (@$step2_operations) {
650             # print Dumper ($selection_sub);
651 31 100       46 unless ($selection_sub->()) {
652 16         29 $selected = 0;
653 16         13 last;
654             }
655             }
656 24 100       64 push (@$tids, $tid) if $selected;
657             }
658             }
659              
660 25         94 return $tids;
661             }
662              
663             # --- handle where condition part -----------------------------
664              
665             sub _handle_where {
666 26     26   28 my ($self, # instance_ref
667             $where_condition, # perl query
668             $table_indexes, # hash with all indexes of table
669             ) = @_;
670              
671 26         25 my $conditions = {};
672 26         22 my $values = {};
673 26         25 my $operation_func_used = 0;
674 26         18 my $index = 0;
675 26         22 my $search_str;
676              
677 26 100       40 return $conditions unless $where_condition;
678            
679 23         28 my $compare_subs = $self->get_compare_subs();
680 23         27 my $combine_subs = $self->get_combine_subs();
681 23         29 my $step2_subs = $self->get_step2_subs();
682              
683 23         18 my @step2_operation_list;
684              
685             my @indexes;
686            
687 23         43 while ($index < $#$where_condition) {
688 27         29 $operation_func_used = 0;
689 27         21 my $operation = '?';
690 27         31 my $param_1 = $where_condition->[$index++];
691 27         41 my $param_2 = $where_condition->[$index++];
692              
693 27         28 my $param_2_ref = ref $param_2;
694              
695             # print "###1 operation: $operation ($param_2_ref)\n";
696              
697 27 100       66 if (!$param_2_ref) {
    100          
    100          
    50          
698 11         10 $operation = 'is';
699             }
700             elsif ($param_2_ref eq "ARRAY") {
701 2         2 $operation = 'in';
702             }
703             elsif ($param_2_ref eq "HASH") {
704 8         14 ($operation) = keys %$param_2;
705 8         13 ($param_2) = values %$param_2;
706             }
707             elsif ($param_2_ref eq "CODE") {
708 6         5 local $_;
709 6 100       12 if ($param_1 eq 'data') {
    100          
710 1         2 $operation = 'data';
711             }
712             elsif ($param_1 eq 'obj') {
713 4         3 $operation = 'obj';
714             }
715             else {
716 1         2 $operation = 'sub_value';
717             }
718             }
719             else {
720             # print "####2 Dump param_2 = " .Dumper ($param_2);
721 0         0 print "###3 operation: $operation ($param_1, $param_2) ($param_2_ref)\n";
722            
723 0         0 return ();
724             }
725            
726 27         39 my $parameters = [$param_1, $param_2];
727              
728 27         31 my $comb_ref = $combine_subs->{$operation};
729              
730 27 50       38 return $comb_ref->($parameters, $table_indexes) if $comb_ref;
731              
732 27         18 my $use_index = 0;
733              
734 27 100       41 if ($operation eq 'is') {
735 11         11 my $column = $parameters->[0];
736 11         10 my $value = $parameters->[1];
737             # print "is $column => $value => $table_indexes\n";
738 11 100       26 if ($table_indexes->{$column}) {
739             # print "# --- use key index field [$column]\n";
740 3         3 $use_index = 1;
741 3         5 push (@indexes, $column, $value);
742             }
743             }
744              
745 27 100       41 unless ($use_index) {
746 24         26 my $cond_ref = $compare_subs->{$operation};
747              
748 24 100       32 if ($cond_ref) {
749 15         14 my $column = $parameters->[0];
750 15         23 ($conditions->{$column}, $values->{$column}) = $cond_ref->($parameters);
751             } else {
752 9         10 my $step2_operation = $step2_subs->{$operation};
753            
754 9 50       11 if ($step2_operation) {
755 9         16 push (@step2_operation_list, $step2_operation->($parameters));
756 9         25 next;
757             }
758             else {
759             # parse_error ("Unknown operation '$operation'")
760 0 0       0 unless ($cond_ref) {
761 0         0 warn ("Unknown operation '$operation'");
762 0     0   0 $cond_ref = sub { return 0; };
  0         0  
763             }
764             }
765             }
766             }
767             }
768              
769             return {
770 23         81 indexes => \@indexes,
771             conditions => $conditions,
772             values => $values,
773             step2_operations => \@step2_operation_list};
774             }
775              
776             # --- parse error --------------------------------------------
777              
778             sub parse_error {
779 0     0 0 0 my $message = shift;
780              
781 0         0 croak "PQL-Parser: $message";
782             }
783              
784             # --- escape character with special meanings in reg exes for exact search ------------
785             sub escape_regular_expression_special_chars {
786 30     30 0 27 my ($self, # instance_ref
787             $text # text to escape chars in it
788             ) = @_;
789              
790 30         26 my $result_text = '';
791 30         22 my $rest = $text;
792              
793 30 50       38 return $text unless $text;
794              
795 30         59 while ($text =~ /([\/\(\)\[\]\.\?\+\*\^\$\|\{\}\\])/o) {
796 3         9 $result_text .= $`."\\".$1;
797 3         7 $rest = $text = $';
798             }
799              
800 30         30 $result_text .= $rest;
801 30         42 return $result_text;
802             }
803              
804             # --- export indexes for human analysis -----------------------
805             # don't use data in any scripting !!!!!!!!!!!!!!!
806              
807             sub export_indexes {
808 0     0 0   my ($self, # instance_ref
809             $table
810             ) = @_;
811              
812 0           my @results;
813              
814 0 0         if ($table) {
815 0           my $string_index_field = $self->get_table_index($table);
816 0           foreach my $row (@$string_index_field) {
817 0           push (@results, "table:$table$row");
818             }
819             }
820             else {
821 0           foreach $table (sort(keys(%{$self->{_index}}))) {
  0            
822 0           my $string_index_field = $self->get_table_index($table);
823 0           foreach my $row (@$string_index_field) {
824 0           push (@results, "table:$table$row");
825             }
826             }
827             }
828 0           return \@results;
829             }
830              
831             1;
832              
833             __END__