DBIx-Custom / lib / DBIx / Custom / QueryBuilder.pm /
Newer Older
370 lines | 8.906kb
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
1
package DBIx::Custom::QueryBuilder;
2

            
3
use strict;
4
use warnings;
5

            
6
use base 'Object::Simple';
7

            
8
use Carp 'croak';
9
use DBIx::Custom::Query;
10

            
fixed Carp trast relationshi...
yuki-kimoto authored on 2010-08-12
11
# Carp trust relationship
12
push @DBIx::Custom::CARP_NOT, __PACKAGE__;
updated document
Yuki Kimoto authored on 2011-01-20
13
push @DBIx::Custom::Where::CARP_NOT, __PACKAGE__;
14

            
cleanup
yuki-kimoto authored on 2010-10-17
15
# Attributes
cleanup
Yuki Kimoto authored on 2011-01-25
16
__PACKAGE__->attr('tags' => sub { {} });
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
17

            
cleanup
yuki-kimoto authored on 2010-10-17
18
sub build_query {
19
    my ($self, $source)  = @_;
20
    
21
    # Parse
22
    my $tree = $self->_parse($source);
23
    
24
    # Build query
25
    my $query = $self->_build_query($tree);
26
    
27
    return $query;
28
}
29

            
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
30
sub register_tag {
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
31
    my $self = shift;
changed argument of tag proc...
yuki-kimoto authored on 2010-08-03
32
    
cleanup
Yuki Kimoto authored on 2011-01-25
33
    # Merge tag
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
34
    my $tags = ref $_[0] eq 'HASH' ? $_[0] : {@_};
35
    $self->tags({%{$self->tags}, %$tags});
changed argument of tag proc...
yuki-kimoto authored on 2010-08-03
36
    
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
37
    return $self;
38
}
39

            
cleanup
yuki-kimoto authored on 2010-10-17
40
sub _build_query {
41
    my ($self, $tree) = @_;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
42
    
cleanup
yuki-kimoto authored on 2010-10-17
43
    # SQL
44
    my $sql = '';
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
45
    
cleanup
yuki-kimoto authored on 2010-10-17
46
    # All Columns
47
    my $all_columns = [];
48
    
add table tag
Yuki Kimoto authored on 2011-02-09
49
    # Tables
50
    my $tables = [];
51
    
cleanup
yuki-kimoto authored on 2010-10-17
52
    # Build SQL 
53
    foreach my $node (@$tree) {
54
        
55
        # Text
56
        if ($node->{type} eq 'text') { $sql .= $node->{value} }
57
        
58
        # Tag
59
        else {
60
            
61
            # Tag name
62
            my $tag_name = $node->{tag_name};
63
            
64
            # Tag arguments
65
            my $tag_args = $node->{tag_args};
66
            
add table tag
Yuki Kimoto authored on 2011-02-09
67
            # Table
68
            if ($tag_name eq 'table') {
69
                my $table = $tag_args->[0];
70
                push @$tables, $table;
71
                $sql .= $table;
72
                next;
73
            }
74

            
cleanup
Yuki Kimoto authored on 2011-01-25
75
            # Get tag
76
            my $tag = $self->tag_processors->{$tag_name}
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
77
                             || $self->tags->{$tag_name};
cleanup
yuki-kimoto authored on 2010-10-17
78
            
cleanup
Yuki Kimoto authored on 2011-01-25
79
            # Tag is not registered
cleanup
yuki-kimoto authored on 2010-10-17
80
            croak qq{Tag "$tag_name" in "{a }" is not registered}
improved error messages
Yuki Kimoto authored on 2011-04-18
81
                . qq{ (DBIx::Custom::QueryBuilder::build_query)}
cleanup
Yuki Kimoto authored on 2011-01-25
82
              unless $tag;
cleanup
yuki-kimoto authored on 2010-10-17
83
            
cleanup
Yuki Kimoto authored on 2011-01-25
84
            # Tag not sub reference
85
            croak qq{Tag "$tag_name" must be sub reference}
improved error messages
Yuki Kimoto authored on 2011-04-18
86
                . qq{ (DBIx::Custom::QueryBuilder::build_query)}
cleanup
Yuki Kimoto authored on 2011-01-25
87
              unless ref $tag eq 'CODE';
cleanup
yuki-kimoto authored on 2010-10-17
88
            
cleanup
Yuki Kimoto authored on 2011-01-25
89
            # Execute tag
90
            my $r = $tag->(@$tag_args);
cleanup
yuki-kimoto authored on 2010-10-17
91
            
cleanup
Yuki Kimoto authored on 2011-01-25
92
            # Check tag return value
93
            croak qq{Tag "$tag_name" must return [STRING, ARRAY_REFERENCE]}
improved error messages
Yuki Kimoto authored on 2011-04-18
94
                . qq{ (DBIx::Custom::QueryBuilder::build_query)}
cleanup
yuki-kimoto authored on 2010-10-17
95
              unless ref $r eq 'ARRAY' && defined $r->[0] && ref $r->[1] eq 'ARRAY';
96
            
97
            # Part of SQL statement and colum names
98
            my ($part, $columns) = @$r;
99
            
100
            # Add columns
101
            push @$all_columns, @$columns;
102
            
103
            # Join part tag to SQL
104
            $sql .= $part;
105
        }
106
    }
107

            
108
    # Check placeholder count
109
    my $placeholder_count = $self->_placeholder_count($sql);
110
    my $column_count      = @$all_columns;
111
    croak qq{Placeholder count in "$sql" must be same as column count $column_count}
improved error messages
Yuki Kimoto authored on 2011-04-18
112
        . qq{ (DBIx::Custom::QueryBuilder::build_query)}
cleanup
yuki-kimoto authored on 2010-10-17
113
      unless $placeholder_count eq @$all_columns;
114
    
115
    # Add semicolon
116
    $sql .= ';' unless $sql =~ /;$/;
117
    
118
    # Query
add table tag
Yuki Kimoto authored on 2011-02-09
119
    my $query = DBIx::Custom::Query->new(
120
        sql => $sql,
121
        columns => $all_columns,
122
        tables => $tables
123
    );
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
124
    
125
    return $query;
126
}
127

            
128
sub _parse {
129
    my ($self, $source) = @_;
130
    
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
131
    # Source
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
132
    $source ||= '';
fixed tests
yuki-kimoto authored on 2010-08-06
133

            
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
134
    # Tree
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
135
    my @tree;
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
136
    
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
137
    # Value
138
    my $value = '';
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
139
    
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
140
    # State
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
141
    my $state = 'text';
142
    
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
143
    # Before charactor
144
    my $before = '';
145

            
146
    # Position
fixed DBIx::Custom::QueryBui...
yuki-kimoto authored on 2010-08-15
147
    my $pos = 0;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
148
    
149
    # Parse
added tests
yuki-kimoto authored on 2010-08-12
150
    my $original = $source;
fixed DBIx::Custom::QueryBui...
yuki-kimoto authored on 2010-08-15
151
    while (defined(my $c = substr($source, $pos, 1))) {
152
        
153
        # Last
154
        last unless length $c;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
155
        
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
156
        # State is text
157
        if ($state eq 'text') {
158
            
159
            # Tag start charactor
160
            if ($c eq '{') {
161
                
162
                # Escaped charactor
163
                if ($before eq "\\") {
164
                    substr($value, -1, 1, '');
165
                    $value .= $c;
166
                }
167
                
168
                # Tag start
169
                else {
170
                    
171
                    # Change state
172
                    $state = 'tag';
173
                    
174
                    # Add text
175
                    push @tree, {type => 'text', value => $value}
176
                      if $value;
177
                    
178
                    # Clear
179
                    $value = '';
180
                }
181
            }
182
            
183
            # Tag end charactor
184
            elsif ($c eq '}') {
185
            
186
                # Escaped charactor
187
                if ($before eq "\\") {
188
                    substr($value, -1, 1, '');
189
                    $value .= $c;
190
                }
191
                
192
                # Unexpected
193
                else {
improved error messages
Yuki Kimoto authored on 2011-04-18
194
                    croak qq{Parsing error. unexpected "\}". }
195
                        . qq{pos $pos of "$original"}
196
                        . qq{ (DBIx::Custom::QueryBuilder::build_query)};
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
197
                }
198
            }
199
            
200
            # Normal charactor
201
            else { $value .= $c }
202
        }
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
203
        
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
204
        # State is tags
added tests
yuki-kimoto authored on 2010-08-12
205
        else {
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
206
            
207
            # Tag start charactor
208
            if ($c eq '{') {
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
209
            
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
210
                # Escaped charactor
211
                if ($before eq "\\") {
212
                    substr($value, -1, 1, '');
213
                    $value .= $c;
214
                }
215
                
216
                # Unexpected
217
                else {
improved error messages
Yuki Kimoto authored on 2011-04-18
218
                    croak qq{Parsing error. unexpected "\{". }
219
                        . qq{pos $pos of "$original"}
220
                        . qq{ (DBIx::Custom::QueryBuilder::build_query)};
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
221
                }
222
            }
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
223
            
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
224
            # Tag end charactor
225
            elsif ($c eq '}') {
226
                
227
                # Escaped charactor
228
                if ($before eq "\\") {
229
                    substr($value, -1, 1, '');
230
                    $value .= $c;
231
                }
232
                
233
                # Tag end
234
                else {
235
                
236
                    # Change state
237
                    $state = 'text';
238
                    
239
                    # Add tag
240
                    my ($tag_name, @tag_args) = split /\s+/, $value;
241
                    push @tree, {type => 'tag', tag_name => $tag_name, 
242
                                 tag_args => \@tag_args};
243
                    
244
                    # Clear
245
                    $value = '';
246
                }
247
            }
248
            
249
            # Normal charactor
250
            else { $value .= $c }
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
251
        }
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
252
        
253
        # Save before charactor
254
        $before = $c;
255
        
256
        # increment position
257
        $pos++;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
258
    }
259
    
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
260
    # Tag not finished
added tests
yuki-kimoto authored on 2010-08-12
261
    croak qq{Tag not finished. "$original"}
improved error messages
Yuki Kimoto authored on 2011-04-18
262
        . qq{ (DBIx::Custom::QueryBuilder::build_query)}
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
263
      if $state eq 'tag';
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
264
    
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
265
    # Add rest text
266
    push @tree, {type => 'text', value => $value}
267
      if $value;
268
    
269
    return \@tree;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
270
}
271

            
272
sub _placeholder_count {
273
    my ($self, $expand) = @_;
274
    
changed argument of tag proc...
yuki-kimoto authored on 2010-08-03
275
    # Count
276
    $expand ||= '';
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
277
    my $count = 0;
278
    my $pos   = -1;
279
    while (($pos = index($expand, '?', $pos + 1)) != -1) {
280
        $count++;
281
    }
282
    return $count;
283
}
284

            
cleanup
Yuki Kimoto authored on 2011-01-25
285
# DEPRECATED!
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
286
__PACKAGE__->attr('tag_processors' => sub { {} });
287

            
cleanup
Yuki Kimoto authored on 2011-01-25
288
# DEPRECATED!
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
289
sub register_tag_processor {
290
    my $self = shift;
291
    
cleanup
Yuki Kimoto authored on 2011-01-25
292
    # Merge tag
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
293
    my $tag_processors = ref $_[0] eq 'HASH' ? $_[0] : {@_};
294
    $self->tag_processors({%{$self->tag_processors}, %{$tag_processors}});
295
    
296
    return $self;
297
}
298

            
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
299
1;
300

            
301
=head1 NAME
302

            
303
DBIx::Custom::QueryBuilder - Query builder
304

            
305
=head1 SYNOPSIS
306
    
307
    my $builder = DBIx::Custom::QueryBuilder->new;
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
308
    my $query = $builder->build_query(
309
        "select from table {= k1} && {<> k2} || {like k3}"
310
    );
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
311

            
312
=head1 ATTRIBUTES
313

            
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
314
=head2 C<tags>
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
315

            
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
316
    my $tags = $builder->tags;
cleanup
Yuki Kimoto authored on 2011-01-25
317
    $builder = $builder->tags(\%tags);
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
318

            
cleanup
Yuki Kimoto authored on 2011-01-25
319
Tags.
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
320

            
321
=head1 METHODS
322

            
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
323
L<DBIx::Custom::QueryBuilder> inherits all methods from L<Object::Simple>
324
and implements the following new ones.
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
325

            
326
=head2 C<build_query>
327
    
328
    my $query = $builder->build_query($source);
329

            
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
330
Create a new L<DBIx::Custom::Query> object from SQL source.
331
SQL source contains tags, such as {= title}, {like author}.
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
332

            
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
333
C<{> and C<}> is reserved. If you use these charactors,
334
you must escape them using '\'. Note that '\' is
335
already perl escaped charactor, so you must write '\\'. 
336

            
337
    'select * from books \\{ something statement \\}'
338

            
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
339
B<Example:>
340

            
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
341
SQL source
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
342

            
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
343
      "select * from table where {= title} && {like author} || {<= price}"
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
344

            
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
345
Query
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
346

            
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
347
    {
348
        sql     => "select * from table where title = ? && author like ? price <= ?;"
349
        columns => ['title', 'author', 'price']
350
    }
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
351

            
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
352
=head2 C<register_tag>
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
353

            
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
354
    $builder->register_tag(\%tags);
355
    $builder->register_tag(%tags);
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
356

            
cleanup
Yuki Kimoto authored on 2011-01-25
357
Register tag.
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
358

            
renamed build_query to creat...
yuki-kimoto authored on 2010-08-06
359
B<Example:>
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
360

            
cleanup
Yuki Kimoto authored on 2011-01-25
361
    $builder->register_tag(
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
362
        '?' => sub {
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
363
            my $column = shift;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
364
            
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
365
            return ['?', [$column]];
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
366
        }
367
    );
368

            
cleanup
Yuki Kimoto authored on 2011-01-25
369
See also L<DBIx::Custom::Tag> to know tag.
update document
yuki-kimoto authored on 2010-08-07
370