[Bio] / Sprout / ERDBQuery.pm Repository:
ViewVC logotype

Annotation of /Sprout/ERDBQuery.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (view) (download) (as text)

1 : parrello 1.1 package ERDBQuery;
2 :    
3 :     use strict;
4 :     use DBKernel;
5 :     use ERDBObject;
6 :     use DBI;
7 :     use Tracer;
8 :    
9 :     =head1 Entity-Relationship Database Package Query Iterator
10 :    
11 :     =head2 Introduction
12 :    
13 :     This package defines the Iteration object for an Entity-Relationship Database. The iteration object
14 :     represents a filtered SELECT statement against an Entity-Relationship Database, and provides
15 :     methods for getting the appropriate records.
16 :    
17 :     There are two common ways an iteration object can be created. An I<entity iterator> is created when the
18 :     client asks for objects of a given entity type. A I<relationship iterator> is created when the
19 :     client asks for objects across a relationship starting from a specific entity instance. The
20 :     entity iterator returns a single object at each position; the relationship iterator returns two
21 :     objects at each position-- one for the target entity, and one for the relationship instance
22 :     that connects it to the source entity.
23 :    
24 :     For example, a client could ask for all B<Feature> instances that are marked active. This would
25 :     return an entity iterator. Each position in the iteration would consist of a single
26 :     B<Feature> instance. From a specific B<Feature> instance, the client could decide to cross the
27 :     B<IsLocatedIn> relationship to get all the B<Contig> instances which contain residues that
28 :     participate in the feature. This would return a relationship iterator. Each position in the
29 :     iterator would contain a single B<IsLocatedIn> instance and a single B<Contig> instance.
30 :    
31 :     At each point in the result set, the iterator returns a B<ERDBObject>. The ERDBObject allows the
32 :     client to access the fields of the current entity or relationship instance.
33 :    
34 :     It is also possible to ask for many different objects in a single iterator by chaining long
35 :     sequences of entities together by relationships. This is discussed in the documentation for the
36 :     B<ERDB> object's C<Get> method.
37 :    
38 :     Finally, objects of this type should never by created directly. Instead, they are created
39 :     by the aforementioned C<Get> method and the B<ERDBObject>'s C<Cross> method.
40 :    
41 :     =head2 Public Methods
42 :    
43 :     =head3 Fetch
44 :    
45 :     my $dbObject = $dbQuery->Fetch();
46 :    
47 :     Retrieve a record from this query. The record returned will be a B<ERDBObject>, which
48 :     may represent a single entity instance or a list of entity instances joined by relationships.
49 :     The first time this method is called it will return the first result from query. After that it
50 :     will continue sequentially. It will return an undefined value if we've reached the end of the
51 :     result set.
52 :    
53 :     =cut
54 :    
55 :     sub Fetch {
56 :     # Get the parameters;
57 :     my ($self) = @_;
58 :     # Declare the return variable.
59 :     my $retVal;
60 : parrello 1.4 # Do we have a statement handle?
61 :     my $sth = $self->{_sth};
62 :     if (! defined $sth) {
63 :     # No, so we have a prepared statement. Start the query.
64 :     $sth = $self->_Run();
65 :     }
66 : parrello 1.1 # Fetch the next row in the query result set.
67 :     my @row = $sth->fetchrow;
68 :     # Check to see if we got any results.
69 :     if (@row == 0) {
70 :     # Here we have no result. If we're at the end of the result set, this is okay, because
71 :     # we'll be returning an undefined value in $retVal. If an error occurred, we need to abort.
72 :     if ($sth->err) {
73 :     Confess("FETCH error: " . $sth->err);
74 :     } else {
75 :     # Trace the number of results returned.
76 :     Trace("$self->{_results} rows processed by query.") if T(SQL => 4);
77 :     }
78 :     } else {
79 :     # Here we have a result, so we need to turn it into an instance object.
80 :     $retVal = ERDBObject->_new($self, @row);
81 :     $self->{_results}++;
82 :     }
83 :     # Return the result.
84 :     return $retVal;
85 :     }
86 :    
87 : parrello 1.2 =head3 DefaultObjectName
88 :    
89 :     my $objectName = $query->DefaultObjectName();
90 :    
91 :     Return the name of this query's default entity or relationship.
92 :    
93 :     =cut
94 :    
95 :     sub DefaultObjectName {
96 :     # Get the parameters.
97 :     my ($self) = @_;
98 :     # Get the relation map.
99 :     my $map = $self->{_objectNames};
100 :     # Get the default object alias. This is always the first alias in the
101 :     # relation map.
102 :     my $retVal = $map->[0][0];
103 :     # Return the result.
104 :     return $retVal;
105 :     }
106 :    
107 :    
108 : parrello 1.1 =head3 AnalyzeFieldName
109 :    
110 :     my ($objectName, $fieldName, $type) = $query->AnalyzeFieldName($name);
111 :    
112 :     Analyze a field name (such as might be found in a [[ErdbPm#GetAll]]
113 :     parameter list) and return the real name of the relevant entity or
114 :     relationship, the field name itself, and the associated type object
115 :     (which will be a subclass of [[ERDBTypePm]].
116 :    
117 :     =over 4
118 :    
119 :     =item name
120 :    
121 :     Field name to examine, in the standard field name format used by [[ErdbPm]].
122 :    
123 :     =item RETURN
124 :    
125 :     Returns a 3-tuple containing the name of the object containing the field, the
126 :     base field name, and a type object describing the field's type.
127 :    
128 :     =back
129 :    
130 :     =cut
131 :    
132 :     sub AnalyzeFieldName {
133 :     # Get the parameters.
134 :     my ($self, $name) = @_;
135 : parrello 1.3 # Attempt to find the field's data.
136 :     my ($objectName, $fieldName, $type) = $self->CheckFieldName($name);
137 :     # Process errors.
138 :     if (! defined $objectName) {
139 :     Confess("Field identifier \"$name\" has an invalid format.");
140 :     } elsif (! defined $fieldName) {
141 :     Confess("Object name \"$objectName\" not found in query.");
142 :     } elsif (! defined $type) {
143 :     Confess("Field name \"$fieldName\" not found in \"$objectName\".");
144 :     }
145 :     # Return the results.
146 :     return ($objectName, $fieldName, $type);
147 :     }
148 :    
149 :    
150 :     =head3 CheckFieldName
151 :    
152 :     my ($objectName, $fieldName, $type) = $query->CheckFieldName($name);
153 :    
154 :     Analyze a field name (such as might be found in a [[ErdbPm#GetAll]]
155 :     parameter list) and return the real name of the relevant entity or
156 :     relationship, the field name itself, and the associated type object
157 : parrello 1.5 (which will be a subclass of [[ERDBTypePm]]). Unlike L</AnalyzeFIeldName>,
158 : parrello 1.3 this method always returns results. If the field name is invalid, one
159 :     or more of the three results will be undefined.
160 :    
161 :     =over 4
162 :    
163 :     =item name
164 :    
165 :     Field name to examine, in the standard field name format used by [[ErdbPm]].
166 :    
167 :     =item RETURN
168 :    
169 :     Returns a 3-tuple containing the name of the object containing the field, the
170 :     base field name, and a type object describing the field's type. If the field
171 :     descriptor is invalid, the returned object name will be undefined. If the object
172 :     name is invalid, the returned field name will be undefined, and if the field
173 :     name is invalid, the returned type will be undefined.
174 :    
175 :     =back
176 :    
177 :     =cut
178 :    
179 :     sub CheckFieldName {
180 :     # Get the parameters.
181 :     my ($self, $name) = @_;
182 : parrello 1.1 # Declare the return variables.
183 :     my ($objectName, $fieldName, $type);
184 :     # Get the relation map.
185 :     my $map = $self->{_objectNames};
186 :     # Get the default object alias. This is always the first alias in the
187 :     # relation map.
188 :     my $defaultName = $map->[0][0];
189 :     # Parse the field name.
190 : parrello 1.3 my ($alias, $fieldThing) = ERDB::ParseFieldName($name, $defaultName);
191 :     # Only proceed if we could successfully parse the field. If we couldn't,
192 :     # everything will be going back undefined.
193 :     if (defined $alias) {
194 : parrello 1.1 # Find the alias in the relation map.
195 :     my ($aliasTuple) = grep { $_->[0] eq $alias } @$map;
196 :     if (! defined $aliasTuple) {
197 : parrello 1.3 # We have a bad object name, so the object name is all
198 :     # we return.
199 :     $objectName = $alias;
200 : parrello 1.1 } else {
201 :     # Get the real object name.
202 : parrello 1.3 $objectName = $aliasTuple->[1];
203 :     # Now it's safe to return the field name.
204 :     $fieldName = $fieldThing;
205 : parrello 1.1 # Ask the database for the field descriptor. If the field name is
206 :     # invalid, this will throw an error.
207 : parrello 1.3 my $fieldData = $self->{_db}->_CheckField($objectName, $fieldName);
208 :     # Only proceed if the field exists.
209 :     if (defined $fieldData) {
210 :     # Extract the field type.
211 :     my $typeName = $fieldData->{type};
212 :     # Get the corresponding type object.
213 :     $type = ERDB::GetDataTypes()->{$typeName};
214 :     }
215 : parrello 1.1 }
216 :     }
217 :     # Return the results.
218 :     return ($objectName, $fieldName, $type);
219 :     }
220 :    
221 :     =head2 Internal Methods
222 :    
223 :     =head3 _new
224 :    
225 :     my $query = ERDBQuery->new($database, $sth, $relationMap, $searchObject);
226 :    
227 :     Create a new query object.
228 :    
229 :     =over 4
230 :    
231 :     =item database
232 :    
233 :     ERDB object for the relevant database.
234 :    
235 :     =item sth
236 :    
237 :     Statement handle for the SELECT clause generated by the query.
238 :    
239 :     =item relationMap
240 :    
241 :     Reference to a list of 2-tuples. Each tuple consists of an object name as used
242 :     in the query followed by the actual name of that object. This enables the
243 :     B<ERDBObject> to determine the order of the tables in the query and which object
244 :     name belongs to each mapped object name. Most of the time these two values are
245 :     the same; however, if a relation occurs twice in the query, the relation name in
246 :     the field list and WHERE clause will use a mapped name (generally the actual
247 :     relation name with a numeric suffix) that does not match the actual relation
248 :     name.
249 :    
250 :     =item searchObject (optional)
251 :    
252 :     If specified, then the query is a full-text search, and the first field will be a
253 :     relevance indicator for the named table.
254 :    
255 :     =back
256 :    
257 :     =cut
258 :    
259 :     sub _new {
260 :     # Get the parameters.
261 :     my ($database, $sth, $relationMap, $searchObject) = @_;
262 :     # Create this object.
263 :     my $self = { _db => $database, _sth => $sth, _objectNames => $relationMap,
264 :     _fullText => $searchObject, _results => 0 };
265 :     # Bless and return it.
266 :     bless $self;
267 :     return $self;
268 :     }
269 :    
270 : parrello 1.4 =head3 _Prepare
271 :    
272 :     $query->_Prepare($command, $parms);
273 :    
274 :     Cache the SQL command and parameter list for this query. The information
275 :     can be used to run the query at a future point.
276 :    
277 :     =over 4
278 :    
279 :     =item command
280 :    
281 :     SQL command to execute for this query.
282 :    
283 :     =item parms
284 :    
285 :     Parameters to feed to the query.
286 :    
287 :     =back
288 :    
289 :     =cut
290 :    
291 :     sub _Prepare {
292 :     # Get the parameters.
293 :     my ($self, $command, $parms) = @_;
294 :     # Stash the SQL command and the parameters.
295 :     $self->{_sql} = $command;
296 :     $self->{_parms} = $parms;
297 :     }
298 :    
299 :     =head3 _Run
300 :    
301 :     $query->_Run();
302 :    
303 :     Run this query. This method is used the first time Fetch is called
304 :     on a prepared query.
305 :    
306 :     =cut
307 :    
308 :     sub _Run {
309 :     # Get the parameters.
310 :     my ($self) = @_;
311 :     # Declare the return variable.
312 :     my $retVal;
313 :     # Get the database object and the SQL command.
314 :     my $erdb = $self->{_db};
315 :     my $command = $self->{_sql};
316 :     # Insure both are valid.
317 :     if (! defined $erdb) {
318 :     Confess("No database available to run this query.");
319 :     } elsif (! defined $command) {
320 :     Confess("Attempt to get results from an unprepared query.");
321 :     } else {
322 :     # Get the parameters. If there are no parameters, we use an empty list.
323 :     my $parms = $self->{_parms} || [];
324 :     # Create the statement handle and run the query.
325 :     $retVal = $erdb->_GetStatementHandle($command, $parms);
326 :     # Save the statement handle for Fetch to use.
327 :     $self->{_sth} = $retVal;
328 :     }
329 :     # Return it.
330 :     return $retVal;
331 :     }
332 :    
333 :    
334 : parrello 1.1 1;

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3