File Coverage

blib/lib/EntityModel/Query/Base.pm
Criterion Covered Total %
statement 71 80 88.7
branch 16 20 80.0
condition 6 12 50.0
subroutine 9 11 81.8
pod 9 9 100.0
total 111 132 84.0


line stmt bran cond sub pod time code
1             package EntityModel::Query::Base;
2             {
3             $EntityModel::Query::Base::VERSION = '0.102';
4             }
5 16     16   18379 use EntityModel::Class;
  16         30  
  16         108  
6 16     16   7734 no if $] >= 5.017011, warnings => "experimental::smartmatch";
  16         34  
  16         100  
7              
8             =head1 NAME
9              
10             EntityModel::Query::Base - base class for L-derived components
11              
12             =head1 VERSION
13              
14             version 0.102
15              
16             =head1 SYNOPSIS
17              
18             See L.
19              
20             =head1 DESCRIPTION
21              
22             See L.
23              
24             =cut
25              
26             my %ParseHandler;
27              
28             =head2 register
29              
30             Register a parse handler for the given key(s).
31              
32             Called from subclass ->import methods to hook into the configuration parser:
33              
34             EntityModel::Query->new(
35             x => [ ],
36             y => [ ]
37             )
38              
39             will call the registered parse_x and parse_y methods to handle the two directives, unless those methods are already available on the class.
40              
41             =cut
42              
43             sub register {
44 96     96 1 167 my $class = shift;
45 96         256 my %args = @_;
46 96         242 foreach my $k (keys %args) {
47 96 50       305 die "Parse handler for $k already handled by " . $ParseHandler{$k}->{class} if exists $ParseHandler{$k};
48 96         2679 $ParseHandler{$k} = {
49             class => $class,
50             parser => $args{$k}
51             };
52             }
53             }
54              
55             =head2 can_parse
56              
57             If this class supports the parse_X method, or the given configuration key was registered by
58             one of the subclasses, returns the appropriate parse handler.
59              
60             Returns undef if no handler was available.
61              
62             =cut
63              
64             sub can_parse {
65 35     35 1 43 my $self = shift;
66 35         50 my $k = shift;
67             # TODO should probably drop the 'exists', don't think it's needed
68 35   66     407 return $self->can("parse_$k") || (exists($ParseHandler{$k}) ? $ParseHandler{$k}->{parser} : undef);
69             }
70              
71             =head2 inlineSQL
72              
73              
74             =cut
75              
76             sub inlineSQL {
77 0     0 1 0 my $self = shift;
78 0   0     0 my $class = ref($self) || $self;
79 0         0 die "Virtual ->inlineSQL method from EntityModel::Query::Base called on $class.";
80             }
81              
82             =head2 normaliseInlineSQL
83              
84             Merge adjacent plaintext sections in an inline SQL expression.
85              
86             This would for example convert the following:
87              
88             'select', ' ', Entity::Field, ' ', 'from', ' ', Entity::Table
89              
90             into:
91              
92             'select ', Entity::Field, ' from ', Entity::Table
93              
94             =cut
95              
96             sub normaliseInlineSQL {
97 16     16 1 20 my $self = shift;
98 16         45 my @sql = @_;
99 16         21 my @text;
100              
101             my @out;
102 16         32 while(@sql) {
103 166         157 my $next = shift(@sql);
104 166 100 66     466 if(defined($next) && !ref($next)) {
105 154         277 push @text, $next;
106             } else {
107 12 50       38 push @out, join('', @text) if @text;
108 12         14 push @out, $next;
109 12         28 @text = ();
110             }
111             }
112 16 100       43 push @out, join('', @text) if @text;
113 16         67 return \@out;
114             }
115              
116             =head2 decantValue
117              
118             Extract a value.
119              
120             =cut
121              
122             sub decantValue {
123 13     13 1 19 my $self = shift;
124 13         15 my $in = shift;
125 13         12 my $out;
126 13         20 my $type = ref $in;
127 13         20 given($type) {
128 13         27 when('SCALAR') {
129 13         15 $out = $$in;
130 13 100 66     173 if(defined($out) && $out ~~ /^-?\d+(?:\.\d+)?$/) {
131 6         26 $out = $out+0;
132             }
133             }
134 0         0 default { $out = 'unknown'; }
  0         0  
135             }
136 13         89 return $out;
137             }
138              
139             =head2 decantQuotedValue
140              
141             Extract a quoted value suitable for use in direct SQL strings.
142              
143             The plain-string form of SQL query is only intended for debugging and tracing; regular queries should always use the prepared statement form
144             provided by L.
145              
146             =cut
147              
148             sub decantQuotedValue {
149 13     13 1 18 my $self = shift;
150 13         16 my $in = shift;
151 13         15 my $out;
152 13         18 my $type = ref $in;
153 13         19 given($type) {
154 13         30 when('SCALAR') {
155 13         15 $out = $$in;
156 13 50       82 if(!defined($out)) {
    100          
157 0         0 $out = 'NULL';
158             } elsif($out =~ /^-?\d+(?:\.\d+)?$/) {
159 6         15 $out = $out+0;
160             } else {
161 7         16 $out =~ s/'/''/g;
162 7         23 $out = "'$out'";
163             }
164             }
165 0         0 default { $out = 'unknown'; }
  0         0  
166             }
167 13         42 return $out;
168             }
169              
170             =head2 sqlString
171              
172             =cut
173              
174             sub sqlString {
175 11     11 1 22 my $self = shift;
176 11         17 my @query = @{ $self->inlineSQL };
  11         52  
177              
178 11         27 my $sql = '';
179 11         19 foreach my $part (@query) {
180 54         78 my $type = ref $part;
181 54 100       82 if($type) {
182 13         72 $sql .= $self->decantQuotedValue($part);
183             } else {
184 41         76 $sql .= $part;
185             }
186             }
187 11         82 return $sql;
188             }
189              
190             =head2 sqlAndParameters
191              
192             =cut
193              
194             sub sqlAndParameters {
195 11     11 1 28 my $self = shift;
196 11         17 my @query = @{ $self->inlineSQL };
  11         35  
197              
198 11         23 my $sql = '';
199 11         14 my @bind;
200 11         15 my $id = 1;
201 11         22 foreach my $part (@query) {
202 54         78 my $type = ref $part;
203 54 100       90 if($type) {
204 13         62 push @bind, $self->decantValue($part);
205 13         37 $sql .= '$'. $id++;
206             } else {
207 41         74 $sql .= $part;
208             }
209             }
210 11 50       60 return wantarray ? ($sql, @bind) : $sql;
211             }
212              
213             =head2 asString
214              
215             =cut
216              
217 0     0 1   sub asString { shift->sqlString }
218              
219             1;
220              
221             __END__