line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# |
2
|
|
|
|
|
|
|
# DB::Ent - A Database Entity Layer |
3
|
|
|
|
|
|
|
# Copyright (C) 2001-2003 Erick Calder |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# This program is free software; you can redistribute it and/or modify |
6
|
|
|
|
|
|
|
# it under the terms of the GNU General Public License as published by |
7
|
|
|
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or |
8
|
|
|
|
|
|
|
# (at your option) any later version. |
9
|
|
|
|
|
|
|
# |
10
|
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful, |
11
|
|
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
12
|
|
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13
|
|
|
|
|
|
|
# GNU General Public License for more details. |
14
|
|
|
|
|
|
|
# |
15
|
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License |
16
|
|
|
|
|
|
|
# along with this program; if not, write to the Free Software |
17
|
|
|
|
|
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18
|
|
|
|
|
|
|
# |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 NAME |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
DB::Ent - Database Entity Layer |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 SYNOPSIS |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
use DB::Ent; |
27
|
|
|
|
|
|
|
$ef = DB::Ent->new(); # entity factory |
28
|
|
|
|
|
|
|
$au = $ef->mk(artist => "Peter Gabriel"); # create an artist |
29
|
|
|
|
|
|
|
$cd = $au->mksub(CD => "Passion"); # create subordinate entity |
30
|
|
|
|
|
|
|
$cd->attr(id => "0x0440F020") || die; # set attributes |
31
|
|
|
|
|
|
|
$cv = $dbe->mk(cover => "http://..."); # create a cover |
32
|
|
|
|
|
|
|
$cd->rel($cv); # link to CD |
33
|
|
|
|
|
|
|
$cd->rm(); # remove the CD |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 DESCRIPTION |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
This module presents an interface to an entity-centric database schema, providing all necessary methods to create, discover and manipulate entities and associated data. |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
The schema consists of 4 basic element storage types: 1) entities, 2) attributes, 3) relationships, and 4) extended attributes. |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
The terms C and C are used here in accordance to the common definition used in relational database theory. |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
A differentiating factor between an entity and an attribute is that attributes serve no other purpose but to qualify an entity and cannot exist on their own. Entities may exist without qualifiers, requiring only a name. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
Relationships of various kinds may be established between any two entities and these may be codified, enumerated or both. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Extended attributes comprise special datatypes and are typically used to store large format data. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=cut |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# --- prologue ---------------------------------------------------------------- |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
package DB::Ent; |
54
|
|
|
|
|
|
|
|
55
|
1
|
|
|
1
|
|
26464
|
use 5.006; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
38
|
|
56
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
33
|
|
57
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
40
|
|
58
|
|
|
|
|
|
|
|
59
|
1
|
|
|
1
|
|
5
|
use Exporter; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
57
|
|
60
|
1
|
|
|
1
|
|
5
|
use vars qw/@ISA/; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
5138
|
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
our $VERSION = substr q$Revision: 1.63 $, 10; |
63
|
0
|
|
|
0
|
0
|
0
|
sub OK { 1; } |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
### TODO ### |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# new(): allow empty values for attributes |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# --- exported module interface ----------------------------------------------- |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=head1 SYNTAX |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
Methods are listed by declaring their signatures, the general format of which consists of a return value followed by an equal sign and the method name, followed by a list of parameters, e.g. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
IreturnE = method-name ErequiredE [optional = default] {alternate1 | alternate2}> |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head2 Parameters |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Required parameters are named within angles whilst optional parameters within brackets; alternative parameters use braces separated by pipes. Whenever optional parameters are specified, the default value may be represented by following the parameter name with an equal sign and the default value. When listing alternative parameters the syntax may nest brackets, e.g. the line below names that EITHER two required parameters may be passed OR a hash or hash reference. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
I<{Eval-1E Eval-2E | hash[-ref]}> |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Please note that failing to pass required arguments in a method call results in program death. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=head2 Return values |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
In general, methods return objects unless an error occurs in which case I is returned. Certain methods specify their return value as I which indicates a success flag (set to I on failure). Upon encountering an error, the caller should consult the {err} and {errstr} fields in the object which contain a numeric error code and its corresponding description string. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
Return values are typically context-sensitive and may also be sensitive to the argument signature. When different return values may be expected, these appear separated with pipes. When the return value is an IerrE>, if the context is scalar, only error code is returned. |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head2 Signature templates |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
Whenever the syntax for a method is indicated with a double colon, it specifies that the signature for the current method follows that of a template method which is indicated following the double colon (e.g. I). Parameters to the method in question are placed after the template's last required parameter and before its first optional parameter. |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=cut |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# --- entity factory methods -------------------------------------------------- |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=head1 METHODS |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
The module presents an object-oriented interface divided into two major functional groups: entity-factory methods, and entity-management methods. |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Entity factory methods concern the binding of perl objects to datastore items. This includes insertion, discovery and retrieval methods as well as entity-factory configuration methods. This category includes the following: |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head2 = new [hash[-ref]] |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
Before entities can be created and managed, an entity factory must be instantiated. This method returns such an object and receives two kinds of parameters: connection parameters, and configuration parameters. If any error is encountered the method returns I for the object, followed by an error list. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Parameters are all passed as a hash or hash reference whose keys are described below: |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
I |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
To establish a connection to a datastore, the caller must pass credentials. These may be passed either in URL syntax and/or as separate keys. Any information passed separately overrides the appropriate value in the URL. If no connection information is passed, the variable I in the main namespace is assumed to contain a URL with the information. If this variable is empty, the environment variable of the same name is used. Any credential pieces not passed assume defaults. |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=over |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=item DBED |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Indicates the low-level driver to use. At present this value defaults to and can only be set to C. As other low level drivers are written, their names may be passed here. |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=item URL |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Specifies a connection URL of the form: I where the items indicated in brackets are optional and which may be passed separately as described below. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=item proto |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
This key specifies the drivers to use for connecting to a datastore. The value will be passed through to the I and if not supplied, the DBED will select an appropriate default e.g. C. |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=item host |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
This indicates the name or IP address of the server hosting the datastore. If not provided, the low level driver will choose a default. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=item user |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
Specifies the account to use for logging into the datastore. If not provided the low-level driver will choose a default. |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=item password |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
Necessary when security measures have been placed on the account in use. Passwords are provided in plain-text. If not provided, default is left to the low-level driver. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item database |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
This key specifies the namespace within the server to use. If not specified, a default home will be used. Please note however that not all database systems either have a namespace concept, nor a default value for it. |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=back |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
I |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
The following keys define various behaviours for the entity factory. The values are stored as keys in the object itself and may be manipulated directly. |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=over |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=item mkuid |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Specifies a code reference to be used in generating unique ids e.g. I<\MyModule::nextid>. If no value is specified, unique strings are computed based on the md5 value of the canonicalised name. |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=item dups |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
The duplicates flag indicates the action to take when db insert fails because an entity already exists. The value may be set to any of the following constants B, B, B (see section B at the end of this document). |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=item upsert |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
This key allows the user to automatically overwrite existing entity attributes. For more information please see the the B method. |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=item debug |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
Setting this key allows for debugging output to be produced by the module. A 1 is the minimum required with increased verbosity resulting from larger values. By default no debugging output is generated. |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=item trace |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
Setting this key to a filename will cause all commands issued to the datastore to be recorded in the file. |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=back |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
I |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
The following methods may be overridden by the caller as desired: |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=over |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=item dbcmd |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
This method is called with the full command about to be sent to the datastore. By default this method does nothing; |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=item dberr |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
This key allows the caller to specify an error handler to use when low-level driver problems arise. The constants B (current default) and B may also be used to request that the default error handler die or merely warn upon errors; |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=back |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=cut |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sub new { |
194
|
1
|
|
|
1
|
1
|
495
|
my $proto = shift; |
195
|
1
|
|
33
|
|
|
10
|
my $class = ref($proto) || $proto; |
196
|
1
|
|
|
|
|
5
|
my $self = bless { DBED => "DBI", &args }, $class; |
197
|
|
|
|
|
|
|
|
198
|
1
|
|
50
|
|
|
11
|
$self->{debug} ||= 0; |
199
|
|
|
|
|
|
|
|
200
|
1
|
|
|
|
|
4
|
my $DBED = "DB::Ent::" . $self->{DBED}; |
201
|
1
|
50
|
|
|
|
11
|
eval qq/require $DBED/ unless $self->{DBED} eq "DBI"; |
202
|
1
|
|
|
|
|
27
|
@ISA = ($DBED); |
203
|
|
|
|
|
|
|
|
204
|
1
|
|
|
|
|
6
|
$self->u2h(); |
205
|
1
|
|
|
|
|
12
|
$self->SUPER::new( |
206
|
|
|
|
|
|
|
debug => $self->{debug} - 1, |
207
|
|
|
|
|
|
|
trace => $self->{trace}, |
208
|
|
|
|
|
|
|
); |
209
|
|
|
|
|
|
|
|
210
|
0
|
|
|
|
|
0
|
my @err = ($self->{err}, $self->{errstr}); |
211
|
0
|
0
|
|
|
|
0
|
$self = undef if $err[0]; |
212
|
0
|
0
|
|
|
|
0
|
wantarray() ? ($self, @err) : $self; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=head2 = mk [attr-hashref] |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
Creates an entity with the given name and type and populates it with the attributes given in the optional hash reference. If the object already exists in the datastore no insertion is made and any attributes provided are discarded, unless the I flag is set in the entity factory, in which case all attributes specified are overwritten with the values supplied. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
All entities have a unique id, normally calculated upon creation by the B method (see Configuration Parameters for the B method). This default id is generated from a combination of the name and type of entity and may be overridden by specifying the I key in the attribute hash. Additionally, to generate the uid using the current uid generator but with an alternative value, the key I may be passed. |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=cut |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub mk { |
224
|
0
|
|
|
0
|
1
|
0
|
my ($self, $type, $nm, $d) = @_; |
225
|
|
|
|
|
|
|
|
226
|
0
|
0
|
|
|
|
0
|
die qq/mk(): No name passed!/ unless $nm; |
227
|
0
|
0
|
|
|
|
0
|
die qq/mk(): No type passed!/ unless $type; |
228
|
|
|
|
|
|
|
|
229
|
0
|
|
|
|
|
0
|
my ($ent, $id); |
230
|
0
|
|
0
|
|
|
0
|
$id->{uid} = $d->{uid} || $self->uid($d->{uidstr} || "$type:$nm"); |
231
|
0
|
|
|
|
|
0
|
delete $d->{uidstr}; |
232
|
|
|
|
|
|
|
|
233
|
0
|
0
|
|
|
|
0
|
unless ($ent = $self->ent($id)) { |
234
|
0
|
|
|
|
|
0
|
$ent = {nm => $nm, type => $type, uid => $id->{uid}}; |
235
|
0
|
|
0
|
|
|
0
|
$ent->{id} = $self->ins(ent => $ent) || return; |
236
|
0
|
|
|
|
|
0
|
$ent->{$_} = $self->{$_} for qw/dbh debug/; |
237
|
0
|
|
|
|
|
0
|
bless $ent, ref($self); |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
0
|
0
|
|
|
|
0
|
return $ent if $ent->attr($d) == 0; |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=head2 = ent [opts-hash[-ref]] |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
This method retrieves entities from the datastore. |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Entities are returned according to selection criteria which are specified in a hash consisting of the attribute values being sought. Values in the selection hash may contain list references when multiple matches are desired. Additionally, hash values may contain the I<%> wildcard to indicate partial matches. If no selection criteria are specified, the method will assume an I is being sought and takes the value of I<$_>. |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
A hash of options intended to modify the return set may also be passed containing keys as outlined below: |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=over |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=item sort |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
Specifies the attribute(s) to sort results by. A list reference may be passed when multiple values are desired; defaults to C. |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
=back |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
The return value consists of blessed objects, the number of which depends on the selection criteria. When a single object is found it is returned as a scalar, unless the calling context requests a list. |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
B The entities returned by this method contain only the most minimal of information i.e. that contained in the B table. No attribute, relationship or other information is retrieved but these values may be got by calling specific methods for each. For a catalogue of such methods please refer to the B section below. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=cut |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub ent { |
266
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
267
|
0
|
|
|
|
|
0
|
my $sel = shift; |
268
|
0
|
0
|
|
|
|
0
|
$sel = { $sel =~ /^\d+$/ ? "id" : "nm" => $sel } |
|
|
0
|
|
|
|
|
|
269
|
|
|
|
|
|
|
unless ref $sel; |
270
|
|
|
|
|
|
|
|
271
|
0
|
|
|
|
|
0
|
my $opts = &args; |
272
|
0
|
|
0
|
|
|
0
|
$opts->{sort} ||= "nm"; |
273
|
|
|
|
|
|
|
|
274
|
0
|
|
|
|
|
0
|
my (@ent, @ret); |
275
|
0
|
|
|
|
|
0
|
@ent = $self->sel(ent => $sel, sort => $opts->{sort}); |
276
|
0
|
|
|
|
|
0
|
push @ret, bless $_, $self for @ent; |
277
|
|
|
|
|
|
|
|
278
|
0
|
0
|
0
|
|
|
0
|
@ret == 1 && !wantarray() ? $ret[0] : @ret; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=head2 = cs |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
This method returns a url containing the connection information in use for the given datastore object. For information on the format of this string, please refer to the I argument to the I method. |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=cut |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
sub cs { |
288
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
289
|
0
|
|
|
|
|
0
|
$self->h2u(); |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# --- entity management methods ----------------------------------------------- |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
=head1 METHODS - Entity Management |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
The methods listed below provide mechanisms for managing entity objects, their attributes and relationships. These methods can only be called on the objects generated by calls to entity-factory methods. |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=head2 = rm [RELSONLY] |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
Removes an entity and all relationships to other entities. The method works recursively, destroying all dependencies, otherwise requested. For more information please refer to the documentation for the B method. |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
=cut |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
sub rm { |
305
|
0
|
|
|
0
|
1
|
0
|
my ($self, $rels) = @_; |
306
|
|
|
|
|
|
|
|
307
|
0
|
0
|
|
|
|
0
|
$self->rmattr() || return; |
308
|
0
|
0
|
|
|
|
0
|
$self->rmrel(relsonly => $rels) || return; |
309
|
0
|
0
|
|
|
|
0
|
$self->del(ent => {id => $self->id}) || return; |
310
|
|
|
|
|
|
|
|
311
|
0
|
|
|
|
|
0
|
OK; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=head2 mksub :: mk |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
Creates a sub-ordinate entity. The method must be called from an object generated by an entity factory and assumes such serves as the parent. |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=cut |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub mksub { |
321
|
0
|
|
|
0
|
1
|
0
|
my ($self, $type, $nm, $d) = @_; |
322
|
|
|
|
|
|
|
|
323
|
0
|
|
0
|
|
|
0
|
my $ent = $self->mk($type => $nm, $d) |
324
|
|
|
|
|
|
|
|| return; |
325
|
0
|
0
|
|
|
|
0
|
$ent->rel($self) |
326
|
|
|
|
|
|
|
|| return; |
327
|
|
|
|
|
|
|
|
328
|
0
|
|
|
|
|
0
|
$ent; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
# --- attribute methods ------------------------------------------------------- |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=head2 | = attr [ [value = $_] | attr-hashref] |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
Sets attributes for an entity. |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
Two parameter signatures are allowed; in the first form the method sets a single attribute; the second form allows for multiple attributes to be set. The return value in this case consists of an I value. |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
Attributes are created whenever they do not exist in the datastore but when already present, their values are respected, unless the I flag in the entity factory is set. |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
The current value of an attribute may be updated via a calculation by passing a code reference as a I. The code-ref will be called with the value of I<$_> set to the current attribute's value and will be responsible for returning the new value, e.g. to increment a value the following may be used: |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
I<$self-Eattr(count =E sub { $_++ });> |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=cut |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
sub attr { |
348
|
0
|
|
|
0
|
1
|
0
|
my ($self, $attr, $val) = @_; |
349
|
|
|
|
|
|
|
|
350
|
0
|
0
|
0
|
|
|
0
|
$val = $_ unless ref($attr) || defined($val); |
351
|
0
|
0
|
|
|
|
0
|
my %attr = ref($attr) ? %$attr : ($attr, $val); |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
# bail if no attrs to process |
354
|
|
|
|
|
|
|
|
355
|
0
|
0
|
|
|
|
0
|
return !OK unless %attr; |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
# intrinsic attributes don't get stored in the C table |
358
|
|
|
|
|
|
|
|
359
|
0
|
|
|
|
|
0
|
for ($self->tabcols("ent")) { |
360
|
0
|
0
|
|
|
|
0
|
next if /^id$/i; |
361
|
0
|
|
|
|
|
0
|
delete $attr{$_}; |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
# grab attributes in case me virgin |
365
|
|
|
|
|
|
|
|
366
|
0
|
0
|
|
|
|
0
|
$self->attrs() unless $self->{__cf}{attrs}; |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# insert attributes |
369
|
|
|
|
|
|
|
|
370
|
0
|
|
|
|
|
0
|
for (keys %attr) { |
371
|
0
|
|
0
|
|
|
0
|
my $v = $attr{$_} || next; |
372
|
0
|
|
0
|
|
|
0
|
$self->{$_} ||= ""; |
373
|
0
|
0
|
|
|
|
0
|
next if $self->{$_} eq $v; |
374
|
|
|
|
|
|
|
|
375
|
0
|
|
|
|
|
0
|
my ($nm, $dt) = nmdt(); # attribute names may include datatype |
376
|
0
|
0
|
|
|
|
0
|
next unless $nm; # attributes should have names |
377
|
0
|
|
0
|
|
|
0
|
$dt ||= strdt($v); # guess datatype if not specified |
378
|
|
|
|
|
|
|
|
379
|
0
|
0
|
|
|
|
0
|
my $fn = $self->{$_} ? "upd" : "ins"; |
380
|
0
|
|
|
|
|
0
|
my $ret = $self->$fn( |
381
|
|
|
|
|
|
|
attr => { nm => $nm, $dt => $v }, |
382
|
|
|
|
|
|
|
{ id => $self->id } |
383
|
|
|
|
|
|
|
); |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# inserts into C do not auto-generate |
386
|
|
|
|
|
|
|
# an id so the return value of ins() is not |
387
|
|
|
|
|
|
|
# meaninful and sqlerr() must be checked |
388
|
|
|
|
|
|
|
|
389
|
0
|
0
|
|
|
|
0
|
$ret = $self->dberr() if $fn eq "ins"; |
390
|
0
|
0
|
|
|
|
0
|
return !OK unless $ret; |
391
|
|
|
|
|
|
|
|
392
|
0
|
|
|
|
|
0
|
$self->{$nm} = $v; |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
|
395
|
0
|
|
|
|
|
0
|
OK; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
=head2 = attrs [attr-listref] [DTT] |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
Retrieves the attributes of an identity. Specific attributes may be requested by passing their names in a list reference. If the constant B is passed, datatype information will be embedded in the keys of the return hash. |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
The attributes retrieved (and returned) are also embedded internally into the object. |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
=cut |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
sub attrs { |
407
|
0
|
|
|
0
|
1
|
0
|
my ($self, $nm, $dtt) = @_; |
408
|
|
|
|
|
|
|
|
409
|
0
|
|
|
|
|
0
|
my %attr; |
410
|
0
|
|
|
|
|
0
|
for ($self->sel(attr => {id => $self->id, nm => $nm})) { |
411
|
0
|
|
|
|
|
0
|
my $nm = $_->{nm}; |
412
|
0
|
|
|
|
|
0
|
for my $dt (qw/s i f d/) { |
413
|
0
|
|
0
|
|
|
0
|
my $v = $_->{$dt} || next; |
414
|
0
|
0
|
|
|
|
0
|
$attr{$dtt ? "$dt:$nm" : $nm} = $v; |
415
|
0
|
|
|
|
|
0
|
last; |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
|
419
|
0
|
|
|
|
|
0
|
$self->{__cf}{attrs} = 1; # already queried |
420
|
0
|
|
|
|
|
0
|
%$self = (%$self, %attr); |
421
|
0
|
0
|
|
|
|
0
|
$nm ? $attr{$nm} : wantarray() ? %attr : \%attr; |
|
|
0
|
|
|
|
|
|
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=head2 = rmattr [attr-list] |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
Removes an entity's attributes. If a list of attribute names is provided, only those attributes are removed. |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=cut |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
sub rmattr { |
431
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
432
|
0
|
|
|
|
|
0
|
my $crit = { id => $self->id() }; |
433
|
0
|
0
|
|
|
|
0
|
$crit->{nm} = \@_ if @_; |
434
|
0
|
|
|
|
|
0
|
$self->del(attr => $crit); |
435
|
|
|
|
|
|
|
} |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
# --- relationship methods ---------------------------------------------------- |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=head2 = rel [options-hashref] |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
Creates a relationship between the current entity and a list of given entities. Relationshps are always enumerated but may also be codified, as indicated in the options hash optionally passed. Valid keys for this hash are as follows: |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=over |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=item type |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
Specifies the type of relationship. If no type is specified, the relationship is not considered to be codified. |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=item nn |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
Passing a value with this key cements the relationship number between two entities. Generally there is no good reason to want this, and as attempting to establish a relationship between to entities with the same numeric value (within a given relationship-type (code)) will cause an error, abstention from use of this key is recommended. If not specified, its value is calculated as the next number available. |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=item unique |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
If this key is set to true, the system will enforces a single relationship between the two entities (regardless of relationship type). By default this value is false. |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=item parent |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
Most commonly the caller will intend to create child relationships between the current entity and the passed list of subordinate entities. At times however, it may be necessary to reverse the sense of this assignment, making each of the entities in the list, the parent of the current entity. Setting this flag to true allows for that to happen. |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=back |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=cut |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
sub rel { |
466
|
0
|
|
|
0
|
1
|
0
|
my ($self, $rel, $opts) = @_; |
467
|
|
|
|
|
|
|
|
468
|
0
|
0
|
|
|
|
0
|
my @rel = ref($rel) eq "ARRAY" ? @$rel : ($rel); |
469
|
0
|
|
|
|
|
0
|
for (@rel) { |
470
|
0
|
|
0
|
|
|
0
|
my $i = $opts->{nn} || 1 + $self->max(rel => "i", { |
471
|
|
|
|
|
|
|
id => $self->id, pid => $_->id, type => $opts->{type} |
472
|
|
|
|
|
|
|
}); |
473
|
|
|
|
|
|
|
|
474
|
0
|
0
|
0
|
|
|
0
|
next if $opts->{unique} && !$opts->{nn} && $i > 1; |
|
|
|
0
|
|
|
|
|
475
|
|
|
|
|
|
|
|
476
|
0
|
0
|
|
|
|
0
|
my ($id, $pid) = $opts->{parent} ? qw/pid id/ : qw/id pid/; |
477
|
0
|
|
|
|
|
0
|
$self->ins(rel => { |
478
|
|
|
|
|
|
|
$id => $self->id, $pid => $_->id, type => $opts->{type}, i => $i |
479
|
|
|
|
|
|
|
}); |
480
|
0
|
0
|
|
|
|
0
|
return if $self->{err}; |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
|
483
|
0
|
|
|
|
|
0
|
OK; |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
=head2 = rels [opts-hash[-ref]] |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
Returns a list of entities related to the current entity. Please note that these values get cached inside the object as either I<_parents_> or I<_children_>. |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
The options hash specifies behaviour as follows: |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
=over |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
=item cd |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
The caller may limit the entities returned by relationship code. The value passed may be either a scalar or a list reference. |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
=item parent |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
If this flag is set, instead of returning an entity's children, the parents are returned. |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
=back |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
=cut |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
sub rels { |
508
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
509
|
0
|
|
|
|
|
0
|
my $opts = &args; |
510
|
|
|
|
|
|
|
|
511
|
0
|
0
|
|
|
|
0
|
my @ret; my $id = $opts->{parent} ? "id" : "pid"; |
|
0
|
|
|
|
|
0
|
|
512
|
0
|
|
|
|
|
0
|
for ($self->sel(rel => {$id => $self->id, cd => $opts->{cd}})) { |
513
|
0
|
|
|
|
|
0
|
push @ret, $self->ent($_->{id}); |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
0
|
0
|
|
|
|
0
|
$self->{$opts->{parent} ? "_parents_" : "_children_"} = \@ret; |
517
|
0
|
0
|
|
|
|
0
|
wantarray ? @ret : \@ret; |
518
|
|
|
|
|
|
|
} |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=head2 = rmrel [ents => listref, relsonly => 1] |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
Removes an entity's relationships with the given list of entities by removing these from the datastore. Please note that this process works recursively, removing children's children to any level, thus effectively pruning the relationship tree connected at the current entity. If the list passed is empty, all children are removed. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
If the flag I is set, the method only severs the entity's relationships with other entities without destroying these. |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=cut |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
sub rmrel { |
529
|
0
|
|
|
0
|
1
|
0
|
my ($self, %args) = @_; |
530
|
|
|
|
|
|
|
|
531
|
0
|
0
|
|
|
|
0
|
unless ($args{relsonly}) { |
532
|
0
|
|
0
|
|
|
0
|
my @rels = $args{ents} || $self->rels(); |
533
|
0
|
|
0
|
|
|
0
|
$_->rm() || last for @rels; |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
|
536
|
0
|
0
|
|
|
|
0
|
$self->del(rel => {id => $self->id}) || return; |
537
|
0
|
0
|
|
|
|
0
|
$self->del(rel => {pid => $self->id}) || return; |
538
|
|
|
|
|
|
|
|
539
|
0
|
|
|
|
|
0
|
OK; |
540
|
|
|
|
|
|
|
} |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=head2 = args [hash, hash-ref, ...] |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
This function conveniently parses arguments passed to a method. It should be called in non OO style without arguments and returns a hash or hash reference (depending on context) with the values. |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
I<- exempli gratia -> |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
sub tst { |
549
|
|
|
|
|
|
|
my $args = &DB::Ent::args; |
550
|
|
|
|
|
|
|
print $args->{key}; |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=cut |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
sub args { |
556
|
2
|
|
|
2
|
1
|
10
|
my @ret = @_; |
557
|
2
|
|
|
|
|
6
|
for my $i (0 .. $#ret) { |
558
|
8
|
50
|
|
|
|
24
|
splice @ret, $i, 1, %{ $ret[$i] } if ref $ret[$i] eq "HASH"; |
|
0
|
|
|
|
|
0
|
|
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
2
|
50
|
|
|
|
21
|
wantarray() ? @ret : { @ret }; |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
# --- internal methods and functions ------------------------------------------ |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
sub id { |
567
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
568
|
0
|
|
|
|
|
0
|
$self->{id}; |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
# converts a connection url to a hash |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
sub u2h { |
574
|
1
|
|
|
1
|
0
|
3
|
my $self = shift; |
575
|
1
|
|
50
|
|
|
21
|
local $_ = $self->{URL} || $::DBE || $ENV{DBE} || ""; |
576
|
|
|
|
|
|
|
|
577
|
1
|
|
|
|
|
5
|
my @dbk = qw/proto usr pwd srv dbn/; |
578
|
1
|
|
|
|
|
2
|
my %url; @url{@dbk} = m|^(\w+)://(\w+):?(\w*)@?(\w+)/?(\w+)$|; |
|
1
|
|
|
|
|
7
|
|
579
|
1
|
|
66
|
|
|
32
|
$self->{$_} ||= $url{$_} for @dbk; |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
# converts a connection hash to a url |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
sub h2u { |
585
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
586
|
0
|
|
|
|
|
0
|
local $_ = sprintf("%s://%s:%s@%s/%s", |
587
|
|
|
|
|
|
|
$self->{proto}, |
588
|
|
|
|
|
|
|
$self->{usr}, $self->{pwd}, |
589
|
|
|
|
|
|
|
$self->{srv}, $self->{dbn} |
590
|
|
|
|
|
|
|
); |
591
|
0
|
|
|
|
|
0
|
s/:@/@/; |
592
|
0
|
|
|
|
|
0
|
$_; |
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
# generate unique id strings. |
596
|
|
|
|
|
|
|
# if a function has been defined for this purpose no string may |
597
|
|
|
|
|
|
|
# be necessary but if the uid is being generated by us, we need a |
598
|
|
|
|
|
|
|
# string (typically the name) |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
sub uid { |
601
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
602
|
0
|
|
|
|
|
0
|
my $nm = shift; |
603
|
|
|
|
|
|
|
|
604
|
0
|
0
|
|
|
|
0
|
return $self->{mkuid}($nm) if $self->{mkuid}; |
605
|
|
|
|
|
|
|
|
606
|
0
|
0
|
|
|
|
0
|
return unless $nm; |
607
|
0
|
|
|
|
|
0
|
require String::Canonical; |
608
|
0
|
|
|
|
|
0
|
import String::Canonical qw/cstr/; |
609
|
0
|
|
|
|
|
0
|
require Digest::MD5; |
610
|
0
|
|
|
|
|
0
|
import Digest::MD5 qw/md5_hex/; |
611
|
|
|
|
|
|
|
|
612
|
0
|
|
|
|
|
0
|
md5_hex(cstr($nm)); |
613
|
|
|
|
|
|
|
} |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
# |
616
|
|
|
|
|
|
|
# Syntax: |
617
|
|
|
|
|
|
|
# = strdt [string = $_] |
618
|
|
|
|
|
|
|
# Synopsis: |
619
|
|
|
|
|
|
|
# Returns the heuristic datatype of a string |
620
|
|
|
|
|
|
|
# |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
sub strdt { |
623
|
0
|
0
|
|
0
|
0
|
0
|
local $_ = @_ > 0 ? shift : $_; |
624
|
0
|
0
|
|
|
|
0
|
return "i" if /^-?\d+$/; |
625
|
0
|
0
|
|
|
|
0
|
return "f" if /^-?\d+\.?\d*$/; |
626
|
0
|
0
|
|
|
|
0
|
return "d" if m|^\d{1,2}/\d{1,2}/(\d\d){1,2}|; |
627
|
0
|
|
|
|
|
0
|
return "s"; |
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
# |
631
|
|
|
|
|
|
|
# Syntax: |
632
|
|
|
|
|
|
|
# = nmdt [attr-nm = $_] |
633
|
|
|
|
|
|
|
# Synopsis: |
634
|
|
|
|
|
|
|
# Splits a compound attribute name into its value |
635
|
|
|
|
|
|
|
# and it's datatype |
636
|
|
|
|
|
|
|
# |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
sub nmdt { |
639
|
0
|
|
0
|
0
|
0
|
0
|
local $_ = shift || $_; |
640
|
0
|
|
|
|
|
0
|
my ($dt, $nm) = /^(?:(.):)?(.*)$/; # datatype may be embedded |
641
|
0
|
|
0
|
|
|
0
|
return ($nm, $dt || ""); |
642
|
|
|
|
|
|
|
} |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
=head1 CONSTANTS |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
A number of constants are used by the various methods; these are typically access directly from the package e.g. B<$DB::Ent::DTT>. A description of each follows: |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
=over |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
=item DUPSQUIET |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
specifies that entity creation failures owning to duplicate keys should be silently ignored. |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
=item DUPSWARN |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
specifies duplicate key violations should issue warnings. |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
=item DUPSDIE |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
specifies duplicate key violations should cause the process to die. |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=item ERRWARN |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
specifies that only warnings should be issued when encountering errors. |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=item ERRDIE |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
specifies that the process should die when errors are found. |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
=item RELSONLY |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
#FIXME |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
=item DTT |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
#FIXME |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
=back |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
=cut |
681
|
|
|
|
|
|
|
|
682
|
0
|
|
|
0
|
1
|
0
|
sub DUPSQUIET { 1; } |
683
|
0
|
|
|
0
|
1
|
0
|
sub DUPSWARN { 2; } |
684
|
0
|
|
|
0
|
1
|
0
|
sub DUPSDIE { 3; } |
685
|
0
|
|
|
0
|
1
|
0
|
sub ERRWARN { 0; } |
686
|
0
|
|
|
0
|
1
|
0
|
sub ERRDIE { 1; } |
687
|
0
|
|
|
0
|
1
|
0
|
sub RELSONLY { 1; } |
688
|
0
|
|
|
0
|
1
|
0
|
sub DTT { 1; } |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
=head1 DRIVERS |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
Drivers are modules that provide low-level primitives to access specific datastores. Please note that the I/I nomenclature may not map directly to a I/I, I/I, I/I, I/I or other metaphor supported by the underlying datastore.
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
At present only a DBI driver exists but a published API (see man page for DB::Ent::DBI) exists to allow developers to write other drivers. |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=head1 AUTHOR |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
Erick Calder |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
=head1 SUPPORT |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
For help and thank you notes, e-mail the author directly. To report a bug, submit a patch or add to our wishlist please visit the CPAN bug manager at: F |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=head1 AVAILABILITY |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
The latest version of the tarball, RPM and SRPM may always be found at: F Additionally the module is available from CPAN. |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
=head1 LICENCE AND COPYRIGHT |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
This utility is free and distributed under GPL, the Gnu Public License. A copy of this license was included in a file called LICENSE. If for some reason, this file was not included, please see F to obtain a copy of this license. |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
=head1 SEE ALSO |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
L |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
$Revision: 1.63 $, $Date: 2003/06/24 03:58:11 $ |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
=cut |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
# --- DBI Driver -------------------------------------------------------------- |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=head1 NAME |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
DB::Ent::DBI - DBI Driver for DB::Ent |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=head1 SYNOPSIS |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
use DB::Ent::DBI; |
729
|
|
|
|
|
|
|
$dbx = DB::Ent::DBI->new(); |
730
|
|
|
|
|
|
|
$dbx->ins(); |
731
|
|
|
|
|
|
|
$dbx->del(); |
732
|
|
|
|
|
|
|
$dbx->upd(); |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=head1 DESCRIPTION |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
This module provides a DBI-based driver for the DB::Ent schema abstraction layer and serves as a guideline for other driver development efforts by documenting the API. |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
Please note that for this driver the choice of nomenclature consists of I. Also, some methods return IerrE>; this is a list consisting of a numeric error code, followed by its human-legible corresponding string.
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
=cut |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
# --- prologue ---------------------------------------------------------------- |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
package DB::Ent::DBI; |
745
|
|
|
|
|
|
|
|
746
|
1
|
|
|
1
|
|
30
|
use 5.006; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
42
|
|
747
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
36
|
|
748
|
1
|
|
|
1
|
|
62
|
use warnings; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
39
|
|
749
|
1
|
|
|
1
|
|
2906
|
use DBI; |
|
1
|
|
|
|
|
23136
|
|
|
1
|
|
|
|
|
74
|
|
750
|
1
|
|
|
1
|
|
12
|
use vars qw/$VERSION %tabs @QDTT/; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2565
|
|
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
$VERSION = substr q$Revision: 1.63 $, 10; |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
@QDTT = qw/char text date/; # datatypes that need quoting |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
%tabs = ( |
757
|
|
|
|
|
|
|
ent => [ |
758
|
|
|
|
|
|
|
"id int unsigned not null auto_increment primary key", |
759
|
|
|
|
|
|
|
"nm varchar(255)", # name |
760
|
|
|
|
|
|
|
"type varchar(30)", # namespace-qualified class |
761
|
|
|
|
|
|
|
"uid char(32) UNIQUE", # universal id |
762
|
|
|
|
|
|
|
], |
763
|
|
|
|
|
|
|
attr => [ |
764
|
|
|
|
|
|
|
"id int unsigned not null", # FK: ent (no DRI) |
765
|
|
|
|
|
|
|
"nm varchar(32)", # name |
766
|
|
|
|
|
|
|
"i int", # various value |
767
|
|
|
|
|
|
|
"f float", # data types |
768
|
|
|
|
|
|
|
"s varchar(255)", |
769
|
|
|
|
|
|
|
"d datetime", |
770
|
|
|
|
|
|
|
"UNIQUE (id, nm)", |
771
|
|
|
|
|
|
|
], |
772
|
|
|
|
|
|
|
rel => [ |
773
|
|
|
|
|
|
|
"id int unsigned not null", # FK: ent |
774
|
|
|
|
|
|
|
"pid int unsigned not null", # parent id |
775
|
|
|
|
|
|
|
"type char(4)", # relationships can be codified |
776
|
|
|
|
|
|
|
"i int", # and/or enumerated |
777
|
|
|
|
|
|
|
"UNIQUE (id, pid, type, i)", |
778
|
|
|
|
|
|
|
], |
779
|
|
|
|
|
|
|
xattr => [ |
780
|
|
|
|
|
|
|
"id int unsigned not null", # FK: ent |
781
|
|
|
|
|
|
|
"type char(4)", |
782
|
|
|
|
|
|
|
"s text", # ascii blob |
783
|
|
|
|
|
|
|
"FULLTEXT (s)", |
784
|
|
|
|
|
|
|
], |
785
|
|
|
|
|
|
|
); |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
while (my ($tab, $cols) = each %tabs) { |
788
|
|
|
|
|
|
|
$tabs{$tab} = {}; |
789
|
|
|
|
|
|
|
for (@$cols) { |
790
|
|
|
|
|
|
|
my ($nm, $def) = /^(\w+)\s*(.*)/i; |
791
|
|
|
|
|
|
|
if ($nm eq uc($nm)) { |
792
|
|
|
|
|
|
|
push @{$tabs{$tab}{mods}}, "$nm $def"; |
793
|
|
|
|
|
|
|
next; |
794
|
|
|
|
|
|
|
} |
795
|
|
|
|
|
|
|
$tabs{$tab}{cols}{$nm}{def} = $def; |
796
|
|
|
|
|
|
|
$tabs{$tab}{cols}{$nm}{quote} ||= $def =~ /$_/i for @QDTT; |
797
|
|
|
|
|
|
|
} |
798
|
|
|
|
|
|
|
} |
799
|
|
|
|
|
|
|
|
800
|
0
|
|
|
0
|
|
0
|
sub OK { 1; } |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
# --- exported module interface ----------------------------------------------- |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
=head2 = new [pass-through] |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
Used to generate a datastore connection object. Any optional arguments passed may be used to create and configure the connection. The method returns a list containing a blessed object, a numeric error code, and a human legible error string. |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
If the object is set to I the caller should check the error values, else the returned object may be used to access the methods listed below: |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=cut |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
sub new { |
813
|
1
|
|
|
1
|
|
3
|
my $self = shift; |
814
|
1
|
0
|
|
|
|
4
|
return unless $self->dbc(&DB::Ent::args); |
815
|
|
|
|
|
|
|
|
816
|
0
|
|
0
|
|
|
0
|
$self->{dups} ||= $DB::Ent::DUPSWARN; |
817
|
0
|
|
|
|
|
0
|
$self->{dbh}->{PrintError} = 0; # we'll display errors |
818
|
0
|
0
|
|
|
|
0
|
$self->{dbh}->trace(2, $self->{trace}) |
819
|
|
|
|
|
|
|
if $self->{trace}; |
820
|
|
|
|
|
|
|
|
821
|
0
|
|
|
|
|
0
|
OK; |
822
|
|
|
|
|
|
|
} |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
sub dbc { |
825
|
1
|
|
|
1
|
|
3
|
my $self = shift; |
826
|
1
|
|
|
|
|
11
|
%$self = (%$self, @_); |
827
|
|
|
|
|
|
|
|
828
|
1
|
|
50
|
|
|
9
|
$self->{proto} ||= "mysql"; |
829
|
1
|
|
50
|
|
|
6
|
$self->{srv} ||= "localhost"; |
830
|
1
|
|
33
|
|
|
1243
|
$self->{usr} ||= (getpwuid($>))[0]; |
831
|
1
|
|
50
|
|
|
10
|
$self->{pwd} ||= ""; |
832
|
1
|
|
50
|
|
|
3
|
$self->{dbn} ||= ""; |
833
|
|
|
|
|
|
|
|
834
|
1
|
|
|
|
|
3
|
my $dsn = join ":", "DBI", @{$self}{qw/proto dbn srv/}; |
|
1
|
|
|
|
|
6
|
|
835
|
1
|
|
|
|
|
13
|
$self->{dbh} = DBI->connect($dsn, $self->{usr}, $self->{pwd}); |
836
|
0
|
|
|
|
|
|
@{$self}{qw/err errstr/} = ($?, $!); |
|
0
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
|
838
|
0
|
|
|
|
|
|
!$?; |
839
|
|
|
|
|
|
|
} |
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
=head2 = init |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
Used to create an entity schema this method must be called with extreme care as it will first destroy an existing schema, including all data. Before this method may be called, a connection to the datastore must be established. |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
Typically it is not necessary for users to call this method directly since the B method will call it if it detects that the datastore has not been initialised. |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
The storage element types (in the nomenclature of this driver these are database tables) created are named: I, I, I, and I. The B parameter to the various methods offered by this module must receive one of these values. |
848
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
This method takes no arguments and returns a success flag. |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
=cut |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
sub init { |
854
|
0
|
|
|
0
|
|
|
my $self = shift; |
855
|
0
|
|
|
|
|
|
my %args = @_; |
856
|
0
|
|
|
|
|
|
for (keys %tabs) { |
857
|
0
|
0
|
|
|
|
|
$self->x("DROP TABLE IF EXISTS $_") if $args{DROP}; |
858
|
0
|
0
|
|
|
|
|
$self->tabmk() || return; |
859
|
|
|
|
|
|
|
} |
860
|
0
|
|
|
|
|
|
OK; |
861
|
|
|
|
|
|
|
} |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
=head2 = ins [filt-hashref] |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
Creates a new entry of the type indicated by the first argument passed (see docs for the I method above for a review of valid names). Attributes must be passed in a hash reference and must match those allowed by the element type. |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
The return value consists of the id of the new entry; in case of failure error information is returned. |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
B For signature compatibility with I, this method accepts a filter hash reference whose keys are added to the I. This makes for easy upserts! |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
=cut |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
# needs to support upserts and coderefs for values |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
sub ins { |
876
|
0
|
|
|
0
|
|
|
my ($self, $nm, $args, $filt) = @_; |
877
|
0
|
|
|
|
|
|
my (@cols, @vals); |
878
|
0
|
0
|
|
|
|
|
$args = {%$args, %$filt} if ref($filt) eq "HASH"; |
879
|
0
|
|
|
|
|
|
for (keys %$args) { |
880
|
0
|
|
|
|
|
|
push @cols, $_; |
881
|
0
|
0
|
|
|
|
|
push @vals, $tabs{$nm}{cols}{$_}{quote} |
882
|
|
|
|
|
|
|
? $self->q($args->{$_}) |
883
|
|
|
|
|
|
|
: $args->{$_} |
884
|
|
|
|
|
|
|
; |
885
|
|
|
|
|
|
|
} |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
$self->x( |
888
|
0
|
|
|
|
|
|
sprintf("INSERT INTO $nm (%s) VALUES (%s)", |
889
|
|
|
|
|
|
|
join(",", @cols), |
890
|
|
|
|
|
|
|
join(",", @vals), |
891
|
|
|
|
|
|
|
) |
892
|
|
|
|
|
|
|
); |
893
|
|
|
|
|
|
|
} |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
=head2 = upd [filt-hashref] |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
This method updates an entry of the type specified by the I parameter (see docs for the I method above for a review of valid names). The data updated is provided as a hash reference of attribute name/value pairs. Additionally a filter may be provided (also as a hash reference of attribute name/value pairs) which limits the update operation to only those entries specified (in table parlance, this represents a row selector i.e. an sql where clause). |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
The return value indicates the number of rows affected. |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
=cut |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
sub upd { |
904
|
0
|
|
|
0
|
|
|
my ($self, $nm, $attr, $filt) = @_; |
905
|
|
|
|
|
|
|
|
906
|
0
|
|
|
|
|
|
my $attrs; |
907
|
|
|
|
|
|
|
$attrs .= "$_ = " . $self->q($attr->{$_}) . ", " |
908
|
0
|
|
|
|
|
|
for keys %$attr; |
909
|
0
|
|
|
|
|
|
$attrs =~ s/,\s+$//; |
910
|
0
|
|
|
|
|
|
my $WHERE = $self->where($nm, $filt); |
911
|
|
|
|
|
|
|
|
912
|
0
|
|
|
|
|
|
$self->x("UPDATE $nm SET $attrs $WHERE"); |
913
|
|
|
|
|
|
|
} |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
=head2 = del |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
Deletes any entity that matches the given list of attributes' values. Instead of not passing any attributes in the hash reference in order to delete all items in a table, pass the key I set to 1 - this is to prevent costly mistakes. |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
=cut |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
sub del { |
922
|
0
|
|
|
0
|
|
|
my $self = shift; |
923
|
0
|
|
|
|
|
|
my ($nm, $cols) = @_; |
924
|
|
|
|
|
|
|
|
925
|
0
|
0
|
0
|
|
|
|
my $WHERE = $cols->{ALL} ? "" : $self->where($nm, $cols) || return; |
926
|
0
|
|
|
|
|
|
$self->x("DELETE FROM $nm $WHERE"); |
927
|
|
|
|
|
|
|
} |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
=head2 = sel |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
Returns a list of hash references containing entities that match the selection criteria. The values in the hash to this method may contain list references and wildcards are allowed within scalars for incomplete matching. |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
=cut |
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
sub sel { |
936
|
0
|
|
|
0
|
|
|
my ($self, $nm, $cols, %opts) = @_; |
937
|
0
|
|
0
|
|
|
|
$opts{sort} ||= 1; |
938
|
|
|
|
|
|
|
|
939
|
0
|
|
|
|
|
|
my $WHERE = $self->where($nm, $cols); |
940
|
0
|
|
|
|
|
|
$self->x("SELECT * FROM $nm $WHERE ORDER BY $opts{sort}"); |
941
|
|
|
|
|
|
|
} |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
sub max { |
944
|
0
|
|
|
0
|
|
|
my $self = shift; |
945
|
0
|
|
|
|
|
|
my $nm = shift; |
946
|
0
|
|
|
|
|
|
my $col = shift; |
947
|
0
|
|
|
|
|
|
my $WHERE = $self->where($nm, &DB::Ent::args); |
948
|
0
|
|
|
|
|
|
my @ret = $self->x("SELECT max($col) max FROM $nm $WHERE"); |
949
|
0
|
0
|
|
|
|
|
$ret[0]->{max} || 0; |
950
|
|
|
|
|
|
|
} |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
=head2 = def |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
Returns a list of attributes associated with a particular entity type. |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
=cut |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
# --- internal utility methods ------------------------------------------------ |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
sub tabcols { |
961
|
0
|
|
|
0
|
|
|
my $self = shift; |
962
|
0
|
|
0
|
|
|
|
my $nm = shift || $_; |
963
|
0
|
|
|
|
|
|
keys %{$tabs{$nm}{cols}}; |
|
0
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
} |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
sub tabmk { |
967
|
0
|
|
|
0
|
|
|
my $self = shift; |
968
|
0
|
|
0
|
|
|
|
my $nm = shift || $_; |
969
|
|
|
|
|
|
|
|
970
|
0
|
|
|
|
|
|
my @cols; |
971
|
0
|
|
|
|
|
|
push @cols, "$_ $tabs{$nm}{cols}{$_}{def}" |
972
|
0
|
|
|
|
|
|
for keys %{$tabs{$nm}{cols}}; |
973
|
0
|
|
|
|
|
|
push @cols, $_ |
974
|
0
|
|
|
|
|
|
for @{$tabs{$nm}{mods}}; |
975
|
0
|
|
|
|
|
|
$self->x( |
976
|
|
|
|
|
|
|
sprintf("CREATE TABLE IF NOT EXISTS $nm (%s)", join(",", @cols)) |
977
|
|
|
|
|
|
|
); |
978
|
|
|
|
|
|
|
} |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
# constructs predicates for a where clause |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
sub where { |
983
|
0
|
|
|
0
|
|
|
my $self = shift; |
984
|
0
|
|
|
|
|
|
my $tab = shift; |
985
|
|
|
|
|
|
|
|
986
|
0
|
|
|
|
|
|
my @ret; my %cols = &DB::Ent::args; |
|
0
|
|
|
|
|
|
|
987
|
0
|
|
|
|
|
|
while (my ($nm, $v) = each %cols) { |
988
|
0
|
0
|
|
|
|
|
next unless defined $v; |
989
|
|
|
|
|
|
|
|
990
|
0
|
|
|
|
|
|
my $q = $tabs{$tab}{cols}{$nm}{quote}; |
991
|
0
|
0
|
|
|
|
|
if (ref $v eq "ARRAY") { |
992
|
0
|
0
|
|
|
|
|
$v = $q ? $self->qin($v) : $self->in($v); |
993
|
0
|
|
|
|
|
|
push @ret, "$nm IN ($v)"; |
994
|
0
|
|
|
|
|
|
next; |
995
|
|
|
|
|
|
|
} |
996
|
0
|
0
|
|
|
|
|
if ($v =~ /%/) { |
997
|
0
|
|
|
|
|
|
push @ret, "$nm LIKE " . $self->q($v); |
998
|
0
|
|
|
|
|
|
next; |
999
|
|
|
|
|
|
|
} |
1000
|
0
|
0
|
|
|
|
|
push @ret, "$nm = " . ($q ? $self->q($v) : $v); |
1001
|
|
|
|
|
|
|
} |
1002
|
|
|
|
|
|
|
|
1003
|
0
|
0
|
|
|
|
|
return "WHERE " . join " AND ", @ret if @ret; |
1004
|
|
|
|
|
|
|
} |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
# safe-quotes a list[-ref] of strings |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
sub q { |
1009
|
0
|
|
|
0
|
|
|
my $self = shift; |
1010
|
0
|
|
|
|
|
|
my @v = &DB::Ent::args; |
1011
|
0
|
|
|
|
|
|
$_ = $self->{dbh}->quote($_) for @v; |
1012
|
0
|
0
|
0
|
|
|
|
warn "q(): multiple args but scalar context!" |
1013
|
|
|
|
|
|
|
if @v > 1 && !wantarray(); |
1014
|
0
|
0
|
|
|
|
|
wantarray() ? @v : $v[0]; |
1015
|
|
|
|
|
|
|
} |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
sub in { |
1018
|
0
|
|
|
0
|
|
|
my $self = shift; |
1019
|
0
|
|
|
|
|
|
join ", ", &DB::Ent::args; |
1020
|
|
|
|
|
|
|
} |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
# returns a string ready for use with an IN statement |
1023
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
sub qin { |
1025
|
0
|
|
|
0
|
|
|
my $self = shift; |
1026
|
0
|
|
|
|
|
|
join ", ", $self->q(@_); |
1027
|
|
|
|
|
|
|
} |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
# Syntax: |
1030
|
|
|
|
|
|
|
# = x |
1031
|
|
|
|
|
|
|
# = x |
1032
|
|
|
|
|
|
|
# = x |
1033
|
|
|
|
|
|
|
# = x |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
sub x { |
1036
|
0
|
|
|
0
|
|
|
my $self = shift; |
1037
|
0
|
|
0
|
|
|
|
$self->{cmd} = shift || $_ || return warn qq/x(): No command!/; |
1038
|
0
|
0
|
|
|
|
|
$self->dbcmd() if $self->{debug} > 0; |
1039
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
# prepare and execute |
1041
|
|
|
|
|
|
|
|
1042
|
0
|
|
0
|
|
|
|
my $sth = $self->{dbh}->prepare($self->{cmd}) |
1043
|
|
|
|
|
|
|
|| return $self->dberr(); |
1044
|
0
|
0
|
|
|
|
|
$sth->execute() |
1045
|
|
|
|
|
|
|
|| return $self->dberr(); |
1046
|
|
|
|
|
|
|
|
1047
|
0
|
|
|
|
|
|
my $ok = $self->dberr(); |
1048
|
|
|
|
|
|
|
|
1049
|
0
|
0
|
|
|
|
|
return $self->{dbh}->{mysql_insertid} |
1050
|
|
|
|
|
|
|
if $self->{cmd} =~ /\bINSERT\b/i; |
1051
|
|
|
|
|
|
|
|
1052
|
0
|
0
|
|
|
|
|
return $self->{dbh}->rows |
1053
|
|
|
|
|
|
|
if $self->{cmd} =~ /\bUPDATE\b/i; |
1054
|
|
|
|
|
|
|
|
1055
|
0
|
0
|
|
|
|
|
return $ok |
1056
|
|
|
|
|
|
|
unless $self->{cmd} =~ /\bSELECT\b/i; |
1057
|
|
|
|
|
|
|
|
1058
|
0
|
|
|
|
|
|
my $ret = $sth->fetchall_arrayref({}); |
1059
|
0
|
0
|
|
|
|
|
wantarray() ? @$ret : $ret; |
1060
|
|
|
|
|
|
|
} |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
sub dberr { |
1063
|
0
|
|
|
0
|
|
|
my $self = shift; |
1064
|
0
|
|
0
|
|
|
|
$self->{err} = $self->{dbh}->err || 0; |
1065
|
0
|
|
0
|
|
|
|
$self->{errstr} = $self->{dbh}->errstr || ""; |
1066
|
|
|
|
|
|
|
|
1067
|
0
|
|
|
|
|
|
my $die = $self->{DIE}; |
1068
|
0
|
0
|
|
|
|
|
if ($self->{err} == 1062) { |
1069
|
0
|
0
|
|
|
|
|
return if $self->{dups} == $DB::Ent::DUPSQUIET; |
1070
|
0
|
0
|
|
|
|
|
$die = undef if $self->{dups} == $DB::Ent::DUPSWARN; |
1071
|
|
|
|
|
|
|
} |
1072
|
0
|
0
|
|
|
|
|
if ($self->{err}) { |
1073
|
0
|
0
|
|
|
|
|
$self->dbcmd() unless $self->{debug} > 0; |
1074
|
0
|
0
|
0
|
|
|
|
$die && die($self->{errstr}) || warn($self->{errstr}); |
1075
|
|
|
|
|
|
|
} |
1076
|
0
|
|
|
|
|
|
!$self->{err}; |
1077
|
|
|
|
|
|
|
} |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
sub dbcmd { |
1080
|
0
|
|
|
0
|
|
|
my $self = shift; |
1081
|
0
|
|
0
|
|
|
|
local $_ = shift || $self->{cmd}; |
1082
|
|
|
|
|
|
|
|
1083
|
0
|
|
|
|
|
|
s/^\s*/> /mg; s/\t/ /g; |
|
0
|
|
|
|
|
|
|
1084
|
0
|
|
|
|
|
|
$_ = sprintf("%s\n%s\n", ln("db->x()"), $_); |
1085
|
|
|
|
|
|
|
|
1086
|
0
|
0
|
|
|
|
|
print unless defined wantarray; $_; |
|
0
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
} |
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
sub ln { |
1090
|
0
|
|
|
0
|
|
|
my $title = shift; |
1091
|
0
|
|
0
|
|
|
|
my $wd = shift || 60; |
1092
|
0
|
|
|
|
|
|
my $ln = "-" x $wd; |
1093
|
0
|
0
|
|
|
|
|
return $title ? substr("--- $title $ln", 0, $wd) : $ln; |
1094
|
|
|
|
|
|
|
} |
1095
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
=head1 AUTHOR |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
Erick Calder |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
=head1 SUPPORT |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
For help and thank you notes, e-mail the author directly. To report a bug, submit a patch or add to our wishlist please visit the CPAN bug manager at: F |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
=head1 AVAILABILITY |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
The latest version of the tarball, RPM and SRPM may always be found at: F Additionally the module is available from CPAN. |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
=head1 SEE ALSO |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
L, L. |
1111
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
=head1 LICENCE AND COPYRIGHT |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
Copyright (c) 2002-2003 Erick Calder. |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
This product is free and distributed under the Gnu Public License (GPL). A copy of this license was included in this distribution in a file called LICENSE. If for some reason, this file was not included, please see F to obtain a copy of this license. |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
$Id: Ent.pm,v 1.63 2003/06/24 03:58:11 ekkis Exp $ |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
=cut |
1121
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
1; # yipiness :) |
| | |