DBIx-Custom / lib / DBIx / Custom / QueryBuilder.pm /
Newer Older
378 lines | 9.433kb
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;
cleanup
Yuki Kimoto authored on 2011-04-25
10
use DBIx::Custom::Util '_subname';
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
11

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

            
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
16
# Parameter regex
17
our $PARAM_RE = qr/(^|[^\.\w]):([\.\w]+)([^\.\w]|$)/sm;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
18

            
cleanup
yuki-kimoto authored on 2010-10-17
19
sub build_query {
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
20
    my ($self, $source) = @_;
cleanup
yuki-kimoto authored on 2010-10-17
21
    
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
22
    my $query;
23
    
24
    # Parse tag. tag is DEPRECATED!
25
    if ($source =~ /\{/ && $source =~ /\}/) {
26
        $query = $self->_parse_tag($source);
27
        my $tag_count = delete $query->{tag_count};
28
        warn qq/Tag system such as {? name} is DEPRECATED! This will be removed after 2017/ .
29
             qq/use parameter system :name instead/
30
          if $tag_count;
31
        my $query2 = $self->_parse_parameter($query->sql);
32
        $query->sql($query2->sql);
33
        for (my $i =0; $i < @{$query->columns}; $i++) {
34
            my $column = $query->columns->[$i];
35
            if ($column eq 'RESERVED_PARAMETER') {
36
                my $column2 = shift @{$query2->columns};
37
                croak ":name syntax is wrong"
38
                  unless defined $column2;
39
                $query->columns->[$i] = $column2;
40
            }
41
        }
42
    }
cleanup
yuki-kimoto authored on 2010-10-17
43
    
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
44
    # Parse parameter
45
    else {
46
        $query = $self->_parse_parameter($source);
47
    }
cleanup
yuki-kimoto authored on 2010-10-17
48
    
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
49
    my $sql = $query->sql;
50
    $sql .= ';' unless $source =~ /;$/;
51
    $query->sql($sql);
52

            
53
    # Check placeholder count
54
    croak qq{Placeholder count in "$sql" must be same as column count}
55
        . _subname
56
      unless $self->_placeholder_count($sql) eq @{$query->columns};
57
        
cleanup
yuki-kimoto authored on 2010-10-17
58
    return $query;
59
}
60

            
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
61
sub _placeholder_count {
62
    my ($self, $sql) = @_;
changed argument of tag proc...
yuki-kimoto authored on 2010-08-03
63
    
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
64
    # Count
65
    $sql ||= '';
66
    my $count = 0;
67
    my $pos   = -1;
68
    while (($pos = index($sql, '?', $pos + 1)) != -1) {
69
        $count++;
70
    }
71
    return $count;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
72
}
73

            
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
74
sub _parse_parameter {
75
    my ($self, $source) = @_;
add table tag
Yuki Kimoto authored on 2011-02-09
76
    
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
77
    # Get and replace parameters
78
    my $sql = $source || '';
79
    my $columns = [];
80
    while ($source =~ /$PARAM_RE/g) {
81
        push @$columns, $2;
cleanup
yuki-kimoto authored on 2010-10-17
82
    }
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
83
    $sql =~ s/$PARAM_RE/$1?$3/g;
cleanup
yuki-kimoto authored on 2010-10-17
84

            
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
85
    # Create query
add table tag
Yuki Kimoto authored on 2011-02-09
86
    my $query = DBIx::Custom::Query->new(
87
        sql => $sql,
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
88
        columns => $columns
add table tag
Yuki Kimoto authored on 2011-02-09
89
    );
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
90
    
91
    return $query;
92
}
93

            
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
94
# DEPRECATED!
95
__PACKAGE__->attr('tags' => sub { {} });
96

            
97
# DEPRECATED!
98
sub register_tag {
99
    my $self = shift;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
100
    
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
101
    # Merge tag
102
    my $tags = ref $_[0] eq 'HASH' ? $_[0] : {@_};
103
    $self->tags({%{$self->tags}, %$tags});
104
    
105
    return $self;
106
}
107

            
108
# DEPRECATED!
109
sub _parse_tag {
110
    my ($self, $source) = @_;
111

            
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
112
    # Source
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
113
    $source ||= '';
fixed tests
yuki-kimoto authored on 2010-08-06
114

            
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
115
    # Tree
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
116
    my @tree;
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
117
    
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
118
    # Value
119
    my $value = '';
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
120
    
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
121
    # State
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
122
    my $state = 'text';
123
    
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
124
    # Before charactor
125
    my $before = '';
126

            
127
    # Position
fixed DBIx::Custom::QueryBui...
yuki-kimoto authored on 2010-08-15
128
    my $pos = 0;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
129
    
130
    # Parse
added tests
yuki-kimoto authored on 2010-08-12
131
    my $original = $source;
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
132
    my $tag_count = 0;
fixed DBIx::Custom::QueryBui...
yuki-kimoto authored on 2010-08-15
133
    while (defined(my $c = substr($source, $pos, 1))) {
134
        
135
        # Last
136
        last unless length $c;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
137
        
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
138
        # Parameter
139
        if ($c eq ':' && (substr($source, $pos + 1, 1) || '') =~ /\w/) {
140
            push @tree, {type => 'param'};;
141
        }
142
        
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
143
        # State is text
144
        if ($state eq 'text') {
145
            
146
            # Tag start charactor
147
            if ($c eq '{') {
148
                
149
                # Escaped charactor
150
                if ($before eq "\\") {
151
                    substr($value, -1, 1, '');
152
                    $value .= $c;
153
                }
154
                
155
                # Tag start
156
                else {
157
                    
158
                    # Change state
159
                    $state = 'tag';
160
                    
161
                    # Add text
162
                    push @tree, {type => 'text', value => $value}
163
                      if $value;
164
                    
165
                    # Clear
166
                    $value = '';
167
                }
168
            }
169
            
170
            # Tag end charactor
171
            elsif ($c eq '}') {
172
            
173
                # Escaped charactor
174
                if ($before eq "\\") {
175
                    substr($value, -1, 1, '');
176
                    $value .= $c;
177
                }
178
                
179
                # Unexpected
180
                else {
improved error messages
Yuki Kimoto authored on 2011-04-18
181
                    croak qq{Parsing error. unexpected "\}". }
cleanup
Yuki Kimoto authored on 2011-04-25
182
                        . qq{pos $pos of "$original" } . _subname
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
183
                }
184
            }
185
            
186
            # Normal charactor
187
            else { $value .= $c }
188
        }
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
189
        
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
190
        # State is tags
added tests
yuki-kimoto authored on 2010-08-12
191
        else {
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
192
            
193
            # Tag start charactor
194
            if ($c eq '{') {
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
195
            
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
196
                # Escaped charactor
197
                if ($before eq "\\") {
198
                    substr($value, -1, 1, '');
199
                    $value .= $c;
200
                }
201
                
202
                # Unexpected
203
                else {
improved error messages
Yuki Kimoto authored on 2011-04-18
204
                    croak qq{Parsing error. unexpected "\{". }
cleanup
Yuki Kimoto authored on 2011-04-25
205
                        . qq{pos $pos of "$original" } . _subname
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
206
                }
207
            }
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
208
            
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
209
            # Tag end charactor
210
            elsif ($c eq '}') {
211
                
212
                # Escaped charactor
213
                if ($before eq "\\") {
214
                    substr($value, -1, 1, '');
215
                    $value .= $c;
216
                }
217
                
218
                # Tag end
219
                else {
220
                
221
                    # Change state
222
                    $state = 'text';
223
                    
224
                    # Add tag
225
                    my ($tag_name, @tag_args) = split /\s+/, $value;
226
                    push @tree, {type => 'tag', tag_name => $tag_name, 
227
                                 tag_args => \@tag_args};
228
                    
229
                    # Clear
230
                    $value = '';
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
231
                    
232
                    # Countup
233
                    $tag_count++;
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
234
                }
235
            }
236
            
237
            # Normal charactor
238
            else { $value .= $c }
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
239
        }
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
240
        
241
        # Save before charactor
242
        $before = $c;
243
        
244
        # increment position
245
        $pos++;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
246
    }
247
    
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
248
    # Tag not finished
cleanup
Yuki Kimoto authored on 2011-04-25
249
    croak qq{Tag not finished. "$original" } . _subname
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
250
      if $state eq 'tag';
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
251
    
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
252
    # Not contains tag
253
    return DBIx::Custom::Query->new(sql => $source, tag_count => $tag_count)
254
      if $tag_count == 0;
255
    
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
256
    # Add rest text
257
    push @tree, {type => 'text', value => $value}
258
      if $value;
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
259
        
260
    # SQL
261
    my $sql = '';
removed DBIx::Custom::Query ...
yuki-kimoto authored on 2010-08-12
262
    
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
263
    # All Columns
264
    my $all_columns = [];
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
265
    
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
266
    # Tables
267
    my $tables = [];
268
    
269
    # Build SQL 
270
    foreach my $node (@tree) {
271
        
272
        # Text
273
        if ($node->{type} eq 'text') { $sql .= $node->{value} }
274
        
275
        # Parameter
276
        elsif ($node->{type} eq 'param') {
277
            push @$all_columns, 'RESERVED_PARAMETER';
278
        }
279
        # Tag
280
        else {
281
            
282
            # Tag name
283
            my $tag_name = $node->{tag_name};
284
            
285
            # Tag arguments
286
            my $tag_args = $node->{tag_args};
287
            
288
            # Table
289
            if ($tag_name eq 'table') {
290
                my $table = $tag_args->[0];
291
                push @$tables, $table;
292
                $sql .= $table;
293
                next;
294
            }
295

            
296
            # Get tag
297
            my $tag = $self->tag_processors->{$tag_name}
298
                             || $self->tags->{$tag_name};
299
            
300
            # Tag is not registered
301
            croak qq{Tag "$tag_name" is not registered } . _subname
302
              unless $tag;
303
            
304
            # Tag not sub reference
305
            croak qq{Tag "$tag_name" must be sub reference } . _subname
306
              unless ref $tag eq 'CODE';
307
            
308
            # Execute tag
309
            my $r = $tag->(@$tag_args);
310
            
311
            # Check tag return value
312
            croak qq{Tag "$tag_name" must return [STRING, ARRAY_REFERENCE] }
313
                . _subname
314
              unless ref $r eq 'ARRAY' && defined $r->[0] && ref $r->[1] eq 'ARRAY';
315
            
316
            # Part of SQL statement and colum names
317
            my ($part, $columns) = @$r;
318
            
319
            # Add columns
320
            push @$all_columns, @$columns;
321
            
322
            # Join part tag to SQL
323
            $sql .= $part;
324
        }
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
325
    }
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
326

            
327
    # Query
328
    my $query = DBIx::Custom::Query->new(
329
        sql => $sql,
330
        columns => $all_columns,
331
        tables => $tables,
332
        tag_count => $tag_count
333
    );
334
    
335
    return $query;
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
336
}
337

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

            
cleanup
Yuki Kimoto authored on 2011-01-25
341
# DEPRECATED!
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
342
sub register_tag_processor {
343
    my $self = shift;
344
    
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
345
    warn "register_tag_processor is DEPRECATED! use register_tag instead";
346
    
cleanup
Yuki Kimoto authored on 2011-01-25
347
    # Merge tag
renamed DBIx::Custom::TagPro...
Yuki Kimoto authored on 2011-01-24
348
    my $tag_processors = ref $_[0] eq 'HASH' ? $_[0] : {@_};
349
    $self->tag_processors({%{$self->tag_processors}, %{$tag_processors}});
350
    
351
    return $self;
352
}
353

            
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
354
1;
355

            
356
=head1 NAME
357

            
358
DBIx::Custom::QueryBuilder - Query builder
359

            
360
=head1 SYNOPSIS
361
    
362
    my $builder = DBIx::Custom::QueryBuilder->new;
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
363
    my $query = $builder->build_query(
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
364
        "select from table title = :title and author = :author"
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
365
    );
renamed default_query_filter...
yuki-kimoto authored on 2010-08-03
366

            
367
=head1 METHODS
368

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

            
372
=head2 C<build_query>
373
    
374
    my $query = $builder->build_query($source);
375

            
remove DBIx::Custom::QueryBu...
yuki-kimoto authored on 2010-08-05
376
Create a new L<DBIx::Custom::Query> object from SQL source.
update document
yuki-kimoto authored on 2010-08-07
377

            
- update_param_tag is DEPREC...
Yuki Kimoto authored on 2011-06-07
378
=cut