File Coverage

blib/lib/LittleORM/Filter.pm
Criterion Covered Total %
statement 21 401 5.2
branch 0 104 0.0
condition 0 62 0.0
subroutine 7 50 14.0
pod 0 27 0.0
total 28 644 4.3


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3 1     1   1196 use strict;
  1         2  
  1         40  
4              
5             package LittleORM::Model;
6 1     1   5 use LittleORM::Model::Field ();
  1         2  
  1         442  
7              
8             # Extend LittleORM::Model capabilities with filter support:
9              
10             sub f
11             {
12 0     0 0   my $self = shift;
13 0           return $self -> filter( @_ );
14             }
15              
16             sub _disambiguate_filter_args
17             {
18 0     0     my ( $self, $args ) = @_;
19              
20             {
21 0           assert( ref( $args ) eq 'ARRAY', 'sanity assert' );
  0            
22              
23 0           my $argsno = scalar @{ $args };
  0            
24 0   0       my $class = ( ref( $self ) or $self );
25 0           my @disambiguated = ();
26              
27 0           my $i = 0;
28 0           foreach my $arg ( @{ $args } )
  0            
29             {
30 0 0 0       if( blessed( $arg ) and $arg -> isa( 'LittleORM::Filter' ) )
    0 0        
31             {
32 0 0         unless( $i % 2 )
33             {
34             # this will wrk only with single column PKs
35              
36 0 0         if( my $attr_co_connect = &LittleORM::Filter::find_corresponding_fk_attr_between_models( $class,
    0          
37             $arg -> model() ) )
38             {
39 0           push @disambiguated, $attr_co_connect;
40 0           $i ++;
41              
42             } elsif( my $rev_connect = &LittleORM::Filter::find_corresponding_fk_attr_between_models( $arg -> model(),
43             $class ) )
44             {
45             # print $class, "\n";
46             # print $arg -> model(), "\n";
47             # print $rev_connect, "\n";
48              
49 0           my $to_connect_with = 0;
50              
51             {
52 0           assert( my $attr = $arg -> model() -> meta() -> find_attribute_by_name( $rev_connect ) );
  0            
53              
54 0 0         if( my $foreign_key_attr_name = &LittleORM::Model::__descr_attr( $attr, 'foreign_key_attr_name' ) )
55             {
56 0           $to_connect_with = $foreign_key_attr_name;
57             } else
58             {
59 0           $to_connect_with = $class -> __find_primary_key() -> name();
60             }
61              
62             }
63              
64 0           push @disambiguated, $to_connect_with;
65 0 0         unless( $arg -> returning() )
66             {
67 0           $arg -> returning( $rev_connect );
68             }
69              
70 0           $i ++;
71              
72              
73             } else
74             {
75 0           assert( 0,
76             sprintf( "Can not automatically connect %s and %s - do they have FK between?",
77             $class,
78             $arg -> model() ) );
79             }
80             }
81             } elsif( blessed( $arg ) and $arg -> isa( 'LittleORM::Clause' ) )
82             {
83 0 0         unless( $i % 2 )
84             {
85 0           push @disambiguated, '_clause';
86 0           $i ++;
87             }
88             }
89              
90 0           push @disambiguated, $arg;
91 0           $i ++;
92             }
93 0           $args = \@disambiguated;
94             }
95              
96 0           return $args;
97             }
98              
99             sub filter
100             {
101 0     0 0   my ( $self, @args ) = @_;
102              
103 0   0       my $class = ( ref( $self ) or $self );
104              
105 0           my $rv = LittleORM::Filter -> new( model => $class );
106              
107 0           $rv -> push_anything_appropriate( @args );
108              
109 0           return $rv;
110             }
111              
112              
113             package LittleORM::Filter;
114              
115             # Actual filter implementation:
116              
117 1     1   7 use Moose;
  1         1  
  1         9  
118              
119             has 'model' => ( is => 'rw', isa => 'Str', required => 1 );
120             has 'table_alias' => ( is => 'rw', isa => 'Str', default => \&get_uniq_alias_for_table );
121             has 'returning' => ( is => 'rw', isa => 'Maybe[Str]' ); # return column name for connecting with other filter
122             has 'returning_field' => ( is => 'rw', isa => 'Maybe[LittleORM::Model::Field]', default => undef );
123             has 'clauses' => ( is => 'rw', isa => 'ArrayRef[LittleORM::Clause]', default => sub { [] } );
124             has 'joined_tables' => ( is => 'rw', isa => 'ArrayRef[HashRef]', default => sub { [] } );
125              
126 1     1   6360 use Carp::Assert 'assert';
  1         2  
  1         8  
127 1     1   108 use List::MoreUtils 'uniq';
  1         1  
  1         10  
128 1     1   838 use LittleORM::Filter::Update ();
  1         2  
  1         16  
129 1     1   331 use LittleORM::Clause ();
  1         2  
  1         3053  
130              
131             {
132             my $counter = 0;
133              
134             sub get_uniq_alias_for_table
135             {
136 0     0 0   $counter ++;
137              
138 0           return "T" . $counter;
139             }
140              
141             }
142              
143             sub borrow_field
144             {
145 0     0 0   my $self = shift;
146              
147 0           my $rv = $self -> model() -> borrow_field( @_ );
148 0           $rv -> table_alias( $self -> table_alias() );
149 0           return $rv;
150             }
151              
152             sub push_anything_appropriate
153             {
154 0     0 0   my $self = shift;
155 0           my @args = @_;
156              
157 0           my @clauseargs = ();
158 0           assert( my $class = $self -> model(), 'must know my model' );
159              
160 0           @args = @{ $self -> model() -> _disambiguate_filter_args( \@args ) };
  0            
161 0           assert( scalar @args % 2 == 0 );
162              
163 0           while( my $arg = shift @args )
164             {
165 0           my $val = shift @args;
166              
167 0 0 0       if( $arg eq '_return' )
    0 0        
    0          
    0          
    0          
    0          
168             {
169 0 0         if( LittleORM::Model::Field -> this_is_field( $val ) )
170             {
171 0           $val -> assert_model( $class );
172 0           $self -> returning_field( $val );
173             } else
174             {
175              
176 0           assert( $self -> model() -> meta() -> find_attribute_by_name( $val ), sprintf( 'Incorrect %s attribute "%s" in return',
177             $class,
178             $val ) );
179 0           $self -> returning( $val );
180             }
181              
182             } elsif( $arg eq '_sortby' )
183             {
184 0           assert( 0, '_sortby is not allowed in filter' );
185              
186             } elsif( $arg eq '_exists' )
187             {
188 0   0       assert( $val and ( ( ref( $val ) eq 'HASH' )
189             or
190             $val -> isa( 'LittleORM::Filter' ) ) );
191 0           $self -> connect_filter_exists( 'EXISTS', $val );
192              
193             } elsif( $arg eq '_not_exists' )
194             {
195 0   0       assert( $val and ( ( ref( $val ) eq 'HASH' )
196             or
197             $val -> isa( 'LittleORM::Filter' ) ) );
198 0           $self -> connect_filter_exists( 'NOT EXISTS', $val );
199              
200             } elsif( blessed( $val ) and $val -> isa( 'LittleORM::Filter' ) )
201             {
202              
203 0           $self -> connect_filter( $arg => $val );
204              
205             } elsif( blessed( $val ) and $val -> isa( 'LittleORM::Clause' ) )
206             {
207 0           $self -> push_clause( $val );
208             } else
209             {
210 0           push @clauseargs, ( $arg, $val );
211             }
212              
213             }
214              
215             {
216 0 0         unless( @clauseargs )
  0            
217             {
218 0           @clauseargs = ( _where => '1=1' );
219             }
220 0           my $clause = LittleORM::Clause -> new( model => $class,
221             cond => \@clauseargs,
222             table_alias => $self -> table_alias() );
223              
224 0           $self -> push_clause( $clause );
225             }
226              
227             }
228              
229             sub form_conn_sql
230             {
231 0     0 0   my ( $self, $arg, $filter ) = @_;
232              
233 0           my $conn_sql = '';
234              
235             {
236 0           my $ta1 = $self -> table_alias();
  0            
237 0           my $ta2 = $filter -> table_alias();
238              
239 0           my $attr1_t = '';
240 0           my $attr2_t = '';
241 0           my $cast = '';
242              
243 0           my $arg1 = $arg;
244 0           my $arg2 = $filter -> get_returning();
245              
246 0           my ( $f1, $f2 ) = ( '', '' );
247              
248             {
249 0           my $attr1 = $self -> model() -> meta() -> find_attribute_by_name( $arg1 );
  0            
250              
251 0   0       assert( ( $attr1 or LittleORM::Model::Field -> this_is_field( $arg1 ) ),
252             'Injalid attribute 1 in filter: ' . $arg1 );
253              
254 0           my $attr2 = $filter -> model() -> meta() -> find_attribute_by_name( $arg2 );
255 0   0       assert( ( $attr2 or LittleORM::Model::Field -> this_is_field( $arg2 ) ),
256             'Injalid attribute 2 in filter (much rarer case)' );
257              
258            
259 0 0 0       if( ( $attr1 and $attr2 ) and ( my $fk = &LittleORM::Model::__descr_attr( $attr1, 'foreign_key' ) ) )
      0        
260             {
261 0 0 0       if( ( $fk eq $filter -> model() )
262             and
263             ( my $fkattr = &LittleORM::Model::__descr_attr( $attr1, 'foreign_key_attr_name' ) ) )
264             {
265 0           assert( $attr2 = $filter -> model() -> meta() -> find_attribute_by_name( $fkattr ),
266             'Injalid attribute 2 in filter (subcase of much rarer case)' );
267             }
268             }
269 0 0         if( $attr1 )
270             {
271 0           $attr1_t = &LittleORM::Model::__descr_attr( $attr1, 'db_field_type' );
272 0           $f1 = sprintf( "%s.%s",
273             $ta1,
274             &LittleORM::Model::__get_db_field_name( $attr1 ) );
275            
276             } else
277             {
278 0           $f1 = $arg1 -> form_field_name_for_db_select( $ta1 );
279             }
280              
281 0 0         if( $attr2 )
282             {
283 0           $attr2_t = &LittleORM::Model::__descr_attr( $attr2, 'db_field_type' );
284 0           $f2 = sprintf( "%s.%s",
285             $ta2,
286             &LittleORM::Model::__get_db_field_name( $attr2 ) );
287              
288             } else
289             {
290 0           $f2 = $arg2 -> form_field_name_for_db_select( $ta2 );
291             }
292              
293 0 0 0       if( $attr1_t and $attr2_t and ( $attr1_t ne $attr2_t ) )
      0        
294             {
295 0           $cast = '::' . $attr1_t;
296             }
297              
298             }
299              
300              
301              
302 0           $conn_sql = sprintf( "%s=%s%s",
303             $f1,
304             $f2,
305             $cast );
306             }
307              
308 0           return $conn_sql;
309              
310             }
311              
312              
313             sub _look_for_connecting_args_in_args_and_do_it_in_a_compatible_way
314             {
315 0     0     my $self = shift;
316            
317 0           my @kws = ( '_clause' );
318 0           my %kws = map { $_ => 1 } @kws;
  0            
319              
320 0           my @rest_args = ();
321 0           my $connecting_args = {};
322              
323 0           my $next_arg_is_what_we_need = undef;
324              
325 0           foreach my $t ( @_ )
326             {
327 0 0         if( $next_arg_is_what_we_need )
    0          
328             {
329 0           assert( not exists $kws{ $t } );
330 0           $connecting_args -> { $next_arg_is_what_we_need } = $t;
331 0           $next_arg_is_what_we_need = undef;
332              
333             } elsif( exists $kws{ $t } )
334             {
335 0           $next_arg_is_what_we_need = $t;
336             } else
337             {
338 0           push @rest_args, $t;
339             }
340             }
341 0           assert( not defined $next_arg_is_what_we_need );
342              
343 0           return ( \@rest_args, $connecting_args );
344             }
345              
346             sub _get_connecting_clause_from_connecting_args
347             {
348 0     0     my ( $self, $args ) = @_;
349              
350 0           my $rv = $args -> { '_clause' };
351              
352 0 0         if( ref( $rv ) eq 'ARRAY' )
353             {
354 0           $rv = $self -> model() -> clause( @{ $rv } );
  0            
355             }
356              
357 0           return $rv;
358             }
359              
360             sub connect_filter
361             {
362 0     0 0   my $self = shift;
363              
364 0           my ( $rest_args, $connecting_args ) = $self -> _look_for_connecting_args_in_args_and_do_it_in_a_compatible_way( @_ );
365              
366 0           @_ = @{ $rest_args };
  0            
367 0           my $connecting_clause = $self -> _get_connecting_clause_from_connecting_args( $connecting_args );
368              
369 0           my ( $arg, $filter ) = $self -> _sanitize_args_for_connecting( ArgAndFilter => \@_,
370             ConnectingClause => $connecting_clause );
371              
372 0           map { $self -> push_clause( $_, $filter -> table_alias() ) } @{ $filter -> clauses() };
  0            
  0            
373              
374 0 0         unless( $connecting_clause )
375             {
376 0           my $conn_sql = $self -> form_conn_sql( $arg, $filter );
377              
378 0           $connecting_clause = $self -> model() -> clause( cond => [ _where => $conn_sql ],
379             table_alias => $self -> table_alias() );
380              
381             }
382              
383 0           $self -> push_clause( $connecting_clause );
384              
385 0           map { $self -> _self_add_table_join( $_ ) } @{ $filter -> joined_tables() };
  0            
  0            
386             }
387              
388             sub _valid_join_type
389             {
390 0     0     my ( $self, $jtype ) = @_;
391              
392 0           my @known = ( 'JOIN', 'INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'LEFT OUTER JOIN', 'RIGHT OUTER JOIN', 'MEGAJOIN 3000' );
393 0           my %known = map { $_ => 1 } @known;
  0            
394              
395 0           my $rv = 0;
396              
397 0 0         if( exists $known{ uc( $jtype ) } )
398             {
399 0           $rv = 1;
400             }
401              
402 0           return $rv;
403             }
404              
405             sub connect_filter_right_join
406             {
407 0     0 0   my $self = shift;
408 0           $self -> connect_filter_complex( 'RIGHT JOIN', @_ );
409             }
410              
411             sub connect_filter_right_outer_join
412             {
413 0     0 0   my $self = shift;
414 0           $self -> connect_filter_complex( 'RIGHT OUTER JOIN', @_ );
415             }
416              
417             sub connect_filter_left_join
418             {
419 0     0 0   my $self = shift;
420 0           $self -> connect_filter_complex( 'LEFT JOIN', @_ );
421             }
422              
423             sub connect_filter_left_outer_join
424             {
425 0     0 0   my $self = shift;
426 0           $self -> connect_filter_complex( 'LEFT OUTER JOIN', @_ );
427             }
428              
429             sub connect_filter_inner_join
430             {
431 0     0 0   my $self = shift;
432 0           $self -> connect_filter_complex( 'INNER JOIN', @_ );
433             }
434              
435              
436             sub connect_filter_join
437             {
438 0     0 0   my $self = shift;
439 0           $self -> connect_filter_complex( 'JOIN', @_ );
440             }
441              
442             sub connect_filter_complex
443             {
444 0     0 0   my $self = shift;
445 0           my $type = shift;
446              
447 0 0         if( $type )
448             {
449 0           assert( $self -> _valid_join_type( $type ), 'I dont know this join type: ' . $type );
450              
451 0           my ( $rest_args, $connecting_args ) = $self -> _look_for_connecting_args_in_args_and_do_it_in_a_compatible_way( @_ );
452 0           @_ = @{ $rest_args };
  0            
453              
454 0           my $connecting_clause = $self -> _get_connecting_clause_from_connecting_args( $connecting_args );
455 0           my ( $arg, $filter ) = $self -> _sanitize_args_for_connecting( ArgAndFilter => \@_,
456             ConnectingClause => $connecting_clause );
457            
458 0           map { $self -> push_clause( $_, $filter -> table_alias() ) } @{ $filter -> clauses() };
  0            
  0            
459              
460 0           my $conn_sql = undef;
461            
462 0 0         if( $connecting_clause )
463             {
464 0           $conn_sql = $connecting_clause -> sql();
465             } else
466             {
467 0           $conn_sql = $self -> form_conn_sql( $arg, $filter );
468             }
469            
470 0           my %join_spec = ( type => $type,
471             to => { $self -> model() -> _db_table() => $self -> table_alias() },
472             table => { $filter -> model() -> _db_table() => $filter -> table_alias() },
473             on => $conn_sql );
474            
475 0           $self -> _self_add_table_join( \%join_spec );
476            
477 0           map { $self -> _self_add_table_join( $_ ) } @{ $filter -> joined_tables() };
  0            
  0            
478              
479             } else
480             {
481 0           $self -> connect_filter( @_ );
482             }
483             }
484              
485             sub _self_add_table_join
486             {
487 0     0     my ( $self, $join_spec ) = @_;
488              
489 0           push @{ $self -> joined_tables() }, $join_spec;
  0            
490             }
491              
492             sub _sanitize_args_for_connecting
493             {
494              
495 0     0     my $self = shift;
496              
497 0           my %args = @_;
498             my ( $arg_and_filter,
499 0           $connecting_clause ) = @args{ 'ArgAndFilter',
500             'ConnectingClause' };
501              
502 0           my ( $arg, $filter ) = @{ $arg_and_filter };
  0            
503              
504 0 0         unless( $filter )
505             {
506 0 0         if( ref( $arg ) eq 'HASH' )
507             {
508 0           assert( scalar keys %{ $arg } == 1 );
  0            
509 0           ( $arg, $filter ) = %{ $arg };
  0            
510             }
511             }
512              
513 0 0         unless( $filter )
514             {
515 0 0 0       if( $arg and blessed( $arg ) and $arg -> isa( 'LittleORM::Filter' ) )
      0        
516             {
517 0           $filter = $arg;
518              
519 0 0         unless( $connecting_clause )
520             {
521 0           my $args = $self -> model() -> _disambiguate_filter_args( [ $arg ] );
522              
523 0           ( $arg, $filter ) = @{ $args };
  0            
524             }
525              
526             } else
527             {
528 0           assert( 0, 'check args sanity' );
529             }
530             }
531              
532 0           return ( $arg, $filter );
533             }
534              
535             sub connect_filter_exists
536             {
537 0     0 0   my $self = shift;
538 0           my $exists_keyword = shift;
539              
540 0           my ( $arg, $filter ) = $self -> _sanitize_args_for_connecting( ArgAndFilter => \@_ );
541              
542 0           my $exf = LittleORM::Filter -> new( model => $filter -> model(),
543             table_alias => $filter -> table_alias() );
544            
545              
546 0           map { $exf -> push_clause( $_, $filter -> table_alias() ) } @{ $filter -> clauses() };
  0            
  0            
547            
548 0           my $conn_sql = $self -> form_conn_sql( $arg, $filter );
549              
550             {
551 0           my $c1 = $self -> model() -> clause( cond => [ _where => $conn_sql ],
552             table_alias => $self -> table_alias() );
553              
554              
555 0           $exf -> push_clause( $c1 );
556             }
557              
558             {
559              
560 0           my $select_from_sql_part = '';
  0            
  0            
561              
562             {
563 0           my %t = $exf -> all_tables_used_in_filter();
  0            
564             # do not include outer table inside EXISTS select:
565 0           $select_from_sql_part = join( ',', map { $t{ $_ } .
566             " " .
567             $_ }
568 0           grep { $_ ne $self -> table_alias() }
  0            
569             keys %t );
570              
571             }
572              
573 0           my $sql = sprintf( " %s (SELECT 1 FROM %s WHERE %s LIMIT 1) ",
574             $exists_keyword,
575             $select_from_sql_part,
576             join( ' AND ', $exf -> translate_into_sql_clauses() ) );
577            
578 0           my $c1 = $self -> model() -> clause( cond => [ _where => $sql ],
579             table_alias => $self -> table_alias() );
580            
581            
582 0           $self -> push_clause( $c1 );
583             }
584            
585 0           return 0;
586             }
587              
588             sub push_clause
589             {
590 0     0 0   my ( $self, $clause, $table_alias ) = @_;
591              
592              
593 0 0         unless( $clause -> table_alias() )
594             {
595 0 0         unless( $table_alias )
596             {
597 0 0         if( $self -> model() eq $clause -> model() )
598             {
599 0           $table_alias = $self -> table_alias();
600              
601             # maybe clone here to preserve original clause obj ?
602 0           my $copy = bless( { %{ $clause } }, ref $clause );
  0            
603 0           $clause = $copy;
604 0           $clause -> table_alias( $table_alias );
605             }
606             }
607             }
608              
609 0 0         if( $clause -> table_alias() )
610             {
611              
612 0           push @{ $self -> clauses() }, $clause;
  0            
613              
614             } else
615             {
616 0           assert( $self -> model() ne $clause -> model(), 'sanity assert' );
617              
618 0           my $other_model_filter = $clause -> model() -> filter( $clause );
619 0           $self -> connect_filter( $other_model_filter );
620              
621              
622             }
623              
624              
625              
626             # if( $self -> model() eq $clause -> model() )
627             # {
628              
629             # } else
630             # {
631             # my $other_model_filter = $clause -> model() -> filter( _clause => $clause );
632             # $self -> connect_filter( $other_model_filter );
633             # }
634              
635              
636 0           return $self -> clauses();
637             }
638              
639             sub get_returning
640             {
641 0     0 0   my $self = shift;
642              
643 0           my $rv = $self -> returning();
644            
645 0 0         if( $rv )
    0          
646             {
647 0           1;
648             } elsif( my $rv_f = $self -> returning_field() )
649             {
650 0           $rv = $rv_f;
651              
652             } else
653             {
654 0           assert( my $pk = $self -> model() -> __find_primary_key(),
655             sprintf( 'Model %s must have PK or specify "returning" manually',
656             $self -> model() ) );
657 0           $rv = $pk -> name();
658             }
659              
660 0           return $rv;
661              
662             }
663              
664             sub translate_into_sql_clauses
665             {
666 0     0 0   my $self = shift;
667 0           my @args = @_;
668              
669 0           my $clauses_number = scalar @{ $self -> clauses() };
  0            
670              
671 0           my @all_clauses_together = ();
672              
673 0           for( my $i = 0; $i < $clauses_number; $i ++ )
674             {
675 0           my $clause = $self -> clauses() -> [ $i ];
676 0           my $sql = $clause -> sql( $self -> _grep_out_non_system_and_clauses( @args ) );
677 0           push @all_clauses_together, $sql;
678             }
679              
680 0           return @all_clauses_together;
681             }
682              
683             sub _grep_out_non_system_and_clauses
684             {
685 0     0     my $self = shift;
686              
687 0           my @args = @_;
688 0           my @rv = ();
689              
690 0           while( my $arg = shift @args )
691             {
692 0           my $val = shift @args;
693 0 0 0       if( ( $arg =~ /^_/ ) and ( $arg ne '_clause' ) ) # looks crutchy
694             {
695 0           push @rv, ( $arg, $val );
696             }
697             }
698              
699 0           return @rv;
700             }
701              
702             sub _table_spec_with_join_support
703             {
704 0     0     my ( $self, $table, $depth ) = @_;
705              
706 0   0       $depth = ( $depth or 0 );
707              
708 0           assert( $depth < 100, 'Too deep in.' );
709              
710 0           my ( $tn, $ta ) = %{ $table };
  0            
711              
712 0           my $rv = '';
713              
714 0 0 0       if( ( $depth == 0 ) and &__in_skip_list( my $s = $tn . ' ' . $ta ) )
    0          
715             {
716 0           return $rv;
717              
718             } elsif( $depth ) # or &__in_skip_list( $s ) )
719             {
720 0           1;
721             } else
722             {
723 0           $rv = $s;
724             }
725              
726 0           foreach my $jt ( @{ $self -> joined_tables() } )
  0            
727             {
728 0           my ( $jt_to_n, $jt_to_a ) = %{ $jt -> { 'to' } };
  0            
729              
730 0 0 0       if( ( $jt_to_n eq $tn )
731             and
732             ( $jt_to_a eq $ta ) )
733             {
734              
735 0           my ( $jt_n, $jt_a ) = %{ $jt -> { 'table' } };
  0            
736 0           my $jspec = $jt_n . ' ' . $jt_a;
737              
738             $rv .= ' ' .
739             $jt -> { 'type' } .
740             ' ' .
741             $jspec .
742 0           ' ON ( ' . $jt -> { 'on' } . ' ) ';
743              
744 0           &__add_to_skip_list( $jspec );
745 0           $rv .= $self -> _table_spec_with_join_support( $jt -> { 'table' }, $depth + 1 );
746             }
747             }
748 0           return $rv;
749             }
750              
751             {
752             # revisit later TODO
753              
754             my %skip_list = ();
755              
756             sub __clear_skip_list
757             {
758 0     0     %skip_list = ();
759             }
760              
761             sub __add_to_skip_list
762             {
763 0     0     my $what = shift;
764 0           $skip_list{ $what } = 1;
765             }
766              
767             sub __in_skip_list
768             {
769 0     0     my $what = shift;
770              
771 0           my $rv = 0;
772            
773 0 0         if( exists $skip_list{ $what } )
774             {
775 0           $rv = 1;
776             }
777 0           return $rv;
778             }
779             }
780              
781              
782             sub _all_tables_used_in_filter_joinable # TODO
783             {
784 0     0     my $self = shift;
785              
786 0           my @rv = ();
787              
788 0           my %skip_duplicates = ();
789              
790 0           &__clear_skip_list();
791              
792             J1Dz1VhnaYMJllvy:
793 0           foreach my $c ( @{ $self -> clauses() } )
  0            
794             {
795 0           my $t = $c -> model() -> _db_table();
796 0           assert( my $ta = $c -> table_alias(), 'Unknown clause origin' );
797              
798 0 0         if( exists $skip_duplicates{ $ta } )
799             {
800 0           1;
801             } else
802             {
803 0 0         if( my $spec = $self -> _table_spec_with_join_support( { $t => $ta } ) )
804             {
805 0           push @rv, $spec;
806             }
807              
808 0           $skip_duplicates{ $ta } = 1;
809             }
810             }
811              
812 0           &__clear_skip_list();
813              
814 0           return \@rv;
815             }
816              
817             sub all_tables_used_in_filter
818             {
819 0     0 0   my $self = shift;
820              
821 0           my %rv = ();
822              
823             J1Dz1VhnaYMJllvy:
824 0           foreach my $c ( @{ $self -> clauses() } )
  0            
825             {
826 0           my $t = $c -> model() -> _db_table();
827 0           assert( my $ta = $c -> table_alias(), 'Unknown clause origin' );
828              
829             # foreach my $join_spec ( @{ $self -> joined_tables() } )
830             # {
831             # if( exists $join_spec -> { 'table' } -> { $t } )
832             # {
833             # next J1Dz1VhnaYMJllvy;
834             # }
835             # }
836 0           $rv{ $ta } = $t;
837             }
838              
839 0           return %rv;
840             }
841              
842             sub get_many
843             {
844 0     0 0   my $self = shift;
845              
846 0           return $self -> call_orm_method( 'get_many', @_,
847             &LittleORM::Model::__for_read() );
848             }
849              
850             sub get
851             {
852 0     0 0   my $self = shift;
853              
854 0           return $self -> call_orm_method( 'get', @_,
855             &LittleORM::Model::__for_read() );
856             }
857              
858             sub count
859             {
860 0     0 0   my $self = shift;
861              
862 0           return $self -> call_orm_method( 'count', @_,
863             &LittleORM::Model::__for_read() );
864             }
865              
866             sub max
867             {
868 0     0 0   my $self = shift;
869              
870 0           return $self -> call_orm_method( 'max', @_,
871             &LittleORM::Model::__for_read() );
872             }
873              
874             sub min
875             {
876 0     0 0   my $self = shift;
877              
878 0           return $self -> call_orm_method( 'min', @_,
879             &LittleORM::Model::__for_read() );
880             }
881              
882             sub delete
883             {
884 0     0 0   assert( 0, 'Delete is not supported in LittleORM::Filter. Just map { $_ -> delete() } at what get_many() returns.' );
885             }
886              
887             sub call_orm_method
888             {
889 0     0 0   my $self = shift;
890 0           my $method = shift;
891              
892 0           my @args = @_;
893              
894 0           my %all = $self -> all_tables_used_in_filter();
895 0           my $all = $self -> _all_tables_used_in_filter_joinable();
896            
897 0           my @targs = $self -> _correct_args_for_sql_translation_when_calling_certain_orm_methods( $method, @args );
898              
899             return $self -> model() -> $method( $self -> _correct_args_for_calling_certain_orm_methods( $method, @args ),
900             _table_alias => $self -> table_alias(),
901 0           _tables_used => [ map { sprintf( "%s %s", $all{ $_ }, $_ ) } keys %all ],
  0            
902             _tables_to_select_from => $all,
903             _where => join( ' AND ', $self -> translate_into_sql_clauses( @targs ) ) );
904             }
905              
906             sub _correct_args_for_calling_certain_orm_methods
907             {
908 0     0     my $self = shift;
909 0           my $method = shift;
910              
911 0           my @args = @_;
912              
913 0     0     my $replace_attr_with_field = sub { my $method = shift;
914 0           my @attrs = @_;
915 0           my $aname = $attrs[ 0 ];
916 0 0         unless( LittleORM::Model::Field -> this_is_field( $aname ) )
917             {
918 0           assert( my $attr = $self -> model() -> meta() -> find_attribute_by_name( $aname ) );
919 0           my $f = $self -> model() -> borrow_field( $aname,
920             select_as => $method );
921 0           $attrs[ 0 ] = $f;
922             }
923 0           return @attrs; };
  0            
924            
925 0           my %cleanse = ( min => $replace_attr_with_field,
926             max => $replace_attr_with_field );
927              
928 0 0         if( my $code = $cleanse{ $method } )
929             {
930 0           @args = $code -> ( $method, @args );
931             }
932              
933 0           return @args;
934             }
935              
936             sub _correct_args_for_sql_translation_when_calling_certain_orm_methods
937             {
938 0     0     my $self = shift;
939 0           my $method = shift;
940              
941 0           my @args = @_;
942              
943 0     0     my $skip_first_arg = sub { shift @_ ; return @_; };
  0            
  0            
944              
945 0           my %cleanse = ( 'min' => $skip_first_arg,
946             'max' => $skip_first_arg );
947              
948 0 0         if( my $code = $cleanse{ $method } )
949             {
950 0           @args = $code -> ( @args );
951             }
952              
953 0           return @args;
954             }
955              
956             sub find_corresponding_fk_attr_between_models
957             {
958 0     0 0   my ( $model1, $model2 ) = @_;
959              
960 0           my $rv = undef;
961              
962             DQoYV7htzKfc5YJC:
963 0           foreach my $attr ( $model1 -> meta() -> get_all_attributes() )
964             {
965 0 0         if( my $fk = &LittleORM::Model::__descr_attr( $attr, 'foreign_key' ) )
966             {
967 0 0         if( $fk eq 'yes' )
968             {
969 0           assert( my $tc = $attr -> type_constraint(),
970             sprintf( '%s attr type_constraint() is missing, did you specify "isa"?',
971             $attr -> name() ) );
972 0           assert( $fk = $tc -> name() );
973             }
974              
975 0 0         if( $model2 eq $fk )
976             {
977 0           $rv = $attr -> name();
978             }
979             }
980             }
981            
982 0           return $rv;
983             }
984              
985             42;