File Coverage

blib/lib/Class/ReluctantORM/SQL/Function.pm
Criterion Covered Total %
statement 35 58 60.3
branch 4 16 25.0
condition 6 6 100.0
subroutine 7 12 58.3
pod 7 7 100.0
total 59 99 59.6


line stmt bran cond sub pod time code
1             package Class::ReluctantORM::SQL::Function;
2 1     1   6 use strict;
  1         2  
  1         28  
3 1     1   5 use warnings;
  1         2  
  1         44  
4              
5             =head1 NAME
6              
7             Class::ReluctantORM::SQL::Function - Represent a SQL function, aggregator, stored proc, or operator
8              
9             =head1 SYNOPSIS
10              
11             use Class::ReluctantORM::SQL::Aliases;
12              
13             # Explicit lookup....
14             my $eq = Function->by_name('='); # not case senstive
15             my $min_arity = $eq->min_inputs; # 2 for '='
16              
17              
18             # Usually used implicitly....
19             # This automatically looks up the '=' function by name
20             my $crit = Criterion->new('=', $column, $param);
21             my $crit2 = Criterion->new($func, $column, $param);
22              
23             # Looks up '=' implicitly
24             my $fc1 = FunctionCall->new('=', $column, $param);
25             my $fc2 = FunctionCall->new($func, $column, $param);
26              
27             # Register new custom functions
28             Function->register(
29             name => 'froobulate',
30             min_inputs => 2,
31             max_inputs => 43,
32             is_associative => 1,
33             is_cummutative => 1,
34             );
35              
36             # Now...
37             my $crit = Criterion->new('froobulate', $column, $param);
38              
39             # Note: your driver must know how to render froobulate!
40              
41             # List functions
42             my @funcs = Function->list_all_functions();
43             my @funcs = Function->list_default_functions();
44             my @funcs = Function->list_aggregate_functions();
45              
46             =head1 DESCRIPTION
47              
48             Registry for functions, operators, and stored procedures, so that
49             they may be represented in an abstract SQL tree.
50              
51             Each individual function is treated as a singleton by name; so if you request (explicitly or implicitly) the '=' operator two different times, you will get the same object both times.
52              
53             =head2 Default Function Kit
54              
55             A fair number of functions are pre-registered, including = < > AND OR NOT. You can get the complete list by calling Function->list_default_functions().
56              
57             =head2 A Function is Not a Function Call
58              
59             A Function object represents _which_ function is being referred to. To actually call a function in an abstract SQL tree, create a FunctionCall object.
60              
61             =cut
62              
63 1     1   5 use base 'Class::Accessor::Fast';
  1         3  
  1         929  
64 1     1   536 use Class::ReluctantORM::Exception;
  1         3  
  1         26  
65 1     1   8 use Class::ReluctantORM::Utilities qw(check_args);
  1         2  
  1         1127  
66              
67             our %REGISTRY;
68              
69             =head1 INSTANCE RETRIEVAL ("CONSTRUCTOR")
70              
71             =cut
72              
73             =head2 $f = Function->by_name('name');
74              
75             Searches for a function with the name given, and returns it. Throws an exception if no such function has been registered.
76              
77             The search is case-insensitive.
78              
79             =cut
80              
81             sub by_name {
82 0     0 1 0 my $class = shift;
83 0         0 my $name = shift;
84 0 0       0 if (ref($class)) {
85 0         0 Class::ReluctantORM::Exception::Call::NotPermitted->croak('by_name may only be called as a class method');
86             }
87              
88 0 0       0 unless ($name) {
89 0         0 Class::ReluctantORM::Exception::Param::Missing->croak(param => 'name');
90             }
91              
92 0         0 $name = uc($name);
93 0 0       0 unless (exists $REGISTRY{$name}) {
94 0         0 Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'name', value => $name, error => "No such Function with name '$name'");
95             }
96              
97 0         0 return $REGISTRY{$name};
98              
99             }
100              
101             =head2 $bool = Function->is_registered('name');
102              
103             Returns true if the function name is registered.
104              
105             =cut
106              
107             sub is_registered {
108 2     2 1 5 my $class = shift;
109 2         3 my $name = shift;
110 2 50       9 if (ref($class)) {
111 0         0 Class::ReluctantORM::Exception::Call::NotPermitted->croak('by_name may only be called as a class method');
112             }
113              
114 2 50       6 unless ($name) {
115 0         0 Class::ReluctantORM::Exception::Param::Missing->croak(param => 'name');
116             }
117              
118 2         5 $name = uc($name);
119 2         12 return exists($REGISTRY{$name});
120             }
121              
122              
123             =head1 OTHER CLASS METHODS
124              
125             =cut
126              
127             =head2 $f = Function->register(%options);
128              
129             Registers a new Function. After this, you can explicitly or implicitly refer to the function by name.
130              
131             Options:
132              
133             =over
134              
135             =item name
136              
137             Required string, may be symbols. Must be unique - no other registered Function may have the same name.
138              
139             =item min_inputs
140              
141             Required positive integer. Minimum number of arguments the function takes.
142              
143             =item max_inputs
144              
145             Optional positive integer. If not provided, Function is assumed to have no limit.
146              
147             =item is_aggregate
148              
149             Optional boolean, default false. If true, marks this function as an aggregrate function.
150              
151             =item is_associative
152              
153             Optional boolean, default false. If true, indicates CRO can re-group multiple invocations of this function. So, (1+2)+3 = 1+(2+3).
154              
155             =item is_commutative
156              
157             Optional boolean, default false. If true, indicates CRO can re-order arguments of this function. So, 1+2 = 2+1.
158              
159             =back
160              
161             =cut
162              
163             sub register {
164 26     26 1 36 my $class = shift;
165 26         136 my %args = check_args(
166             args => \@_,
167             required => [qw(name min_inputs)],
168             optional => [qw(max_inputs is_aggregate is_associative is_commutative)],
169             );
170              
171 26 50       94 if (ref($class)) {
172 0         0 Class::ReluctantORM::Exception::Call::NotPermitted->croak('register may only be called as a class method');
173             }
174              
175 26   100     90 $args{is_aggregate} ||= 0;
176 26   100     77 $args{is_associative} ||= 0;
177 26   100     74 $args{is_commutative} ||= 0;
178              
179 26         58 $args{name} = uc($args{name});
180 26 50       67 if (exists $REGISTRY{$args{name}}) {
181 0         0 Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'name', value => $args{name}, error => "A Function with name '$args{name}' already is registered");
182             }
183              
184 26         88 my $func = bless {}, $class;
185 26         41 foreach my $attr (qw(name min_inputs max_inputs is_aggregate is_associative is_commutative)) {
186 156         1075 $func->set($attr, $args{$attr});
187             }
188 26         207 $func->set('is_default', 0);
189              
190             # These are singletons. Permanently caching them is appropriate, so do not weaken this ref.
191 26         181 $REGISTRY{$func->name()} = $func;
192 26         209 return $func;
193             }
194              
195             =head2 @funcs = Function->list_all_functions();
196              
197             Returns an array of all registered functions.
198              
199             =cut
200              
201 0     0 1   sub list_all_functions { return values %REGISTRY; }
202              
203             =head2 @funcs = Function->list_default_functions();
204              
205             Returns an array of all functions that are provided by default, and guarenteed to be renderable by all drivers.
206              
207             =cut
208              
209 0     0 1   sub list_default_functions { return grep { $_->is_defualt() } values %REGISTRY; }
  0            
210              
211             =head2 @funcs = Function->list_aggregate_functions();
212              
213             Returns an array of all aggregate functions.
214              
215             =cut
216              
217             # This gets hammered on, so cache return
218             our @AGGREGATES;
219             sub list_aggregate_functions {
220 0 0   0 1   unless (@AGGREGATES) {
221 0           @AGGREGATES = grep { $_->is_aggregate() } values %REGISTRY;
  0            
222             }
223 0           return @AGGREGATES;
224             }
225              
226             =head1 INSTANCE METHODS
227              
228             =cut
229              
230             =head2 $str = $f->name()
231              
232             Returns the name of the Function. Always available and always unique.
233              
234             =cut
235              
236             __PACKAGE__->mk_ro_accessors('name');
237              
238             =head2 $int = $f->min_inputs()
239              
240             Returns the minimum number of inputs the function accepts. Always available.
241              
242             =cut
243              
244             __PACKAGE__->mk_ro_accessors('min_inputs');
245              
246             =head2 $int = $f->max_inputs()
247              
248             Returns the maximum number of inputs the function accepts. May be undef,
249             in which case the function has no upper limit on the number of inputs. May also be zero, as for NOW().
250              
251             =cut
252              
253             __PACKAGE__->mk_ro_accessors('max_inputs');
254              
255             =head2 $bool = $f->is_default()
256              
257             Returns true if the function is part of the default kit, which all drivers are required to be able to render. Returns false if the Function was registered by you.
258              
259             =cut
260              
261             __PACKAGE__->mk_ro_accessors('is_default');
262              
263             =head2 $bool = $f->is_aggregate()
264              
265             Returns true if the function is an aggregate function, like COUNT or MAX. These functions may only be used in output column expressions, and alter the semantics of the query.
266              
267             =cut
268              
269             __PACKAGE__->mk_ro_accessors('is_aggregate');
270              
271             __PACKAGE__->mk_ro_accessors('is_associative');
272              
273             __PACKAGE__->mk_ro_accessors('is_commutative');
274              
275             =head2 $same = $func->clone();
276              
277             Sine each function is a singleton, it doesn't make sense to clone them. This method thus returns the original, and is provided for consistency with other SQL objects.
278              
279             =cut
280              
281             sub clone {
282 0     0 1   my $self = shift;
283             # Function is a singleton - return the same thing
284 0           return $self;
285             }
286              
287             #===================================================#
288             # Default Functions
289             #===================================================#
290              
291             our @DEFAULTS = (
292             {name => 'AND', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 1 },
293             {name => 'OR', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 1 },
294             {name => 'NOT', min_inputs => 1, max_inputs => 1, is_associative => 0, is_commutative => 0 },
295             {name => '=', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 1 },
296             {name => '<>', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 1 },
297             {name => '>', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 0 },
298             {name => '<', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 0 },
299             {name => '>=', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 0 },
300             {name => '<=', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 0 },
301             {name => 'IS', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 1 },
302             {name => 'IS NOT', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 1 },
303             {name => 'LIKE', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 1 },
304             {name => 'ILIKE', min_inputs => 2, max_inputs => 2, is_associative => 1, is_commutative => 1 },
305             {name => 'EXISTS', min_inputs => 1, max_inputs => 1, is_associative => 0, is_commutative => 0 },
306             {name => 'REPLACE',min_inputs => 3, max_inputs => 3, is_associative => 0, is_commutative => 0 },
307              
308             {name => '+', min_inputs => 2, max_inputs => undef, is_associative => 1, is_commutative => 1 },
309             {name => '-', min_inputs => 2, max_inputs => 2, is_associative => 0, is_commutative => 0 },
310              
311             {name => 'KEY_COMPOSITOR_INSIDE_SUBQUERY', min_inputs => 1, max_inputs => undef},
312             {name => 'KEY_COMPOSITOR_OUTSIDE_SUBQUERY', min_inputs => 1, max_inputs => undef},
313              
314             # Aggregates
315             {name => 'COUNT', min_inputs => 1, max_inputs => 1, is_aggregate => 1 },
316             {name => 'MAX', min_inputs => 1, max_inputs => 1, is_aggregate => 1 },
317             {name => 'MIN', min_inputs => 1, max_inputs => 1, is_aggregate => 1 },
318             {name => 'AVG', min_inputs => 1, max_inputs => 1, is_aggregate => 1 },
319             {name => 'STDDEV', min_inputs => 1, max_inputs => 1, is_aggregate => 1 },
320             {name => 'SUM', min_inputs => 1, max_inputs => 1, is_aggregate => 1 },
321             );
322             foreach my $def (@DEFAULTS) {
323             my $f = __PACKAGE__->register(%$def);
324             $f->set('is_default', 1);
325             }
326              
327             1;
328              
329