line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DBIx::SearchBuilder::Record; |
2
|
|
|
|
|
|
|
|
3
|
20
|
|
|
20
|
|
2195583
|
use strict; |
|
20
|
|
|
|
|
270
|
|
|
20
|
|
|
|
|
673
|
|
4
|
20
|
|
|
20
|
|
120
|
use warnings; |
|
20
|
|
|
|
|
48
|
|
|
20
|
|
|
|
|
796
|
|
5
|
|
|
|
|
|
|
|
6
|
20
|
|
|
20
|
|
140
|
use vars qw($AUTOLOAD); |
|
20
|
|
|
|
|
39
|
|
|
20
|
|
|
|
|
1045
|
|
7
|
20
|
|
|
20
|
|
7989
|
use Class::ReturnValue; |
|
20
|
|
|
|
|
211314
|
|
|
20
|
|
|
|
|
2084
|
|
8
|
20
|
|
|
20
|
|
10068
|
use Encode qw(); |
|
20
|
|
|
|
|
168536
|
|
|
20
|
|
|
|
|
602
|
|
9
|
|
|
|
|
|
|
|
10
|
20
|
|
|
20
|
|
7714
|
use DBIx::SearchBuilder::Util qw/ sorted_values /; |
|
20
|
|
|
|
|
58
|
|
|
20
|
|
|
|
|
7230
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 NAME |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
DBIx::SearchBuilder::Record - Superclass for records loaded by SearchBuilder |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 SYNOPSIS |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
package MyRecord; |
19
|
|
|
|
|
|
|
use base qw/DBIx::SearchBuilder::Record/; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
sub _Init { |
22
|
|
|
|
|
|
|
my $self = shift; |
23
|
|
|
|
|
|
|
my $DBIxHandle = shift; # A DBIx::SearchBuilder::Handle::foo object for your database |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
$self->_Handle($DBIxHandle); |
26
|
|
|
|
|
|
|
$self->Table("Users"); |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# Tell Record what the primary keys are |
30
|
|
|
|
|
|
|
sub _PrimaryKeys { |
31
|
|
|
|
|
|
|
return ['id']; |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# Preferred and most efficient way to specify fields attributes in a derived |
35
|
|
|
|
|
|
|
# class, used by the autoloader to construct Attrib and SetAttrib methods. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# read: calling $Object->Foo will return the value of this record's Foo column |
38
|
|
|
|
|
|
|
# write: calling $Object->SetFoo with a single value will set Foo's value in |
39
|
|
|
|
|
|
|
# both the loaded object and the database |
40
|
|
|
|
|
|
|
sub _ClassAccessible { |
41
|
|
|
|
|
|
|
{ |
42
|
|
|
|
|
|
|
Tofu => { 'read' => 1, 'write' => 1 }, |
43
|
|
|
|
|
|
|
Maz => { 'auto' => 1, }, |
44
|
|
|
|
|
|
|
Roo => { 'read' => 1, 'auto' => 1, 'public' => 1, }, |
45
|
|
|
|
|
|
|
}; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# A subroutine to check a user's password without returning the current value |
49
|
|
|
|
|
|
|
# For security purposes, we didn't expose the Password method above |
50
|
|
|
|
|
|
|
sub IsPassword { |
51
|
|
|
|
|
|
|
my $self = shift; |
52
|
|
|
|
|
|
|
my $try = shift; |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# note two __s in __Value. Subclasses may muck with _Value, but |
55
|
|
|
|
|
|
|
# they should never touch __Value |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
if ( $try eq $self->__Value('Password') ) { |
58
|
|
|
|
|
|
|
return (1); |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
else { |
61
|
|
|
|
|
|
|
return (undef); |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# Override DBIx::SearchBuilder::Create to do some checking on create |
66
|
|
|
|
|
|
|
sub Create { |
67
|
|
|
|
|
|
|
my $self = shift; |
68
|
|
|
|
|
|
|
my %fields = ( |
69
|
|
|
|
|
|
|
UserId => undef, |
70
|
|
|
|
|
|
|
Password => 'default', #Set a default password |
71
|
|
|
|
|
|
|
@_ |
72
|
|
|
|
|
|
|
); |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# Make sure a userid is specified |
75
|
|
|
|
|
|
|
unless ( $fields{'UserId'} ) { |
76
|
|
|
|
|
|
|
die "No userid specified."; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# Get DBIx::SearchBuilder::Record->Create to do the real work |
80
|
|
|
|
|
|
|
return ( |
81
|
|
|
|
|
|
|
$self->SUPER::Create( |
82
|
|
|
|
|
|
|
UserId => $fields{'UserId'}, |
83
|
|
|
|
|
|
|
Password => $fields{'Password'}, |
84
|
|
|
|
|
|
|
Created => time |
85
|
|
|
|
|
|
|
) |
86
|
|
|
|
|
|
|
); |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head1 DESCRIPTION |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
DBIx::SearchBuilder::Record is designed to work with DBIx::SearchBuilder. |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=head2 What is it trying to do. |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
DBIx::SearchBuilder::Record abstracts the agony of writing the common and generally |
97
|
|
|
|
|
|
|
simple SQL statements needed to serialize and De-serialize an object to the |
98
|
|
|
|
|
|
|
database. In a traditional system, you would define various methods on |
99
|
|
|
|
|
|
|
your object 'create', 'find', 'modify', and 'delete' being the most common. |
100
|
|
|
|
|
|
|
In each method you would have a SQL statement like: |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
select * from table where value='blah'; |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
If you wanted to control what data a user could modify, you would have to |
105
|
|
|
|
|
|
|
do some special magic to make accessors do the right thing. Etc. The |
106
|
|
|
|
|
|
|
problem with this approach is that in a majority of the cases, the SQL is |
107
|
|
|
|
|
|
|
incredibly simple and the code from one method/object to the next was |
108
|
|
|
|
|
|
|
basically the same. |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
Enter, DBIx::SearchBuilder::Record. |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
With Record, you can in the simple case, remove all of that code and |
115
|
|
|
|
|
|
|
replace it by defining two methods and inheriting some code. It's pretty |
116
|
|
|
|
|
|
|
simple, and incredibly powerful. For more complex cases, you can |
117
|
|
|
|
|
|
|
do more complicated things by overriding certain methods. Let's stick with |
118
|
|
|
|
|
|
|
the simple case for now. |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
The two methods in question are L and L. All they |
121
|
|
|
|
|
|
|
really do are define some values and send you on your way. As you might |
122
|
|
|
|
|
|
|
have guessed the '_' means that these are private methods. |
123
|
|
|
|
|
|
|
They will get called by your record object's constructor. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=over 4 |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=item '_Init' |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
Defines what table we are talking about, and set a variable to store |
130
|
|
|
|
|
|
|
the database handle. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=item '_ClassAccessible |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
Defines what operations may be performed on various data selected |
135
|
|
|
|
|
|
|
from the database. For example you can define fields to be mutable, |
136
|
|
|
|
|
|
|
or immutable, there are a few other options but I don't understand |
137
|
|
|
|
|
|
|
what they do at this time. |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=back |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
And really, that's it. So let's have some sample code. |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=head2 An Annotated Example |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
The example code below makes the following assumptions: |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=over 4 |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=item * |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
The database is 'postgres', |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=item * |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
The host is 'reason', |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=item * |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
The login name is 'mhat', |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=item * |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
The database is called 'example', |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=item * |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
The table is called 'simple', |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=item * |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
The table looks like so: |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
id integer not NULL, primary_key(id), |
174
|
|
|
|
|
|
|
foo varchar(10), |
175
|
|
|
|
|
|
|
bar varchar(10) |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=back |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
First, let's define our record class in a new module named "Simple.pm". |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
000: package Simple; |
182
|
|
|
|
|
|
|
001: use DBIx::SearchBuilder::Record; |
183
|
|
|
|
|
|
|
002: @ISA = (DBIx::SearchBuilder::Record); |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
This should be pretty obvious, name the package, import ::Record and then |
186
|
|
|
|
|
|
|
define ourself as a subclass of ::Record. |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
003: |
189
|
|
|
|
|
|
|
004: sub _Init { |
190
|
|
|
|
|
|
|
005: my $this = shift; |
191
|
|
|
|
|
|
|
006: my $handle = shift; |
192
|
|
|
|
|
|
|
007: |
193
|
|
|
|
|
|
|
008: $this->_Handle($handle); |
194
|
|
|
|
|
|
|
009: $this->Table("Simple"); |
195
|
|
|
|
|
|
|
010: |
196
|
|
|
|
|
|
|
011: return ($this); |
197
|
|
|
|
|
|
|
012: } |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
Here we set our handle and table name. While it's not obvious so far, we'll |
200
|
|
|
|
|
|
|
see later that $handle (line: 006) gets passed via C<::Record::new> when a |
201
|
|
|
|
|
|
|
new instance is created. That's actually an important concept: the DB handle |
202
|
|
|
|
|
|
|
is not bound to a single object but rather, it is shared across objects. |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
013: |
205
|
|
|
|
|
|
|
014: sub _ClassAccessible { |
206
|
|
|
|
|
|
|
015: { |
207
|
|
|
|
|
|
|
016: Foo => { 'read' => 1 }, |
208
|
|
|
|
|
|
|
017: Bar => { 'read' => 1, 'write' => 1 }, |
209
|
|
|
|
|
|
|
018: Id => { 'read' => 1 } |
210
|
|
|
|
|
|
|
019: }; |
211
|
|
|
|
|
|
|
020: } |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
What's happening might be obvious, but just in case this method is going to |
214
|
|
|
|
|
|
|
return a reference to a hash. That hash is where our columns are defined, |
215
|
|
|
|
|
|
|
as well as what type of operations are acceptable. |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
021: |
218
|
|
|
|
|
|
|
022: 1; |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
Like all perl modules, this needs to end with a true value. |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
Now, on to the code that will actually *do* something with this object. |
223
|
|
|
|
|
|
|
This code would be placed in your Perl script. |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
000: use DBIx::SearchBuilder::Handle; |
226
|
|
|
|
|
|
|
001: use Simple; |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
Use two packages, the first is where I get the DB handle from, the latter |
229
|
|
|
|
|
|
|
is the object I just created. |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
002: |
232
|
|
|
|
|
|
|
003: my $handle = DBIx::SearchBuilder::Handle->new(); |
233
|
|
|
|
|
|
|
004: $handle->Connect( 'Driver' => 'Pg', |
234
|
|
|
|
|
|
|
005: 'Database' => 'test', |
235
|
|
|
|
|
|
|
006: 'Host' => 'reason', |
236
|
|
|
|
|
|
|
007: 'User' => 'mhat', |
237
|
|
|
|
|
|
|
008: 'Password' => ''); |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
Creates a new DBIx::SearchBuilder::Handle, and then connects to the database using |
240
|
|
|
|
|
|
|
that handle. Pretty straight forward, the password '' is what I use |
241
|
|
|
|
|
|
|
when there is no password. I could probably leave it blank, but I find |
242
|
|
|
|
|
|
|
it to be more clear to define it. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
009: |
245
|
|
|
|
|
|
|
010: my $s = Simple->new($handle); |
246
|
|
|
|
|
|
|
011: |
247
|
|
|
|
|
|
|
012: $s->LoadById(1); |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
LoadById is one of four 'LoadBy' methods, as the name suggests it searches |
250
|
|
|
|
|
|
|
for an row in the database that has id='0'. ::SearchBuilder has, what I |
251
|
|
|
|
|
|
|
think is a bug, in that it current requires there to be an id field. More |
252
|
|
|
|
|
|
|
reasonably it also assumes that the id field is unique. LoadById($id) will |
253
|
|
|
|
|
|
|
do undefined things if there is >1 row with the same id. |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
In addition to LoadById, we also have: |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
=over 4 |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=item LoadByCol |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Takes two arguments, a column name and a value. Again, it will do |
262
|
|
|
|
|
|
|
undefined things if you use non-unique things. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=item LoadByCols |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
Takes a hash of columns=>values and returns the *first* to match. |
267
|
|
|
|
|
|
|
First is probably lossy across databases vendors. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=item LoadFromHash |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
Populates this record with data from a DBIx::SearchBuilder. I'm |
272
|
|
|
|
|
|
|
currently assuming that DBIx::SearchBuilder is what we use in |
273
|
|
|
|
|
|
|
cases where we expect > 1 record. More on this later. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=back |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
Now that we have a populated object, we should do something with it! ::Record |
278
|
|
|
|
|
|
|
automagically generates accessos and mutators for us, so all we need to do |
279
|
|
|
|
|
|
|
is call the methods. Accessors are named (), and Mutators are named |
280
|
|
|
|
|
|
|
Set($). On to the example, just appending this to the code from |
281
|
|
|
|
|
|
|
the last example. |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
013: |
284
|
|
|
|
|
|
|
014: print "ID : ", $s->Id(), "\n"; |
285
|
|
|
|
|
|
|
015: print "Foo : ", $s->Foo(), "\n"; |
286
|
|
|
|
|
|
|
016: print "Bar : ", $s->Bar(), "\n"; |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
That's all you have to to get the data. Now to change the data! |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
017: |
291
|
|
|
|
|
|
|
018: $s->SetBar('NewBar'); |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
Pretty simple! That's really all there is to it. Set($) returns |
294
|
|
|
|
|
|
|
a boolean and a string describing the problem. Let's look at an example of |
295
|
|
|
|
|
|
|
what will happen if we try to set a 'Id' which we previously defined as |
296
|
|
|
|
|
|
|
read only. |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
019: my ($res, $str) = $s->SetId('2'); |
299
|
|
|
|
|
|
|
020: if (! $res) { |
300
|
|
|
|
|
|
|
021: ## Print the error! |
301
|
|
|
|
|
|
|
022: print "$str\n"; |
302
|
|
|
|
|
|
|
023: } |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
The output will be: |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
>> Immutable field |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
Currently Set updates the data in the database as soon as you call |
309
|
|
|
|
|
|
|
it. In the future I hope to extend ::Record to better support transactional |
310
|
|
|
|
|
|
|
operations, such that updates will only happen when "you" say so. |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
Finally, adding a removing records from the database. ::Record provides a |
313
|
|
|
|
|
|
|
Create method which simply takes a hash of key=>value pairs. The keys |
314
|
|
|
|
|
|
|
exactly map to database fields. |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
023: ## Get a new record object. |
317
|
|
|
|
|
|
|
024: $s1 = Simple->new($handle); |
318
|
|
|
|
|
|
|
025: $s1->Create('Id' => 4, |
319
|
|
|
|
|
|
|
026: 'Foo' => 'Foooooo', |
320
|
|
|
|
|
|
|
027: 'Bar' => 'Barrrrr'); |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
Poof! A new row in the database has been created! Now let's delete the |
323
|
|
|
|
|
|
|
object! |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
028: |
326
|
|
|
|
|
|
|
029: $s1 = undef; |
327
|
|
|
|
|
|
|
030: $s1 = Simple->new($handle); |
328
|
|
|
|
|
|
|
031: $s1->LoadById(4); |
329
|
|
|
|
|
|
|
032: $s1->Delete(); |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
And it's gone. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
For simple use, that's more or less all there is to it. In the future, we |
334
|
|
|
|
|
|
|
hope to expand this how-to to discuss using container classes, overloading, |
335
|
|
|
|
|
|
|
etc. |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=head1 METHOD NAMING |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
Each method has a lower case alias; '_' is used to separate words. |
340
|
|
|
|
|
|
|
For example, the method C<_PrimaryKeys> has the alias C<_primary_keys>. |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=head1 METHODS |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
=cut |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=head2 new |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
Instantiate a new record object. |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=cut |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
sub new { |
356
|
1630
|
|
|
1630
|
1
|
31644
|
my $proto = shift; |
357
|
|
|
|
|
|
|
|
358
|
1630
|
|
33
|
|
|
4924
|
my $class = ref($proto) || $proto; |
359
|
1630
|
|
|
|
|
3120
|
my $self = {}; |
360
|
1630
|
|
|
|
|
2998
|
bless ($self, $class); |
361
|
1630
|
|
|
|
|
4704
|
$self->_Init(@_); |
362
|
|
|
|
|
|
|
|
363
|
1630
|
|
|
|
|
3596
|
return $self; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
# Not yet documented here. Should almost certainly be overloaded. |
368
|
|
|
|
|
|
|
sub _Init { |
369
|
34
|
|
|
34
|
|
43
|
my $self = shift; |
370
|
34
|
|
|
|
|
49
|
my $handle = shift; |
371
|
34
|
|
|
|
|
83
|
$self->_Handle($handle); |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=head2 id |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
Returns this row's primary key. |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=cut |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
*id = \&Id; |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
sub Id { |
386
|
1788
|
|
|
1788
|
0
|
12831
|
my $pkey = $_[0]->_PrimaryKey(); |
387
|
1788
|
|
|
|
|
4324
|
return $_[0]->{'values'}->{ $pkey }; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
=head2 primary_keys |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
=head2 PrimaryKeys |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
Return a hash of the values of our primary keys for this function. |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=cut |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
sub PrimaryKeys { |
403
|
84
|
|
|
84
|
1
|
191
|
my $self = shift; |
404
|
84
|
|
|
|
|
183
|
return map { $_ => $self->{'values'}->{lc $_} } @{$self->_PrimaryKeys}; |
|
84
|
|
|
|
|
669
|
|
|
84
|
|
|
|
|
255
|
|
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
sub DESTROY { |
411
|
1630
|
|
|
1630
|
|
84340
|
return 1; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
sub AUTOLOAD { |
416
|
40
|
|
|
40
|
|
2344
|
my $self = $_[0]; |
417
|
|
|
|
|
|
|
|
418
|
20
|
|
|
20
|
|
187
|
no strict 'refs'; |
|
20
|
|
|
|
|
38
|
|
|
20
|
|
|
|
|
56460
|
|
419
|
40
|
|
|
|
|
435
|
my ($Attrib) = ( $AUTOLOAD =~ /::(\w+)$/o ); |
420
|
|
|
|
|
|
|
|
421
|
40
|
100
|
|
|
|
189
|
if ( $self->_Accessible( $Attrib, 'read' ) ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
422
|
20
|
|
|
1693
|
|
146
|
*{$AUTOLOAD} = sub { return ( $_[0]->_Value($Attrib) ) }; |
|
20
|
|
|
|
|
136
|
|
|
1693
|
|
|
|
|
30910
|
|
423
|
20
|
|
|
|
|
151
|
goto &$AUTOLOAD; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
elsif ( $self->_Accessible( $Attrib, 'record-read') ) { |
426
|
1
|
|
|
3
|
|
7
|
*{$AUTOLOAD} = sub { $_[0]->_ToRecord( $Attrib, $_[0]->__Value($Attrib) ) }; |
|
1
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
72
|
|
427
|
1
|
|
|
|
|
5
|
goto &$AUTOLOAD; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
elsif ( $self->_Accessible( $Attrib, 'foreign-collection') ) { |
430
|
1
|
|
|
2
|
|
5
|
*{$AUTOLOAD} = sub { $_[0]->_CollectionValue( $Attrib ) }; |
|
1
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
12
|
|
431
|
1
|
|
|
|
|
4
|
goto &$AUTOLOAD; |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
elsif ( $AUTOLOAD =~ /.*::[sS]et_?(\w+)/o ) { |
434
|
9
|
|
|
|
|
40
|
$Attrib = $1; |
435
|
|
|
|
|
|
|
|
436
|
9
|
100
|
|
|
|
32
|
if ( $self->_Accessible( $Attrib, 'write' ) ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
437
|
6
|
|
|
|
|
41
|
*{$AUTOLOAD} = sub { |
438
|
23
|
|
|
23
|
|
2064
|
return ( $_[0]->_Set( Field => $Attrib, Value => $_[1] ) ); |
439
|
6
|
|
|
|
|
41
|
}; |
440
|
6
|
|
|
|
|
37
|
goto &$AUTOLOAD; |
441
|
|
|
|
|
|
|
} elsif ( $self->_Accessible( $Attrib, 'record-write') ) { |
442
|
1
|
|
|
|
|
6
|
*{$AUTOLOAD} = sub { |
443
|
2
|
|
|
2
|
|
365
|
my $self = shift; |
444
|
2
|
|
|
|
|
5
|
my $val = shift; |
445
|
|
|
|
|
|
|
|
446
|
2
|
100
|
|
|
|
12
|
$val = $val->id if UNIVERSAL::isa($val, 'DBIx::SearchBuilder::Record'); |
447
|
2
|
|
|
|
|
13
|
return ( $self->_Set( Field => $Attrib, Value => $val ) ); |
448
|
1
|
|
|
|
|
6
|
}; |
449
|
1
|
|
|
|
|
7
|
goto &$AUTOLOAD; |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
elsif ( $self->_Accessible( $Attrib, 'read' ) ) { |
452
|
1
|
|
|
1
|
|
8
|
*{$AUTOLOAD} = sub { return ( 0, 'Immutable field' ) }; |
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
8
|
|
453
|
1
|
|
|
|
|
8
|
goto &$AUTOLOAD; |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
else { |
456
|
1
|
|
|
|
|
5
|
return ( 0, 'Nonexistant field?' ); |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
elsif ( $AUTOLOAD =~ /.*::(\w+?)_?[oO]bj$/o ) { |
460
|
2
|
|
|
|
|
9
|
$Attrib = $1; |
461
|
2
|
100
|
|
|
|
8
|
if ( $self->_Accessible( $Attrib, 'object' ) ) { |
462
|
1
|
|
|
|
|
7
|
*{$AUTOLOAD} = sub { |
463
|
1
|
|
|
1
|
|
9
|
return (shift)->_Object( |
464
|
|
|
|
|
|
|
Field => $Attrib, |
465
|
|
|
|
|
|
|
Args => [@_], |
466
|
|
|
|
|
|
|
); |
467
|
1
|
|
|
|
|
7
|
}; |
468
|
1
|
|
|
|
|
6
|
goto &$AUTOLOAD; |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
else { |
471
|
1
|
|
|
|
|
5
|
return ( 0, 'No object mapping for field' ); |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
#Previously, I checked for writability here. but I'm not sure that's the |
476
|
|
|
|
|
|
|
#right idea. it breaks the ability to do ValidateQueue for a ticket |
477
|
|
|
|
|
|
|
#on creation. |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
elsif ( $AUTOLOAD =~ /.*::[vV]alidate_?(\w+)/o ) { |
480
|
6
|
|
|
|
|
26
|
$Attrib = $1; |
481
|
|
|
|
|
|
|
|
482
|
6
|
|
|
15
|
|
36
|
*{$AUTOLOAD} = sub { return ( $_[0]->_Validate( $Attrib, $_[1] ) ) }; |
|
6
|
|
|
|
|
34
|
|
|
15
|
|
|
|
|
109
|
|
483
|
6
|
|
|
|
|
31
|
goto &$AUTOLOAD; |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
# TODO: if autoload = 0 or 1 _ then a combination of lowercase and _ chars, |
487
|
|
|
|
|
|
|
# turn them into studlycapped phrases |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
else { |
490
|
1
|
|
|
|
|
4
|
my ( $package, $filename, $line ); |
491
|
1
|
|
|
|
|
5
|
( $package, $filename, $line ) = caller; |
492
|
|
|
|
|
|
|
|
493
|
1
|
|
|
|
|
14
|
die "$AUTOLOAD Unimplemented in $package. ($filename line $line) \n"; |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=head2 _Accessible KEY MODE |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
Private method. |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
Returns undef unless C is accessible in C otherwise returns C value |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=cut |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
sub _Accessible { |
510
|
2262
|
|
|
2262
|
|
3426
|
my $self = shift; |
511
|
2262
|
|
|
|
|
3381
|
my $attr = shift; |
512
|
2262
|
|
100
|
|
|
5012
|
my $mode = lc(shift || ''); |
513
|
|
|
|
|
|
|
|
514
|
2262
|
|
|
|
|
4827
|
my $attribute = $self->_ClassAccessible(@_)->{$attr}; |
515
|
2262
|
100
|
|
|
|
19662
|
return unless defined $attribute; |
516
|
332
|
|
|
|
|
1419
|
return $attribute->{$mode}; |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
=head2 _PrimaryKeys |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
Return our primary keys. (Subclasses should override this, but our default is that we have one primary key, named 'id'.) |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
=cut |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
sub _PrimaryKeys { |
528
|
1982
|
|
|
1982
|
|
2910
|
my $self = shift; |
529
|
1982
|
|
|
|
|
4329
|
return ['id']; |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
sub _PrimaryKey { |
534
|
1842
|
|
|
1842
|
|
2906
|
my $self = shift; |
535
|
1842
|
|
|
|
|
3194
|
my $pkeys = $self->_PrimaryKeys(); |
536
|
1842
|
50
|
33
|
|
|
6873
|
die "No primary key" unless ( ref($pkeys) eq 'ARRAY' and $pkeys->[0] ); |
537
|
1842
|
50
|
|
|
|
3847
|
die "Too many primary keys" unless ( scalar(@$pkeys) == 1 ); |
538
|
1842
|
|
|
|
|
4174
|
return $pkeys->[0]; |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=head2 _ClassAccessible |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
An older way to specify fields attributes in a derived class. |
545
|
|
|
|
|
|
|
(The current preferred method is by overriding C; if you do |
546
|
|
|
|
|
|
|
this and don't override C<_ClassAccessible>, the module will generate |
547
|
|
|
|
|
|
|
an appropriate C<_ClassAccessible> based on your C.) |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
Here's an example declaration: |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
sub _ClassAccessible { |
552
|
|
|
|
|
|
|
{ |
553
|
|
|
|
|
|
|
Tofu => { 'read'=>1, 'write'=>1 }, |
554
|
|
|
|
|
|
|
Maz => { 'auto'=>1, }, |
555
|
|
|
|
|
|
|
Roo => { 'read'=>1, 'auto'=>1, 'public'=>1, }, |
556
|
|
|
|
|
|
|
}; |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=cut |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
sub _ClassAccessible { |
563
|
53
|
|
|
53
|
|
121
|
my $self = shift; |
564
|
|
|
|
|
|
|
|
565
|
53
|
50
|
|
|
|
202
|
return $self->_ClassAccessibleFromSchema if $self->can('Schema'); |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
# XXX This is stub code to deal with the old way we used to do _Accessible |
568
|
|
|
|
|
|
|
# It should never be called by modern code |
569
|
|
|
|
|
|
|
|
570
|
0
|
|
|
|
|
0
|
my %accessible; |
571
|
0
|
|
|
|
|
0
|
while ( my $col = shift ) { |
572
|
|
|
|
|
|
|
$accessible{$col}->{lc($_)} = 1 |
573
|
0
|
|
|
|
|
0
|
foreach split(/[\/,]/, shift); |
574
|
|
|
|
|
|
|
} |
575
|
0
|
|
|
|
|
0
|
return(\%accessible); |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
sub _ClassAccessibleFromSchema { |
579
|
53
|
|
|
53
|
|
82
|
my $self = shift; |
580
|
|
|
|
|
|
|
|
581
|
53
|
|
|
|
|
81
|
my $accessible = {}; |
582
|
53
|
|
|
|
|
112
|
foreach my $key ($self->_PrimaryKeys) { |
583
|
53
|
|
|
|
|
202
|
$accessible->{$key} = { 'read' => 1 }; |
584
|
|
|
|
|
|
|
}; |
585
|
|
|
|
|
|
|
|
586
|
53
|
|
|
|
|
169
|
my $schema = $self->Schema; |
587
|
|
|
|
|
|
|
|
588
|
53
|
|
|
|
|
357
|
for my $field (keys %$schema) { |
589
|
104
|
100
|
|
|
|
255
|
if ($schema->{$field}{'TYPE'}) { |
|
|
50
|
|
|
|
|
|
590
|
53
|
|
|
|
|
146
|
$accessible->{$field} = { 'read' => 1, 'write' => 1 }; |
591
|
|
|
|
|
|
|
} elsif (my $refclass = $schema->{$field}{'REFERENCES'}) { |
592
|
51
|
100
|
|
|
|
174
|
if (UNIVERSAL::isa($refclass, 'DBIx::SearchBuilder::Record')) { |
|
|
50
|
|
|
|
|
|
593
|
40
|
50
|
|
|
|
106
|
if ($field =~ /(.*)_id$/) { |
594
|
0
|
|
|
|
|
0
|
$accessible->{$field} = { 'read' => 1, 'write' => 1 }; |
595
|
0
|
|
|
|
|
0
|
$accessible->{$1} = { 'record-read' => 1, 'column' => $field }; |
596
|
|
|
|
|
|
|
} else { |
597
|
40
|
|
|
|
|
114
|
$accessible->{$field} = { 'record-read' => 1, 'record-write' => 1 }; |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
} elsif (UNIVERSAL::isa($refclass, 'DBIx::SearchBuilder')) { |
600
|
11
|
|
|
|
|
43
|
$accessible->{$field} = { 'foreign-collection' => 1 }; |
601
|
|
|
|
|
|
|
} else { |
602
|
0
|
|
|
|
|
0
|
warn "Error: $refclass neither Record nor Collection"; |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
53
|
|
|
|
|
185
|
return $accessible; |
608
|
|
|
|
|
|
|
} |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
sub _ToRecord { |
612
|
3
|
|
|
3
|
|
7
|
my $self = shift; |
613
|
3
|
|
|
|
|
4
|
my $field = shift; |
614
|
3
|
|
|
|
|
6
|
my $value = shift; |
615
|
|
|
|
|
|
|
|
616
|
3
|
50
|
|
|
|
8
|
return unless defined $value; |
617
|
|
|
|
|
|
|
|
618
|
3
|
|
|
|
|
8
|
my $schema = $self->Schema; |
619
|
3
|
|
33
|
|
|
18
|
my $description = $schema->{$field} || $schema->{$field . "_id"}; |
620
|
|
|
|
|
|
|
|
621
|
3
|
50
|
|
|
|
8
|
die "Can't get schema for $field on $self" unless $description; |
622
|
|
|
|
|
|
|
|
623
|
3
|
50
|
|
|
|
19
|
return unless $description; |
624
|
|
|
|
|
|
|
|
625
|
3
|
50
|
|
|
|
10
|
return $value unless $description->{'REFERENCES'}; |
626
|
|
|
|
|
|
|
|
627
|
3
|
|
|
|
|
7
|
my $classname = $description->{'REFERENCES'}; |
628
|
|
|
|
|
|
|
|
629
|
3
|
50
|
|
|
|
12
|
return unless UNIVERSAL::isa($classname, 'DBIx::SearchBuilder::Record'); |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
# XXX TODO FIXME perhaps this is not what should be passed to new, but it needs it |
632
|
3
|
|
|
|
|
10
|
my $object = $classname->new( $self->_Handle ); |
633
|
3
|
|
|
|
|
27
|
$object->LoadById( $value ); |
634
|
3
|
|
|
|
|
15
|
return $object; |
635
|
|
|
|
|
|
|
} |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
sub _CollectionValue { |
638
|
2
|
|
|
2
|
|
6
|
my $self = shift; |
639
|
|
|
|
|
|
|
|
640
|
2
|
|
|
|
|
3
|
my $method_name = shift; |
641
|
2
|
50
|
|
|
|
6
|
return unless defined $method_name; |
642
|
|
|
|
|
|
|
|
643
|
2
|
|
|
|
|
7
|
my $schema = $self->Schema; |
644
|
2
|
|
|
|
|
13
|
my $description = $schema->{$method_name}; |
645
|
2
|
50
|
|
|
|
6
|
return unless $description; |
646
|
|
|
|
|
|
|
|
647
|
2
|
|
|
|
|
15
|
my $classname = $description->{'REFERENCES'}; |
648
|
|
|
|
|
|
|
|
649
|
2
|
50
|
|
|
|
24
|
return unless UNIVERSAL::isa($classname, 'DBIx::SearchBuilder'); |
650
|
|
|
|
|
|
|
|
651
|
2
|
|
|
|
|
8
|
my $coll = $classname->new( Handle => $self->_Handle ); |
652
|
|
|
|
|
|
|
|
653
|
2
|
|
|
|
|
7
|
$coll->Limit( FIELD => $description->{'KEY'}, VALUE => $self->id); |
654
|
|
|
|
|
|
|
|
655
|
2
|
|
|
|
|
7
|
return $coll; |
656
|
|
|
|
|
|
|
} |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
# sub {{{ ReadableAttributes |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
=head2 ReadableAttributes |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
Returns an array of the attributes of this class defined as "read" => 1 in this class' _ClassAccessible datastructure |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
=cut |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
sub ReadableAttributes { |
667
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
668
|
1
|
|
|
|
|
4
|
my $ca = $self->_ClassAccessible(); |
669
|
1
|
50
|
|
|
|
12
|
my @readable = grep { $ca->{$_}->{'read'} or $ca->{$_}->{'record-read'} } sort keys %{$ca}; |
|
4
|
|
|
|
|
15
|
|
|
1
|
|
|
|
|
9
|
|
670
|
1
|
|
|
|
|
13
|
return (@readable); |
671
|
|
|
|
|
|
|
} |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=head2 WritableAttributes |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
Returns an array of the attributes of this class defined as "write" => 1 in this class' _ClassAccessible datastructure |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
=cut |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
sub WritableAttributes { |
682
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
683
|
1
|
|
|
|
|
4
|
my $ca = $self->_ClassAccessible(); |
684
|
1
|
100
|
|
|
|
12
|
my @writable = grep { $ca->{$_}->{'write'} || $ca->{$_}->{'record-write'} } sort keys %{$ca}; |
|
4
|
|
|
|
|
22
|
|
|
1
|
|
|
|
|
7
|
|
685
|
1
|
|
|
|
|
11
|
return @writable; |
686
|
|
|
|
|
|
|
} |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
=head2 __Value |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
Takes a field name and returns that field's value. Subclasses should never |
694
|
|
|
|
|
|
|
override __Value. |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=cut |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
sub __Value { |
700
|
1871
|
|
|
1871
|
|
2814
|
my $self = shift; |
701
|
1871
|
|
|
|
|
2993
|
my $field = lc shift; |
702
|
|
|
|
|
|
|
|
703
|
1871
|
|
33
|
|
|
3737
|
$field = $self->_Accessible($field, "column") || $field; |
704
|
|
|
|
|
|
|
|
705
|
1871
|
100
|
|
|
|
7789
|
return $self->{'values'}{$field} if $self->{'fetched'}{$field}; |
706
|
3
|
|
|
|
|
11
|
$self->{'fetched'}{$field} = 1; |
707
|
|
|
|
|
|
|
|
708
|
3
|
|
|
|
|
14
|
my %pk = $self->PrimaryKeys; |
709
|
3
|
50
|
|
|
|
27
|
return undef if grep !defined, values %pk; |
710
|
|
|
|
|
|
|
|
711
|
3
|
|
|
|
|
16
|
my $query = "SELECT $field FROM ". $self->QuotedTableName |
712
|
|
|
|
|
|
|
." WHERE ". join " AND ", map "$_ = ?", sort keys %pk; |
713
|
3
|
100
|
|
|
|
14
|
my $sth = $self->_Handle->SimpleQuery( $query, sorted_values(%pk) ) or return undef; |
714
|
2
|
|
|
|
|
133
|
return $self->{'values'}{$field} = ($sth->fetchrow_array)[0]; |
715
|
|
|
|
|
|
|
} |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=head2 _Value |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
_Value takes a single column name and returns that column's value for this row. |
720
|
|
|
|
|
|
|
Subclasses can override _Value to insert custom access control. |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=cut |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
sub _Value { |
726
|
1701
|
|
|
1701
|
|
2494
|
my $self = shift; |
727
|
1701
|
|
|
|
|
3002
|
return ($self->__Value(@_)); |
728
|
|
|
|
|
|
|
} |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
=head2 _Set |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
_Set takes a single column name and a single unquoted value. |
735
|
|
|
|
|
|
|
It updates both the in-memory value of this column and the in-database copy. |
736
|
|
|
|
|
|
|
Subclasses can override _Set to insert custom access control. |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=cut |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
sub _Set { |
742
|
26
|
|
|
26
|
|
1110
|
my $self = shift; |
743
|
26
|
|
|
|
|
91
|
return ($self->__Set(@_)); |
744
|
|
|
|
|
|
|
} |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
sub __Set { |
750
|
26
|
|
|
26
|
|
60
|
my $self = shift; |
751
|
|
|
|
|
|
|
|
752
|
26
|
|
|
|
|
147
|
my %args = ( |
753
|
|
|
|
|
|
|
'Field' => undef, |
754
|
|
|
|
|
|
|
'Value' => undef, |
755
|
|
|
|
|
|
|
'IsSQL' => undef, |
756
|
|
|
|
|
|
|
@_ |
757
|
|
|
|
|
|
|
); |
758
|
|
|
|
|
|
|
|
759
|
26
|
|
|
|
|
88
|
$args{'Column'} = delete $args{'Field'}; |
760
|
26
|
|
|
|
|
61
|
$args{'IsSQLFunction'} = delete $args{'IsSQL'}; |
761
|
|
|
|
|
|
|
|
762
|
26
|
|
|
|
|
140
|
my $ret = Class::ReturnValue->new(); |
763
|
|
|
|
|
|
|
|
764
|
26
|
100
|
|
|
|
276
|
unless ( $args{'Column'} ) { |
765
|
1
|
|
|
|
|
6
|
$ret->as_array( 0, 'No column specified' ); |
766
|
1
|
|
|
|
|
17
|
$ret->as_error( |
767
|
|
|
|
|
|
|
errno => 5, |
768
|
|
|
|
|
|
|
do_backtrace => 0, |
769
|
|
|
|
|
|
|
message => "No column specified" |
770
|
|
|
|
|
|
|
); |
771
|
1
|
|
|
|
|
19
|
return ( $ret->return_value ); |
772
|
|
|
|
|
|
|
} |
773
|
25
|
|
|
|
|
82
|
my $column = lc $args{'Column'}; |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
# XXX: OLD behaviour, no_undefs_in_set will go away |
776
|
25
|
50
|
66
|
|
|
135
|
if ( !defined $args{'Value'} && $self->{'no_undefs_in_set' } ) { |
777
|
0
|
|
|
|
|
0
|
$ret->as_array( 0, "No value passed to _Set" ); |
778
|
0
|
|
|
|
|
0
|
$ret->as_error( |
779
|
|
|
|
|
|
|
errno => 2, |
780
|
|
|
|
|
|
|
do_backtrace => 0, |
781
|
|
|
|
|
|
|
message => "No value passed to _Set" |
782
|
|
|
|
|
|
|
); |
783
|
0
|
|
|
|
|
0
|
return ( $ret->return_value ); |
784
|
|
|
|
|
|
|
} |
785
|
|
|
|
|
|
|
|
786
|
25
|
100
|
|
|
|
95
|
if ( defined $args{'Value'} ) { |
787
|
17
|
100
|
66
|
|
|
90
|
if ( $args{'Value'} eq '' && |
|
|
|
100
|
|
|
|
|
788
|
|
|
|
|
|
|
( $self->_Accessible( $args{'Column'}, 'is_numeric' ) |
789
|
|
|
|
|
|
|
|| ($self->_Accessible( $args{'Column'}, 'type' ) || '') =~ /INT/i ) ) |
790
|
|
|
|
|
|
|
{ |
791
|
3
|
|
|
|
|
10
|
$args{'Value'} = 0; |
792
|
|
|
|
|
|
|
} |
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
else { |
795
|
8
|
100
|
|
|
|
40
|
if ( $self->_Accessible( $args{Column}, 'no_nulls' ) ) { |
796
|
4
|
|
|
|
|
25
|
my $default = $self->_Accessible( $args{Column}, 'default' ); |
797
|
|
|
|
|
|
|
|
798
|
4
|
100
|
|
|
|
24
|
if ( defined $default ) { |
799
|
2
|
|
|
|
|
8
|
$args{'Value'} = $default; |
800
|
|
|
|
|
|
|
} |
801
|
|
|
|
|
|
|
else { |
802
|
2
|
|
|
|
|
12
|
$ret->as_array( 0, 'Illegal value for non-nullable field ' . $args{'Column'} . ": undef/null value provided and no default specified by class" ); |
803
|
|
|
|
|
|
|
$ret->as_error( |
804
|
|
|
|
|
|
|
errno => 3, |
805
|
|
|
|
|
|
|
do_backtrace => 0, |
806
|
2
|
|
|
|
|
32
|
message => "Illegal value for non-nullable field " . $args{'Column'} . ": undef/null value provided and no default specified by class" |
807
|
|
|
|
|
|
|
); |
808
|
2
|
|
|
|
|
37
|
return ( $ret->return_value ); |
809
|
|
|
|
|
|
|
} |
810
|
|
|
|
|
|
|
} |
811
|
|
|
|
|
|
|
} |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
# First, we truncate the value, if we need to. |
814
|
23
|
|
|
|
|
90
|
$args{'Value'} = $self->TruncateValue( $args{'Column'}, $args{'Value'} ); |
815
|
|
|
|
|
|
|
|
816
|
23
|
|
|
|
|
249
|
my $current_value = $self->__Value($column); |
817
|
|
|
|
|
|
|
|
818
|
23
|
100
|
100
|
|
|
232
|
if ( |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
819
|
|
|
|
|
|
|
( !defined $args{'Value'} && !defined $current_value ) |
820
|
|
|
|
|
|
|
|| ( defined $args{'Value'} |
821
|
|
|
|
|
|
|
&& defined $current_value |
822
|
|
|
|
|
|
|
&& ( $args{'Value'} eq $current_value ) ) |
823
|
|
|
|
|
|
|
) |
824
|
|
|
|
|
|
|
{ |
825
|
3
|
|
|
|
|
16
|
$ret->as_array( 0, "That is already the current value" ); |
826
|
3
|
|
|
|
|
60
|
$ret->as_error( |
827
|
|
|
|
|
|
|
errno => 1, |
828
|
|
|
|
|
|
|
do_backtrace => 0, |
829
|
|
|
|
|
|
|
message => "That is already the current value" |
830
|
|
|
|
|
|
|
); |
831
|
3
|
|
|
|
|
69
|
return ( $ret->return_value ); |
832
|
|
|
|
|
|
|
} |
833
|
|
|
|
|
|
|
|
834
|
20
|
|
|
|
|
71
|
my $method = "Validate" . $args{'Column'}; |
835
|
20
|
100
|
|
|
|
140
|
unless ( $self->$method( $args{'Value'} ) ) { |
836
|
1
|
|
|
|
|
23
|
$ret->as_array( 0, 'Illegal value for ' . $args{'Column'} ); |
837
|
|
|
|
|
|
|
$ret->as_error( |
838
|
|
|
|
|
|
|
errno => 3, |
839
|
|
|
|
|
|
|
do_backtrace => 0, |
840
|
1
|
|
|
|
|
26
|
message => "Illegal value for " . $args{'Column'} |
841
|
|
|
|
|
|
|
); |
842
|
1
|
|
|
|
|
25
|
return ( $ret->return_value ); |
843
|
|
|
|
|
|
|
} |
844
|
|
|
|
|
|
|
|
845
|
19
|
|
|
|
|
206
|
$args{'Table'} = $self->Table(); |
846
|
19
|
|
|
|
|
78
|
$args{'PrimaryKeys'} = { $self->PrimaryKeys() }; |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
# The blob handling will destroy $args{'Value'}. But we assign |
849
|
|
|
|
|
|
|
# that back to the object at the end. this works around that |
850
|
19
|
|
|
|
|
55
|
my $unmunged_value = $args{'Value'}; |
851
|
|
|
|
|
|
|
|
852
|
19
|
50
|
|
|
|
70
|
unless ( $self->_Handle->KnowsBLOBs ) { |
853
|
|
|
|
|
|
|
# Support for databases which don't deal with LOBs automatically |
854
|
0
|
|
|
|
|
0
|
my $ca = $self->_ClassAccessible(); |
855
|
0
|
|
|
|
|
0
|
my $key = $args{'Column'}; |
856
|
0
|
0
|
|
|
|
0
|
if ( $ca->{$key}->{'type'} =~ /^(text|longtext|clob|blob|lob)$/i ) { |
857
|
0
|
|
|
|
|
0
|
my $bhash = $self->_Handle->BLOBParams( $key, $ca->{$key}->{'type'} ); |
858
|
0
|
|
|
|
|
0
|
$bhash->{'value'} = $args{'Value'}; |
859
|
0
|
|
|
|
|
0
|
$args{'Value'} = $bhash; |
860
|
|
|
|
|
|
|
} |
861
|
|
|
|
|
|
|
} |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
|
864
|
19
|
|
|
|
|
71
|
my $val = $self->_Handle->UpdateRecordValue(%args); |
865
|
19
|
50
|
|
|
|
102
|
unless ($val) { |
866
|
|
|
|
|
|
|
my $message = |
867
|
|
|
|
|
|
|
$args{'Column'} |
868
|
|
|
|
|
|
|
. " could not be set to " |
869
|
0
|
0
|
|
|
|
0
|
. ( defined $args{'Value'} ? $args{'Value'} : 'undef' ) . "."; |
870
|
0
|
|
|
|
|
0
|
$ret->as_array( 0, $message); |
871
|
0
|
|
|
|
|
0
|
$ret->as_error( |
872
|
|
|
|
|
|
|
errno => 4, |
873
|
|
|
|
|
|
|
do_backtrace => 0, |
874
|
|
|
|
|
|
|
message => $message |
875
|
|
|
|
|
|
|
); |
876
|
0
|
|
|
|
|
0
|
return ( $ret->return_value ); |
877
|
|
|
|
|
|
|
} |
878
|
|
|
|
|
|
|
# If we've performed some sort of "functional update" |
879
|
|
|
|
|
|
|
# then we need to reload the object from the DB to know what's |
880
|
|
|
|
|
|
|
# really going on. (ex SET Cost = Cost+5) |
881
|
19
|
50
|
|
|
|
85
|
if ( $args{'IsSQLFunction'} ) { |
882
|
0
|
|
|
|
|
0
|
$self->Load( $self->Id ); |
883
|
|
|
|
|
|
|
} |
884
|
|
|
|
|
|
|
else { |
885
|
19
|
|
|
|
|
104
|
$self->{'values'}->{"$column"} = $unmunged_value; |
886
|
|
|
|
|
|
|
} |
887
|
19
|
|
|
|
|
134
|
$ret->as_array( 1, "The new value has been set." ); |
888
|
19
|
|
|
|
|
713
|
return ( $ret->return_value ); |
889
|
|
|
|
|
|
|
} |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
=head2 _Canonicalize PARAMHASH |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
This routine massages an input value (VALUE) for FIELD into something that's |
894
|
|
|
|
|
|
|
going to be acceptable. |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
Takes |
897
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
=over |
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
=item FIELD |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
=item VALUE |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
=item FUNCTION |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
=back |
907
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
Takes: |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
=over |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
=item FIELD |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
=item VALUE |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
=item FUNCTION |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
=back |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
Returns a replacement VALUE. |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
=cut |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
sub _Canonicalize { |
926
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
927
|
0
|
|
|
|
|
0
|
my $field = shift; |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
} |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
=head2 _Validate FIELD VALUE |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
Validate that VALUE will be an acceptable value for FIELD. |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
Currently, this routine does nothing whatsoever. |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
If it succeeds (which is always the case right now), returns true. Otherwise returns false. |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
=cut |
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
sub _Validate { |
948
|
15
|
|
|
15
|
|
41
|
my $self = shift; |
949
|
15
|
|
|
|
|
33
|
my $field = shift; |
950
|
15
|
|
|
|
|
54
|
my $value = shift; |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
#Check type of input |
953
|
|
|
|
|
|
|
#If it's null, are nulls permitted? |
954
|
|
|
|
|
|
|
#If it's an int, check the # of bits |
955
|
|
|
|
|
|
|
#If it's a string, |
956
|
|
|
|
|
|
|
#check length |
957
|
|
|
|
|
|
|
#check for nonprintables |
958
|
|
|
|
|
|
|
#If it's a blob, check for length |
959
|
|
|
|
|
|
|
#In an ideal world, if this is a link to another table, check the dependency. |
960
|
15
|
|
|
|
|
57
|
return(1); |
961
|
|
|
|
|
|
|
} |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
=head2 TruncateValue KEY VALUE |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
Truncate a value that's about to be set so that it will fit inside the database' |
968
|
|
|
|
|
|
|
s idea of how big the column is. |
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
(Actually, it looks at SearchBuilder's concept of the database, not directly into the db). |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
=cut |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
sub TruncateValue { |
975
|
279
|
|
|
279
|
1
|
629
|
my $self = shift; |
976
|
279
|
|
|
|
|
587
|
my $key = shift; |
977
|
279
|
|
|
|
|
564
|
my $value = shift; |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
# We don't need to truncate empty things. |
980
|
279
|
100
|
|
|
|
814
|
return undef unless defined $value; |
981
|
|
|
|
|
|
|
|
982
|
263
|
|
|
|
|
800
|
my $metadata = $self->_ClassAccessible->{$key}; |
983
|
263
|
100
|
|
|
|
2437
|
return $value unless $metadata; |
984
|
|
|
|
|
|
|
|
985
|
262
|
|
|
|
|
513
|
my $truncate_to; |
986
|
262
|
100
|
66
|
|
|
2836
|
if ( $metadata->{'length'} && !$metadata->{'is_numeric'} ) { |
|
|
100
|
100
|
|
|
|
|
987
|
3
|
|
|
|
|
12
|
$truncate_to = int $metadata->{'length'}; |
988
|
|
|
|
|
|
|
} |
989
|
|
|
|
|
|
|
elsif ($metadata->{'type'} && $metadata->{'type'} =~ /char\((\d+)\)/ ) { |
990
|
138
|
|
|
|
|
588
|
$truncate_to = $1; |
991
|
|
|
|
|
|
|
} |
992
|
262
|
100
|
|
|
|
995
|
return $value unless $truncate_to; |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
# return asap if length in bytes is smaller than limit |
995
|
20
|
100
|
|
20
|
|
183
|
return $value if $truncate_to >= do { use bytes; length $value }; |
|
20
|
|
|
|
|
43
|
|
|
20
|
|
|
|
|
135
|
|
|
141
|
|
|
|
|
282
|
|
|
141
|
|
|
|
|
1037
|
|
996
|
|
|
|
|
|
|
|
997
|
5
|
50
|
|
|
|
27
|
if ( Encode::is_utf8($value) ) { |
998
|
0
|
|
|
|
|
0
|
return Encode::decode_utf8( |
999
|
|
|
|
|
|
|
substr( Encode::encode_utf8( $value ), 0, $truncate_to ), |
1000
|
|
|
|
|
|
|
Encode::FB_QUIET(), |
1001
|
|
|
|
|
|
|
); |
1002
|
|
|
|
|
|
|
} |
1003
|
|
|
|
|
|
|
else { |
1004
|
|
|
|
|
|
|
# XXX: if it's not UTF-8 then why do we convert it to? |
1005
|
5
|
|
|
|
|
29
|
return Encode::encode_utf8( Encode::decode_utf8 ( |
1006
|
|
|
|
|
|
|
substr( $value, 0, $truncate_to ), Encode::FB_QUIET(), |
1007
|
|
|
|
|
|
|
) ); |
1008
|
|
|
|
|
|
|
} |
1009
|
|
|
|
|
|
|
} |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
=head2 _Object |
1013
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
_Object takes a single column name and an array reference. |
1015
|
|
|
|
|
|
|
It creates new object instance of class specified in _ClassAccessable |
1016
|
|
|
|
|
|
|
structure and calls LoadById on recently created object with the |
1017
|
|
|
|
|
|
|
current column value as argument. It uses the array reference as |
1018
|
|
|
|
|
|
|
the object constructor's arguments. |
1019
|
|
|
|
|
|
|
Subclasses can override _Object to insert custom access control or |
1020
|
|
|
|
|
|
|
define default constructor arguments. |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
Note that if you are using a C with a C field, |
1023
|
|
|
|
|
|
|
this is unnecessary: the method to access the column's value will |
1024
|
|
|
|
|
|
|
automatically turn it into the appropriate object. |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
=cut |
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
sub _Object { |
1029
|
1
|
|
|
1
|
|
9
|
my $self = shift; |
1030
|
1
|
|
|
|
|
12
|
return $self->__Object(@_); |
1031
|
|
|
|
|
|
|
} |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
sub __Object { |
1034
|
1
|
|
|
1
|
|
3
|
my $self = shift; |
1035
|
1
|
|
|
|
|
8
|
my %args = ( Field => '', Args => [], @_ ); |
1036
|
|
|
|
|
|
|
|
1037
|
1
|
|
|
|
|
3
|
my $field = $args{'Field'}; |
1038
|
1
|
|
|
|
|
5
|
my $class = $self->_Accessible( $field, 'object' ); |
1039
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
# Globs magic to be sure that we call 'eval "require $class"' only once |
1041
|
|
|
|
|
|
|
# because eval is quite slow -- cubic@acronis.ru |
1042
|
20
|
|
|
20
|
|
4421
|
no strict qw( refs ); |
|
20
|
|
|
|
|
48
|
|
|
20
|
|
|
|
|
36018
|
|
1043
|
1
|
|
|
|
|
4
|
my $vglob = ${ $class . '::' }{'VERSION'}; |
|
1
|
|
|
|
|
13
|
|
1044
|
1
|
50
|
50
|
|
|
11
|
unless ( $vglob && *$vglob{'SCALAR'} ) { |
1045
|
0
|
|
|
|
|
0
|
eval "require $class"; |
1046
|
0
|
0
|
|
|
|
0
|
die "Couldn't use $class: $@" if ($@); |
1047
|
0
|
0
|
0
|
|
|
0
|
unless ( $vglob && *$vglob{'SCALAR'} ) { |
1048
|
0
|
|
|
|
|
0
|
*{ $class . "::VERSION" } = '-1, By DBIx::SearchBuilder'; |
|
0
|
|
|
|
|
0
|
|
1049
|
|
|
|
|
|
|
} |
1050
|
|
|
|
|
|
|
} |
1051
|
|
|
|
|
|
|
|
1052
|
1
|
|
|
|
|
3
|
my $object = $class->new( @{ $args{'Args'} } ); |
|
1
|
|
|
|
|
5
|
|
1053
|
1
|
|
|
|
|
8
|
$object->LoadById( $self->__Value($field) ); |
1054
|
1
|
|
|
|
|
7
|
return $object; |
1055
|
|
|
|
|
|
|
} |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
# load should do a bit of overloading |
1061
|
|
|
|
|
|
|
# if we call it with only one argument, we're trying to load by reference. |
1062
|
|
|
|
|
|
|
# if we call it with a passel of arguments, we're trying to load by value |
1063
|
|
|
|
|
|
|
# The latter is primarily important when we've got a whole set of record that we're |
1064
|
|
|
|
|
|
|
# reading in with a recordset class and want to instantiate objefcts for each record. |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
=head2 Load |
1067
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
Takes a single argument, $id. Calls LoadById to retrieve the row whose primary key |
1069
|
|
|
|
|
|
|
is $id |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
=cut |
1072
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
sub Load { |
1076
|
48
|
|
|
48
|
1
|
21162
|
my $self = shift; |
1077
|
48
|
|
|
|
|
228
|
return $self->LoadById(@_); |
1078
|
|
|
|
|
|
|
} |
1079
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
=head2 LoadByCol |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
Takes two arguments, a column and a value. The column can be any table column |
1084
|
|
|
|
|
|
|
which contains unique values. Behavior when using a non-unique value is |
1085
|
|
|
|
|
|
|
undefined |
1086
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
=cut |
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
sub LoadByCol { |
1090
|
2
|
|
|
2
|
1
|
19
|
my $self = shift; |
1091
|
2
|
|
|
|
|
6
|
return $self->LoadByCols(@_); |
1092
|
|
|
|
|
|
|
} |
1093
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
|
1095
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
=head2 LoadByCols |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
Takes a hash of columns and values. Loads the first record that matches all |
1099
|
|
|
|
|
|
|
keys. |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
The hash's keys are the columns to look at. |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
The hash's values are either: scalar values to look for |
1104
|
|
|
|
|
|
|
OR has references which contain 'operator' and 'value' |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
=cut |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
sub LoadByCols { |
1110
|
58
|
|
|
58
|
1
|
135
|
my $self = shift; |
1111
|
58
|
|
|
|
|
227
|
my %hash = (@_); |
1112
|
58
|
|
|
|
|
145
|
my (@bind, @phrases); |
1113
|
58
|
|
|
|
|
279
|
foreach my $key (sort keys %hash) { |
1114
|
60
|
100
|
100
|
|
|
409
|
if (defined $hash{$key} && $hash{$key} ne '') { |
1115
|
58
|
|
|
|
|
131
|
my $op; |
1116
|
|
|
|
|
|
|
my $value; |
1117
|
58
|
|
|
|
|
122
|
my $function = "?"; |
1118
|
58
|
100
|
|
|
|
168
|
if (ref $hash{$key} eq 'HASH') { |
1119
|
1
|
|
|
|
|
3
|
$op = $hash{$key}->{operator}; |
1120
|
1
|
|
|
|
|
3
|
$value = $hash{$key}->{value}; |
1121
|
1
|
|
50
|
|
|
6
|
$function = $hash{$key}->{function} || "?"; |
1122
|
|
|
|
|
|
|
} else { |
1123
|
57
|
|
|
|
|
116
|
$op = '='; |
1124
|
57
|
|
|
|
|
148
|
$value = $hash{$key}; |
1125
|
|
|
|
|
|
|
} |
1126
|
|
|
|
|
|
|
|
1127
|
58
|
|
|
|
|
254
|
push @phrases, "$key $op $function"; |
1128
|
58
|
|
|
|
|
210
|
push @bind, $value; |
1129
|
|
|
|
|
|
|
} |
1130
|
|
|
|
|
|
|
else { |
1131
|
2
|
|
|
|
|
13
|
push @phrases, "($key IS NULL OR $key = ?)"; |
1132
|
2
|
|
|
|
|
9
|
my $meta = $self->_ClassAccessible->{$key}; |
1133
|
2
|
|
50
|
|
|
47
|
$meta->{'type'} ||= ''; |
1134
|
|
|
|
|
|
|
# TODO: type checking should be done in generic way |
1135
|
2
|
100
|
66
|
|
|
47
|
if ( $meta->{'is_numeric'} || $meta->{'type'} =~ /INT|NUMERIC|DECIMAL|REAL|DOUBLE|FLOAT/i ) { |
1136
|
1
|
|
|
|
|
7
|
push @bind, 0; |
1137
|
|
|
|
|
|
|
} else { |
1138
|
1
|
|
|
|
|
7
|
push @bind, ''; |
1139
|
|
|
|
|
|
|
} |
1140
|
|
|
|
|
|
|
} |
1141
|
|
|
|
|
|
|
} |
1142
|
|
|
|
|
|
|
|
1143
|
58
|
|
|
|
|
265
|
my $QueryString = "SELECT * FROM ".$self->QuotedTableName." WHERE ". |
1144
|
|
|
|
|
|
|
join(' AND ', @phrases) ; |
1145
|
58
|
|
|
|
|
256
|
return ($self->_LoadFromSQL($QueryString, @bind)); |
1146
|
|
|
|
|
|
|
} |
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
|
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
=head2 LoadById |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
Loads a record by its primary key. Your record class must define a single primary key column. |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
=cut |
1156
|
|
|
|
|
|
|
|
1157
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
sub LoadById { |
1159
|
54
|
|
|
54
|
1
|
187
|
my ($self, $id) = @_; |
1160
|
54
|
100
|
|
|
|
241
|
return $self->LoadByCols( $self->_PrimaryKey, defined $id? $id: 0 ); |
1161
|
|
|
|
|
|
|
} |
1162
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
=head2 LoadByPrimaryKeys |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
Like LoadById with basic support for compound primary keys. |
1169
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
=cut |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
sub LoadByPrimaryKeys { |
1175
|
3
|
|
|
3
|
1
|
19
|
my $self = shift; |
1176
|
3
|
100
|
|
|
|
14
|
my $data = (ref $_[0] eq 'HASH')? $_[0]: {@_}; |
1177
|
|
|
|
|
|
|
|
1178
|
3
|
|
|
|
|
6
|
my %cols=(); |
1179
|
3
|
|
|
|
|
6
|
foreach (@{$self->_PrimaryKeys}) { |
|
3
|
|
|
|
|
6
|
|
1180
|
3
|
100
|
|
|
|
15
|
return (0, "Missing PK field: '$_'") unless defined $data->{$_}; |
1181
|
2
|
|
|
|
|
6
|
$cols{$_}=$data->{$_}; |
1182
|
|
|
|
|
|
|
} |
1183
|
2
|
|
|
|
|
8
|
return ($self->LoadByCols(%cols)); |
1184
|
|
|
|
|
|
|
} |
1185
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
|
1189
|
|
|
|
|
|
|
=head2 LoadFromHash |
1190
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
Takes a hashref, such as created by DBIx::SearchBuilder and populates this record's |
1192
|
|
|
|
|
|
|
loaded values hash. |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
=cut |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
sub LoadFromHash { |
1199
|
1434
|
|
|
1434
|
1
|
2059
|
my $self = shift; |
1200
|
1434
|
|
|
|
|
1926
|
my $hashref = shift; |
1201
|
|
|
|
|
|
|
|
1202
|
1434
|
|
|
|
|
4416
|
foreach my $f ( keys %$hashref ) { |
1203
|
3550
|
|
|
|
|
7919
|
$self->{'fetched'}{lc $f} = 1; |
1204
|
|
|
|
|
|
|
} |
1205
|
|
|
|
|
|
|
|
1206
|
1434
|
|
|
|
|
2696
|
$self->{'values'} = $hashref; |
1207
|
1434
|
|
|
|
|
2688
|
return $self->id(); |
1208
|
|
|
|
|
|
|
} |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
=head2 _LoadFromSQL QUERYSTRING @BIND_VALUES |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
Load a record as the result of an SQL statement |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
=cut |
1217
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
sub _LoadFromSQL { |
1222
|
62
|
|
|
62
|
|
155
|
my $self = shift; |
1223
|
62
|
|
|
|
|
128
|
my $QueryString = shift; |
1224
|
62
|
|
|
|
|
181
|
my @bind_values = (@_); |
1225
|
|
|
|
|
|
|
|
1226
|
62
|
|
|
|
|
189
|
my $sth = $self->_Handle->SimpleQuery( $QueryString, @bind_values ); |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
#TODO this only gets the first row. we should check if there are more. |
1229
|
|
|
|
|
|
|
|
1230
|
62
|
100
|
|
|
|
260
|
return ( 0, "Couldn't execute query: ".$self->_Handle->dbh->errstr ) unless $sth; |
1231
|
|
|
|
|
|
|
|
1232
|
61
|
|
|
|
|
3006
|
$self->{'values'} = $sth->fetchrow_hashref; |
1233
|
61
|
|
|
|
|
476
|
$self->{'fetched'} = {}; |
1234
|
61
|
50
|
66
|
|
|
360
|
if ( !$self->{'values'} && $sth->err ) { |
1235
|
0
|
|
|
|
|
0
|
return ( 0, "Couldn't fetch row: ". $sth->err ); |
1236
|
|
|
|
|
|
|
} |
1237
|
|
|
|
|
|
|
|
1238
|
61
|
100
|
|
|
|
233
|
unless ( $self->{'values'} ) { |
1239
|
3
|
|
|
|
|
52
|
return ( 0, "Couldn't find row" ); |
1240
|
|
|
|
|
|
|
} |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
## I guess to be consistant with the old code, make sure the primary |
1243
|
|
|
|
|
|
|
## keys exist. |
1244
|
|
|
|
|
|
|
|
1245
|
58
|
100
|
|
|
|
264
|
if( grep { not defined } $self->PrimaryKeys ) { |
|
116
|
|
|
|
|
409
|
|
1246
|
1
|
|
|
|
|
104
|
return ( 0, "Missing a primary key?" ); |
1247
|
|
|
|
|
|
|
} |
1248
|
|
|
|
|
|
|
|
1249
|
57
|
|
|
|
|
146
|
foreach my $f ( keys %{$self->{'values'}} ) { |
|
57
|
|
|
|
|
675
|
|
1250
|
143
|
|
|
|
|
430
|
$self->{'fetched'}{lc $f} = 1; |
1251
|
|
|
|
|
|
|
} |
1252
|
57
|
|
|
|
|
1252
|
return ( 1, "Found Object" ); |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
} |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
=head2 Create |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
Takes an array of key-value pairs and drops any keys that aren't known |
1263
|
|
|
|
|
|
|
as columns for this recordtype |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
=cut |
1266
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
sub Create { |
1270
|
160
|
|
|
160
|
1
|
5906
|
my $self = shift; |
1271
|
160
|
|
|
|
|
658
|
my %attribs = @_; |
1272
|
|
|
|
|
|
|
|
1273
|
160
|
|
|
|
|
328
|
my ($key); |
1274
|
160
|
|
|
|
|
685
|
foreach $key ( keys %attribs ) { |
1275
|
|
|
|
|
|
|
|
1276
|
255
|
100
|
|
|
|
1033
|
if ( $self->_Accessible( $key, 'record-write' ) ) { |
1277
|
|
|
|
|
|
|
$attribs{$key} = $attribs{$key}->id |
1278
|
3
|
100
|
|
|
|
24
|
if UNIVERSAL::isa( $attribs{$key}, |
1279
|
|
|
|
|
|
|
'DBIx::SearchBuilder::Record' ); |
1280
|
|
|
|
|
|
|
} |
1281
|
|
|
|
|
|
|
|
1282
|
255
|
100
|
|
|
|
838
|
if ( defined $attribs{$key} ) { |
1283
|
242
|
50
|
33
|
|
|
834
|
if ( $attribs{$key} eq '' && |
|
|
|
66
|
|
|
|
|
1284
|
|
|
|
|
|
|
( $self->_Accessible( $key, 'is_numeric' ) |
1285
|
|
|
|
|
|
|
|| ($self->_Accessible( $key, 'type' ) || '') =~ /INT/i ) ) |
1286
|
|
|
|
|
|
|
{ |
1287
|
1
|
|
|
|
|
4
|
$attribs{$key} = 0; |
1288
|
|
|
|
|
|
|
} |
1289
|
|
|
|
|
|
|
} |
1290
|
|
|
|
|
|
|
else { |
1291
|
13
|
100
|
|
|
|
52
|
$attribs{$key} = $self->_Accessible( $key, 'default' ) |
1292
|
|
|
|
|
|
|
if $self->_Accessible( $key, 'no_nulls' ); |
1293
|
|
|
|
|
|
|
} |
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
#Truncate things that are too long for their datatypes |
1296
|
255
|
|
|
|
|
974
|
$attribs{$key} = $self->TruncateValue( $key => $attribs{$key} ); |
1297
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
} |
1299
|
160
|
50
|
|
|
|
676
|
unless ( $self->_Handle->KnowsBLOBs ) { |
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
# Support for databases which don't deal with LOBs automatically |
1302
|
0
|
|
|
|
|
0
|
my $ca = $self->_ClassAccessible(); |
1303
|
0
|
|
|
|
|
0
|
foreach $key ( keys %attribs ) { |
1304
|
0
|
|
|
|
|
0
|
my $type = $ca->{$key}->{'type'}; |
1305
|
0
|
0
|
0
|
|
|
0
|
next unless $type && $type =~ /^(text|longtext|clob|blob|lob)$/i; |
1306
|
|
|
|
|
|
|
|
1307
|
0
|
|
|
|
|
0
|
my $bhash = $self->_Handle->BLOBParams( $key, $type ); |
1308
|
0
|
|
|
|
|
0
|
$bhash->{'value'} = $attribs{$key}; |
1309
|
0
|
|
|
|
|
0
|
$attribs{$key} = $bhash; |
1310
|
|
|
|
|
|
|
} |
1311
|
|
|
|
|
|
|
} |
1312
|
160
|
|
|
|
|
531
|
return ( $self->_Handle->Insert( $self->Table, %attribs ) ); |
1313
|
|
|
|
|
|
|
} |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
=head2 Delete |
1317
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
Delete this record from the database. On failure return a Class::ReturnValue with the error. On success, return 1; |
1319
|
|
|
|
|
|
|
|
1320
|
|
|
|
|
|
|
=cut |
1321
|
|
|
|
|
|
|
|
1322
|
|
|
|
|
|
|
*delete = \&Delete; |
1323
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
sub Delete { |
1325
|
2
|
|
|
2
|
1
|
403
|
$_[0]->__Delete; |
1326
|
|
|
|
|
|
|
} |
1327
|
|
|
|
|
|
|
|
1328
|
|
|
|
|
|
|
sub __Delete { |
1329
|
2
|
|
|
2
|
|
10
|
my $self = shift; |
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
#TODO Check to make sure the key's not already listed. |
1332
|
|
|
|
|
|
|
#TODO Update internal data structure |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
## Constructs the where clause. |
1335
|
2
|
|
|
|
|
7
|
my @bind=(); |
1336
|
2
|
|
|
|
|
11
|
my %pkeys=$self->PrimaryKeys(); |
1337
|
2
|
|
|
|
|
13
|
my $where = 'WHERE '; |
1338
|
2
|
|
|
|
|
15
|
foreach my $key (sort keys %pkeys) { |
1339
|
2
|
|
|
|
|
12
|
$where .= $key . "=?" . " AND "; |
1340
|
2
|
|
|
|
|
9
|
push (@bind, $pkeys{$key}); |
1341
|
|
|
|
|
|
|
} |
1342
|
|
|
|
|
|
|
|
1343
|
2
|
|
|
|
|
20
|
$where =~ s/AND\s$//; |
1344
|
2
|
|
|
|
|
15
|
my $QueryString = "DELETE FROM ". $self->QuotedTableName . ' ' . $where; |
1345
|
2
|
|
|
|
|
12
|
my $return = $self->_Handle->SimpleQuery($QueryString, @bind); |
1346
|
|
|
|
|
|
|
|
1347
|
2
|
50
|
|
|
|
37
|
if (UNIVERSAL::isa($return, 'Class::ReturnValue')) { |
1348
|
0
|
|
|
|
|
0
|
return ($return); |
1349
|
|
|
|
|
|
|
} else { |
1350
|
2
|
|
|
|
|
66
|
return(1); |
1351
|
|
|
|
|
|
|
} |
1352
|
|
|
|
|
|
|
} |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
|
1355
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
=head2 Table |
1359
|
|
|
|
|
|
|
|
1360
|
|
|
|
|
|
|
Returns or sets the name of the current Table |
1361
|
|
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
=cut |
1363
|
|
|
|
|
|
|
|
1364
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
sub Table { |
1367
|
1818
|
|
|
1818
|
1
|
7986
|
my $self = shift; |
1368
|
1818
|
100
|
|
|
|
3932
|
if (@_) { |
1369
|
1596
|
|
|
|
|
3789
|
$self->{'table'} = shift; |
1370
|
|
|
|
|
|
|
} |
1371
|
1818
|
|
|
|
|
4148
|
return ($self->{'table'}); |
1372
|
|
|
|
|
|
|
} |
1373
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
=head2 QuotedTableName |
1375
|
|
|
|
|
|
|
|
1376
|
|
|
|
|
|
|
Returns the name of current Table, or the table provided as an argument, including any quoting |
1377
|
|
|
|
|
|
|
based on yje Handle's QuoteTableNames flag and driver method. |
1378
|
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
=cut |
1380
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
sub QuotedTableName { |
1382
|
63
|
|
|
63
|
1
|
185
|
my ($self, $name) = @_; |
1383
|
63
|
50
|
|
|
|
201
|
unless ($name) { |
1384
|
63
|
100
|
|
|
|
233
|
return $self->{'_quoted_table'} if defined $self->{'_quoted_table'}; |
1385
|
58
|
50
|
|
|
|
174
|
$self->{'_quoted_table'} |
1386
|
|
|
|
|
|
|
= $self->_Handle->QuoteTableNames ? $self->_Handle->QuoteName( $self->Table ) : $self->Table; |
1387
|
58
|
|
|
|
|
380
|
return $self->{'_quoted_table'}; |
1388
|
|
|
|
|
|
|
} |
1389
|
0
|
0
|
|
|
|
0
|
return $self->_Handle->QuoteTableNames ? $self->_Handle->QuoteName($name) : $name; |
1390
|
|
|
|
|
|
|
} |
1391
|
|
|
|
|
|
|
|
1392
|
|
|
|
|
|
|
=head2 _Handle |
1393
|
|
|
|
|
|
|
|
1394
|
|
|
|
|
|
|
Returns or sets the current DBIx::SearchBuilder::Handle object |
1395
|
|
|
|
|
|
|
|
1396
|
|
|
|
|
|
|
=cut |
1397
|
|
|
|
|
|
|
|
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
sub _Handle { |
1400
|
2119
|
|
|
2119
|
|
5979
|
my $self = shift; |
1401
|
2119
|
100
|
|
|
|
4532
|
if (@_) { |
1402
|
1630
|
|
|
|
|
2836
|
$self->{'DBIxHandle'} = shift; |
1403
|
|
|
|
|
|
|
} |
1404
|
2119
|
|
|
|
|
4708
|
return ($self->{'DBIxHandle'}); |
1405
|
|
|
|
|
|
|
} |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
|
1408
|
|
|
|
|
|
|
if( eval { require capitalization } ) { |
1409
|
|
|
|
|
|
|
capitalization->unimport( __PACKAGE__ ); |
1410
|
|
|
|
|
|
|
} |
1411
|
|
|
|
|
|
|
|
1412
|
|
|
|
|
|
|
1; |