| 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 |  |  |  |  |  |  |  |