line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Query::Abstract; |
2
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
65356
|
use v5.10; |
|
3
|
|
|
|
|
11
|
|
|
3
|
|
|
|
|
146
|
|
4
|
3
|
|
|
3
|
|
18
|
use strict; |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
123
|
|
5
|
3
|
|
|
3
|
|
16
|
use warnings; |
|
3
|
|
|
|
|
10
|
|
|
3
|
|
|
|
|
155
|
|
6
|
|
|
|
|
|
|
|
7
|
3
|
|
|
3
|
|
2000
|
use Class::Load qw/load_class/; |
|
3
|
|
|
|
|
89844
|
|
|
3
|
|
|
|
|
176
|
|
8
|
3
|
|
|
3
|
|
21
|
use Carp qw/croak/; |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
120
|
|
9
|
3
|
|
|
3
|
|
1715
|
use Data::Dumper; |
|
3
|
|
|
|
|
17421
|
|
|
3
|
|
|
|
|
1897
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
sub new { |
14
|
2
|
|
|
2
|
0
|
28
|
my $class = shift; |
15
|
2
|
|
|
|
|
8
|
my %args = @_; |
16
|
2
|
|
|
|
|
5
|
my $driver = $args{driver}; |
17
|
2
|
50
|
|
|
|
10
|
croak "Wrong driver" unless ref $driver; |
18
|
|
|
|
|
|
|
|
19
|
2
|
|
|
|
|
7
|
my $self = bless {}, $class; |
20
|
|
|
|
|
|
|
|
21
|
2
|
50
|
|
|
|
9
|
if ( ref $driver eq 'ARRAY' ) { |
|
|
0
|
|
|
|
|
|
22
|
2
|
|
|
|
|
7
|
my $driver_class = 'Query::Abstract::Driver::' . $driver->[0]; |
23
|
2
|
|
|
|
|
11
|
load_class($driver_class); |
24
|
|
|
|
|
|
|
|
25
|
2
|
100
|
|
|
|
65
|
$self->{driver} = $driver_class->new( @{ $driver->[1] || [] } ); |
|
2
|
|
|
|
|
41
|
|
26
|
|
|
|
|
|
|
} elsif ( $driver->isa('Query::Abstract::Driver::Base') ) { |
27
|
0
|
|
|
|
|
0
|
$self->{driver} = $driver; |
28
|
|
|
|
|
|
|
} else { |
29
|
0
|
|
|
|
|
0
|
croak "Wrong driver [$driver]"; |
30
|
|
|
|
|
|
|
} |
31
|
|
|
|
|
|
|
|
32
|
2
|
|
|
|
|
9
|
$self->init(); |
33
|
|
|
|
|
|
|
|
34
|
2
|
|
|
|
|
8
|
return $self; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub init { |
38
|
2
|
|
|
2
|
0
|
4
|
my $self = shift; |
39
|
2
|
|
|
|
|
16
|
$self->{driver}->init(@_); |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub convert_query { |
44
|
19
|
|
|
19
|
1
|
41579
|
my ($self, @args) = @_; |
45
|
19
|
|
|
|
|
60
|
my %query = $self->_normalize_query(@args); |
46
|
19
|
|
|
|
|
93
|
return $self->{driver}->convert_query(%query); |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub convert_filter { |
50
|
0
|
|
|
0
|
1
|
0
|
my ($self, $filter) = @_; |
51
|
|
|
|
|
|
|
|
52
|
0
|
|
|
|
|
0
|
return $self->{driver}->convert_filter( |
53
|
|
|
|
|
|
|
$self->_normalize_where($filter) |
54
|
|
|
|
|
|
|
); |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub convert_sort { |
59
|
0
|
|
|
0
|
1
|
0
|
my ($self, $sort_by) = @_; |
60
|
|
|
|
|
|
|
|
61
|
0
|
|
|
|
|
0
|
return $self->{driver}->convert_sort( |
62
|
|
|
|
|
|
|
$self->_normalize_sort_by($sort_by) |
63
|
|
|
|
|
|
|
); |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub _normalize_query { |
67
|
19
|
|
|
19
|
|
23
|
my $self = shift; |
68
|
19
|
|
|
|
|
24
|
my %query; |
69
|
|
|
|
|
|
|
|
70
|
19
|
100
|
|
|
|
58
|
if ( ref($_[0]) eq 'ARRAY' ) { |
71
|
15
|
|
|
|
|
31
|
$query{where} = $_[0]; |
72
|
|
|
|
|
|
|
} else { |
73
|
4
|
|
|
|
|
9
|
%query = @_; |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
19
|
|
|
|
|
51
|
my $where = $self->_normalize_where($query{where}); |
77
|
19
|
|
|
|
|
59
|
my $sort_by = $self->_normalize_sort_by($query{sort_by}); |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
return ( |
80
|
19
|
|
|
|
|
77
|
where => $where, |
81
|
|
|
|
|
|
|
sort_by => $sort_by |
82
|
|
|
|
|
|
|
); |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub _normalize_where { |
86
|
19
|
|
|
19
|
|
25
|
my ($self, $where) = @_; |
87
|
19
|
50
|
|
|
|
50
|
return [] unless $where; |
88
|
|
|
|
|
|
|
|
89
|
19
|
|
|
|
|
14
|
my @norm_where; |
90
|
|
|
|
|
|
|
|
91
|
19
|
|
|
|
|
62
|
for (my $i = 0; $i < @$where; $i+=2) { |
92
|
25
|
|
|
|
|
59
|
my $field = $where->[$i]; |
93
|
25
|
|
|
|
|
21
|
my ($oper, $restriction); |
94
|
25
|
100
|
|
|
|
55
|
if ( ref($where->[$i+1]) eq 'HASH' ) { |
95
|
22
|
|
|
|
|
30
|
my $condition = $where->[$i+1]; |
96
|
22
|
|
|
|
|
64
|
($oper, $restriction) = %$condition; |
97
|
|
|
|
|
|
|
} else { |
98
|
3
|
50
|
|
|
|
15
|
$oper = ref($where->[$i+1]) eq 'ARRAY' ? 'in' : 'eq'; |
99
|
3
|
|
|
|
|
4
|
$restriction = $where->[$i+1]; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
325
|
|
|
|
|
381
|
die "UNSUPPORTED OPERATOR [$oper]" |
103
|
25
|
50
|
|
|
|
58
|
unless grep { $oper eq $_ } qw/eq in ne gt lt le gt ge like < > <= >=/; |
104
|
|
|
|
|
|
|
|
105
|
25
|
|
|
|
|
95
|
push @norm_where, $field => {$oper => $restriction} ; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
19
|
|
|
|
|
38
|
return \@norm_where; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub _normalize_sort_by { |
112
|
19
|
|
|
19
|
|
37
|
my ($self, $sort_by) = @_; |
113
|
19
|
100
|
|
|
|
49
|
return [] unless $sort_by; |
114
|
3
|
100
|
|
|
|
12
|
return $sort_by if ref $sort_by eq 'ARRAY'; |
115
|
|
|
|
|
|
|
# TODO add validation |
116
|
|
|
|
|
|
|
|
117
|
2
|
|
|
|
|
9
|
return [ split(/\s*,\s*/, $sort_by, 2) ]; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
1; # End of Query::Abstract |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head1 NAME |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
Query::Abstract - Create filters in Perlish way and transforms them into coderefs or SQL |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head1 SYNOPSIS |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Pure Perl filtering |
129
|
|
|
|
|
|
|
my $qa = Query::Abstract->new( driver => ['ArrayOfHashes'] ); |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
my $query_sub = $qa->convert_query( |
132
|
|
|
|
|
|
|
where => [ |
133
|
|
|
|
|
|
|
name => 'John', |
134
|
|
|
|
|
|
|
age => { '>' => 25 }, |
135
|
|
|
|
|
|
|
last_name => { like => 'ing' } |
136
|
|
|
|
|
|
|
], |
137
|
|
|
|
|
|
|
sort_by => 'last_name DESC, login ASC' |
138
|
|
|
|
|
|
|
); |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
$filtered_and_sorted_users = $query_sub->(\@users); |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# Preparing SQL statement |
143
|
|
|
|
|
|
|
my $qa = Query::Abstract->new( driver => ['SQL' => [table => 'users']] ); |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
## The same but explicilty creating driver object. |
146
|
|
|
|
|
|
|
my $qa = Query::Abstract->new( driver => Query::Abstract::Driver::SQL->new(table => 'users') ); |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
my $sql_statement = $qa->convert_query( |
149
|
|
|
|
|
|
|
where => [ |
150
|
|
|
|
|
|
|
name => 'John', |
151
|
|
|
|
|
|
|
age => { '>' => 25 }, |
152
|
|
|
|
|
|
|
last_name => { like => 'ing' } |
153
|
|
|
|
|
|
|
], |
154
|
|
|
|
|
|
|
sort_by => 'last_name DESC, login ASC' |
155
|
|
|
|
|
|
|
); |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=head1 WARNING |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
This software is under the heavy development and considered ALPHA quality. |
160
|
|
|
|
|
|
|
Things might be broken, not all features have been implemented, and APIs will be likely to change. |
161
|
|
|
|
|
|
|
YOU HAVE BEEN WARNED. |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=head1 DESCRIPTION |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
L - allows you to write queries and then tranform them into another format(depends in driver). Queries are almost compatible with Rose::DB::Object queries. |
166
|
|
|
|
|
|
|
This module apperared because I wanted to have pure Perl queries but with ability to convert them into SQL(or other format). |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
Currently this module has two standard drivers - ArrayOfHashes and SQL.(You can write your own) |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=head1 METHODS |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=head2 C |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
$self->convert_filter([ name => 'John', age => { '>' => 25 }, last_name => { like => 'ing' } ]); |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
"SQL" Driver will return 'WHERE' clause and bind values. |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
"ArrayOfHashes" will return a coderef which takes hashref and returns true or false depending on condition testing result. |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
my $tester = $self->convert_filter([ name => 'John', age => { '>' => 25 }, last_name => { like => 'ing' } ]); |
181
|
|
|
|
|
|
|
@filtered = grep { $tester->($_) } ( {name => 'Anton', age => 37, last_name => 'Corning'}, {name => 'John'} ... ) |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=head2 C |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
$self->convert_sort('name DESC, age ASC, last_name DESC'); |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
"SQL" Driver will return 'ORDER BY' clause. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
"ArrayOfHashes" will return a coderef for "sort" function |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
my $sort_sub = $self->convert_sort(...); |
192
|
|
|
|
|
|
|
@sorted = sort $sort_sub @data; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head2 C |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
$self->convert_query( where => [name => 'John'], sort_by => 'last_name DESC' ); |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
"SQL" Driver will return 'SELECT' with 'WHERE' and 'ORDER BY' conditions. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
"ArrayOfHashes" will return a coderef for quering data |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
my $query_sub = $self->convert_query(...); |
203
|
|
|
|
|
|
|
$filtered_and_sorted = $query_sub->( \@data ); |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=head1 AUTHOR |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
Viktor Turskyi |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=head1 BUGS |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
Please report any bugs or feature requests to Github L |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=head1 SEE ALSO |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
L, L, L |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=cut |