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