KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdbc > JdbcQueryResult


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.jdbc;
13
14 import com.versant.core.common.Debug;
15 import com.versant.core.common.OID;
16 import com.versant.core.common.QueryResultContainer;
17 import com.versant.core.common.State;
18 import com.versant.core.metadata.*;
19 import com.versant.core.server.*;
20 import com.versant.core.jdbc.metadata.*;
21 import com.versant.core.jdbc.query.JdbcCompiledQuery;
22 import com.versant.core.jdbc.query.JdbcJDOQLCompiler;
23 import com.versant.core.jdbc.query.SqlStruct;
24 import com.versant.core.jdbc.sql.exp.SelectExp;
25 import com.versant.core.jdbc.sql.exp.SqlExp;
26 import com.versant.core.jdbc.sql.SqlDriver;
27
28 import com.versant.core.jdbc.ejbql.JdbcQueryResultEJBQL;
29
30
31 import java.math.BigDecimal JavaDoc;
32 import java.math.BigInteger JavaDoc;
33 import java.sql.*;
34 import java.util.*;
35
36 import com.versant.core.common.BindingSupportImpl;
37 import com.versant.core.storagemanager.RunningQuery;
38 import com.versant.core.storagemanager.ExecuteQueryReturn;
39 import com.versant.core.storagemanager.ApplicationContext;
40 import com.versant.core.jdo.QueryDetails;
41
42 /**
43  * The results of a JDBC JDOQL or SQL query.
44  */

45 public class JdbcQueryResult
46         implements RunningQuery, ExecuteQueryReturn {
47
48     private FetchInfo fetchInfo = new FetchInfo();
49
50     protected PreparedStatement ps;
51     private ResultSet rs;
52     private Connection con;
53
54     private ClassMetaData cmd;
55     protected final JdbcStorageManager sm;
56     private FetchGroup fetchGroup;
57     private boolean includeSubclasses;
58     private boolean scrollable;
59     /**
60      * The rs column position.
61      */

62     private int pos;
63     private int oidCols;
64     private JdbcOID lastOID;
65
66     public String JavaDoc sql;
67
68     /**
69      * A map of {@link ColFHKey} to {@link ColFieldHolder}. A ColFieldHolder is
70      * added to the map once some or all of its results have been processed.
71      */

72     private Map colHMap = new HashMap();
73     /**
74      * The compiled query for this result.
75      */

76     protected JdbcCompiledQuery cq;
77     /**
78      * used for creating queries for collecion fields.
79      */

80     private JdbcJDOQLCompiler qCompiler;
81
82     private ModelMetaData jmd;
83     private Object JavaDoc[] params;
84     private boolean forUpdate;
85     private int nextCount;
86
87     private final Object JavaDoc[] singleResult = new Object JavaDoc[1];
88     private boolean isCrossJoined;
89
90     /**
91      * No iteration has been done on the query.
92      */

93     private static final int STATUS_NOT_STARTED = 0;
94     /**
95      * This query is being iterated over.
96      */

97     private static final int STATUS_BUSY = 1;
98     /**
99      * This query has been iterated over to the end.
100      */

101     private static final int STATUS_FINISHED = 2;
102
103     /**
104      * Container used for caching the results
105      */

106     CachedQueryResult qRCache;
107     /**
108      * The state of the result.
109      *
110      * @see #STATUS_NOT_STARTED
111      * @see #STATUS_BUSY
112      * @see #STATUS_FINISHED
113      */

114     private int status;
115     /**
116      * This is a flag to indicate if the results might be cached or may be retrieved from cache.
117      * A true might change to false but a false may not become a true.
118      */

119     private boolean isCacheble;
120
121     public JdbcQueryResult next;
122     public JdbcQueryResult prev;
123
124     public JdbcQueryResult(JdbcStorageManager sm, JdbcCompiledQuery cq,
125             Object JavaDoc[] params, boolean cachable) {
126         this.sm = sm;
127         this.cq = cq;
128         this.params = params;
129         isCacheble = cachable && cq.isCacheble();
130     }
131
132     public void init(ClassMetaData cmd, boolean scrollable) {
133         this.cmd = cmd;
134         if (cmd != null) {
135             this.fetchGroup = cmd.fetchGroups[cq.getFetchGroupIndex()];
136             this.oidCols = ((JdbcClass)cmd.storeClass).table.pkSimpleColumnCount;
137             isCrossJoined = cq.isCrossJoinAllowed()
138                     && fetchGroup.crossJoinedCollectionField != null;
139         } else {
140             isCrossJoined = false;
141         }
142
143         this.jmd = sm.getJmd();
144         this.includeSubclasses = cq.isIncludeSubclasses();
145         this.scrollable = scrollable;
146     }
147
148     public RunningQuery getRunningQuery() {
149         return this;
150     }
151
152     public int getRelativeResultCount() {
153         return nextCount;
154     }
155
156     public void resetRelativeResultCount() {
157         nextCount = 0;
158     }
159
160     public Object JavaDoc[] getParams() {
161         return params;
162     }
163
164     public void setParams(Object JavaDoc[] params) {
165         this.params = params;
166     }
167
168     public FgDs getFgDs() {
169         return ((JdbcFetchGroup)fetchGroup.storeFetchGroup).getFgDs(includeSubclasses, false);
170     }
171
172     private OID getLastOID() {
173         return lastOID;
174     }
175
176     public void cancel() {
177         try {
178             ps.cancel();
179         } catch (NullPointerException JavaDoc e) {
180             //ignore
181
} catch (SQLException e) {
182             throw BindingSupportImpl.getInstance().invalidOperation(e.getMessage(), e);
183         }
184     }
185
186     public CompiledQuery getCompiledQuery() {
187         return cq;
188     }
189
190     public JdbcCompiledQuery getJdbcCompiledQuery() {
191         return cq;
192     }
193
194     public boolean isClosed() {
195         return ps == null;
196     }
197
198     /**
199      * Close these results.
200      */

201     public void close() {
202         if (colHMap != null) {
203             Collection col = colHMap.values();
204             for (Iterator iterator = col.iterator(); iterator.hasNext();) {
205                 ColFieldHolder holder = (ColFieldHolder)iterator.next();
206                 if (holder != null) {
207                     holder.close();
208                 }
209             }
210             colHMap.clear();
211         }
212
213         if (ps == null) return;
214         if (rs != null) {
215             try {
216                 rs.close();
217             } catch (SQLException e) {
218                 // ignore
219
}
220             rs = null;
221         }
222         if (isEJBQLHack()) {
223
224             ((JdbcQueryResultEJBQL)this).closeImp();
225
226         } else {
227             try {
228                 ps.close();
229                 ps = null;
230             } catch (SQLException e) {
231                 throw sm.handleException(e);
232             }
233         }
234
235         setNonCacheble();
236         status = STATUS_FINISHED;
237
238         qRCache = null;
239         params = null;
240         //cq = null;
241
}
242
243     /**
244      * Are there more results? Calling this advances to the next result if
245      * there is one and returns true otherwise it returns false.
246      */

247     public boolean next(int skip) {
248         pos = 1;
249         /**
250          * If a crossJoin was used then we may be on the next row already.
251          */

252         try {
253             if (isCrossJoined) {
254                 if (skip == 0 && fetchInfo.onNextRow) {
255                     fetchInfo.onNextRow = false;
256                     return true;
257                 }
258                 OID compareTo = null;
259                 if (!fetchInfo.onNextRow && !fetchInfo.onValidRow) {
260                     /**
261                      * Not already on a next row
262                      */

263                     if (!rs.next()) {
264                         fetchInfo.onValidRow = false;
265                         return false;
266                     }
267                     fetchInfo.onValidRow = true;
268                     if (skip == 0) {
269                         return true;
270                     }
271                 }
272
273                 /**
274                  * If already on next with a skip greater that zero
275                  */

276                 compareTo = fetchInfo.nextOid;
277                 if (compareTo == null) {
278                     compareTo = getResultOID();
279                     pos = 1;
280                 }
281                 for (;skip >= 0;) {
282                     /**
283                      * Skip over the amount of same oids.
284                      */

285                     if (!rs.next()) {
286                         return false;
287                     }
288                     if (!getResultOID().equals(compareTo)) {
289                         //found a next batch
290
skip--;
291                     }
292                     //update to the lastOid
293
compareTo = lastOID;
294                 }
295                 fetchInfo.nextOid = lastOID;
296                 return true;
297             } else {
298                 if (scrollable) {
299                     return rs.relative(skip + 1);
300                 } else {
301                     for (int i = 0; i < skip; i++) if (!rs.next()) return false;
302                     return rs.next();
303                 }
304             }
305         } catch (SQLException e) {
306             throw sm.handleException(e);
307         }
308     }
309
310     public boolean isRandomAccess() {
311         return scrollable;
312     }
313
314     public int getResultCount() {
315         if (isNotStarted()) {
316             updateCacheble();
317             executeQueryImp();
318             if (isCacheble()) {
319                 qRCache = new CachedQueryResult();
320             }
321         }
322
323         try {
324             if (!rs.last()) return 0;
325             return rs.getRow();
326         } catch (SQLException e) {
327             throw sm.handleException(e);
328         }
329     }
330
331     public boolean absolute(int index) {
332         try {
333             pos = 1;
334             return rs.absolute(index + 1);
335         } catch (SQLException e) {
336             throw sm.handleException(e);
337         }
338     }
339
340     /**
341      * Get the OID for the current result. This may only be called once
342      * per result and must be called before getResultState.
343      */

344     public OID getResultOID() {
345         pos = 1;
346         try {
347             lastOID = (JdbcOID)cmd.createOID(false);
348             if (cq.isSqlQuery()) {
349                 if (cmd.pkFields == null) {
350                     lastOID.copyKeyFields(rs, cq.getMappingInfo(rs).dsPkIndex);
351                 } else {
352                     lastOID.copyKeyFields(rs, cq.getMappingInfo(rs).fields,
353                             cq.getMappingInfo(rs).pkIndexInFieldsArray);
354                 }
355             } else {
356                 lastOID.copyKeyFields(rs, pos);
357                 pos += oidCols;
358             }
359             if (Debug.DEBUG) nextCount++;
360             return lastOID;
361         } catch (SQLException e) {
362             throw sm.handleException(e);
363         }
364     }
365
366     /**
367      * Get the State for the current result. This may only be called once
368      * per result and may only be called if getResultOID has been called.
369      *
370      * @see #getResultOID
371      */

372     public State getResultState(boolean forUpdate, StateContainer container) {
373         try {
374             if (cq.isSqlQuery()) {
375                 return getState(lastOID, cq.getMappingInfo(rs), rs);
376             } else {
377                 MutableInt mutableInt = new MutableInt();
378                 State state = sm.createStateImp(rs, lastOID,
379                         fetchGroup, forUpdate, pos, mutableInt, includeSubclasses,
380                         container, cq.fgDs, !cq.isParColFetchEnabled(),
381                         cq.isCrossJoinAllowed(), null);
382                 //must fetch crossJoin results if enabled
383
if (cq.isCrossJoinAllowed()) {
384                     FetchGroupField cjFGF = cq.fgDs.fg.crossJoinedCollectionField;
385                     if (cjFGF != null) {
386                         JdbcCollectionField colField = (JdbcCollectionField) cjFGF.fmd.storeField;
387                         colField.fetchFrom(rs, lastOID, state,
388                                 cq.fgDs.fg.crossJoinedCollectionField,
389                                 forUpdate, container,
390                                 !cq.isParColFetchEnabled(),
391                                 mutableInt.value, fetchInfo, sm);
392                     }
393                 }
394                 return state;
395             }
396         } catch (SQLException e) {
397             throw sm.handleException(e);
398         }
399     }
400
401     private static State getState(OID oid, JdbcCompiledQuery.MappingInfo mi, ResultSet rs) throws SQLException {
402         ClassMetaData cmd = mi.cmd;
403         if (mi.discrIndex != 0) {
404             Object JavaDoc classId = ((JdbcClass)cmd.storeClass).classIdCol.get(rs, mi.discrIndex);
405             if (rs.wasNull()) {
406                 throw BindingSupportImpl.getInstance().objectNotFound("No row for " +
407                         cmd.storeClass + " " + oid.toSString() + " OR "
408                         + ((JdbcClass)cmd.storeClass).classIdCol.name +
409                         " is null for row");
410             }
411             cmd = ((JdbcClass)cmd.storeClass).findClass(classId);
412             if (cmd == null) {
413                 throw BindingSupportImpl.getInstance().fatalDatastore(
414                         "Row for OID " + oid.toSString() +
415                         " is not in the heirachy starting at " +
416                         mi.cmd.storeClass +
417                         " (" + ((JdbcClass)mi.cmd.storeClass).classIdCol.name
418                         + " for row is " + classId + ")");
419             }
420         }
421         State state = cmd.createState();
422         oid.resolve(state);
423         ((JdbcState)state).copyPass1Fields(rs, mi.fields);
424         return state;
425     }
426
427     /**
428      * Get fetchAmount of data if possible. This assumes that the next cursor is
429      * correctly positioned.
430      */

431     public boolean addNextResult(ApplicationContext context,
432             QueryResultContainer results, int fetchAmount) {
433         try {
434             /**
435              * If this query contains 'out' params then we will return a Object[]
436              * with length the same as the amount of 'out' and 'out_cursor' params.
437              */

438             if (cq.isSqlQuery()) {
439                 if (cq.isUnique()) {
440                     int[] paramDir = cq.getParamDirection();
441                     Object JavaDoc[] outResult = new Object JavaDoc[cq.getOutParamCount()];
442                     int outCount = 0;
443                     for (int i = 0; i < paramDir.length; i++) {
444                         if (paramDir[i] == JdbcCompiledQuery.PARAM_OUT) {
445                             outResult[outCount++] = ((CallableStatement)ps).getObject(i + 1);
446                         } else if (paramDir[i] == JdbcCompiledQuery.PARAM_OUT_CURSOR) {
447                             //if there is mapping info available then map it to a class
448
//else create an object[] with all the cols read as getObject
449
JdbcCompiledQuery.MappingInfo mi = cq.getMappingInfo(rs);
450                             if (mi.isPkValid()) {
451                                 //if this can be resolved to oid-state
452
throw BindingSupportImpl.getInstance().unsupported();
453                             } else {
454                                 outResult[outCount++] = fetchAsObjects(mi);
455                             }
456                         }
457                     }
458                     results.addRow(outResult);
459                     return true;
460                 } else {
461                     JdbcCompiledQuery.MappingInfo mi = cq.getMappingInfo(rs);
462                     if (mi.isPkValid()) {
463                         return fetchManagedTypes(context, fetchAmount,
464                                 cq.getFetchGroup(), results);
465                     } else {
466                         int colCount = mi.colCount;
467                         do {
468                             Object JavaDoc[] oa = new Object JavaDoc[colCount];
469                             for (int j = 0; j < colCount; j++) {
470                                 oa[j] = rs.getObject(j + 1);
471
472                             }
473                             results.addRow(oa);
474                         } while (next(0));
475                         return true;
476                     }
477                 }
478             }
479
480
481             ProjectionQueryDecoder decoder = cq.getProjectionDecoder();
482             /**
483              * This is a normal query for a instance with no supplied projection
484              * or aggregate info('result' is null or equal to default).
485              *
486              * In this scenario nothing is added to the 'result' as the
487              * StatesReturned will already contain all the data.
488              */

489             if (decoder == null || decoder.isContainsThisOnly()) {
490                 return fetchManagedTypes(context, fetchAmount, cq.getFetchGroup(), results);
491             } else {
492                 if (decoder.containsThis()) {
493                     return doContainsThis(fetchAmount, cq.getFetchGroup(),
494                             decoder.getResultTypeArray(), decoder.getTypeCodes(),
495                             decoder.getFmdArray(),
496                             results);
497                 } else {
498                     return doResultOnly(fetchAmount,
499                             decoder.getResultTypeArray(),
500                             decoder.getFmdArray(), decoder.getTypeCodes(),
501                             results);
502                 }
503             }
504         } catch (SQLException e) {
505             throw sm.handleException(e);
506         }
507     }
508
509     private Object JavaDoc fetchAsObjects(JdbcCompiledQuery.MappingInfo mi)
510             throws SQLException {
511         int colCount = mi.colCount;
512         ArrayList rowData = new ArrayList();
513         do {
514             Object JavaDoc[] oa = new Object JavaDoc[colCount];
515             for (int j = 0; j < colCount; j++) {
516                 oa[j] = rs.getObject(j + 1);
517             }
518             rowData.add(oa);
519         } while (next(0));
520         return rowData;
521     }
522
523     /**
524      * Fill the results with managed instances(oid-state).
525      */

526     private boolean fetchManagedTypes(ApplicationContext context,
527             int fetchAmount, FetchGroup fg, QueryResultContainer results) {
528         OID currentOid = null;
529         OID prevOid = null;
530
531         if (fetchAmount == -1) {
532             //get all results
533
for(;;) {
534                 currentOid = getResultOID();
535
536                 //this is used to skip over same oids
537
if (!currentOid.equals(prevOid)) {
538                     sm.getState(context, currentOid, fg, this, results.container);
539                     results.addRow(currentOid);
540                 } else {
541                     fetchInfo.onNextRow = false;
542                 }
543                 prevOid = currentOid;
544                 if (fetchInfo.finished || !next(0)) break;
545             }
546             return true;
547         } else {
548             for (int i = 0; i < fetchAmount; i++) {
549                 if (fetchInfo.onNextRow && fetchInfo.nextOid != null) {
550                     currentOid = fetchInfo.nextOid;
551                     fetchInfo.onNextRow = false;
552                     fetchInfo.nextOid = null;
553                 } else {
554                     currentOid = getResultOID();
555                 }
556
557                 //this is used to skip over same oids
558
if (!currentOid.equals(prevOid)) {
559                     sm.getState(context, currentOid, fg, this, results.container);
560                     results.addRow(currentOid);
561                 } else {
562                     fetchInfo.onNextRow = false;
563                 }
564
565                 //if no more then return
566
if (fetchInfo.finished || !next(0)) return true;
567                 prevOid = currentOid;
568             }
569             return false;
570         }
571     }
572
573     private boolean doContainsThis(int fetchAmount,
574             FetchGroup fg, int[] typeArray, int[] typeCodes,
575             Object JavaDoc[] fmdArray,
576             QueryResultContainer results) throws SQLException {
577         //this rs column index to start reading
578
int rsIndex = 1 + cq.getSelectColumnCount();
579         if (fetchAmount == -1) {
580             do {
581                 sm.getState(getResultOID(), fg, results.container);
582                 getDataImp(typeArray, fmdArray, typeCodes, rsIndex, results);
583             } while (next(0));
584             return true;
585         } else {
586             for (int i = 0; i < fetchAmount; i++) {
587                 sm.getState(getResultOID(), fg, results.container);
588                 getDataImp(typeArray, fmdArray, typeCodes, rsIndex, results);
589                 if (!next(0)) return true;
590             }
591             return false;
592         }
593     }
594
595     private boolean doResultOnly(int fetchAmount, int[] typeArray,
596             Object JavaDoc[] fmdArray, int[] typeCodes,
597             QueryResultContainer results) throws SQLException {
598         //this rs column index to start reading
599
if (fetchAmount == -1) {
600             do {
601                 getDataImp(typeArray, fmdArray, typeCodes, 1, results);
602             } while (next(0));
603             return true;
604         } else {
605             for (int i = 0; i < fetchAmount; i++) {
606                 getDataImp(typeArray, fmdArray, typeCodes, 1, results);
607                 if (!next(0)) return true;
608             }
609             return false;
610         }
611     }
612
613     private void getDataImp(int[] typeArray, Object JavaDoc[] typeObjectArray,
614                             int[] typeCodes, int currentRsIndex,
615                             QueryResultContainer results)
616             throws SQLException {
617
618         Object JavaDoc[] resArray;
619         if (typeArray.length == 1) {
620             resArray = singleResult;
621         } else {
622             resArray = new Object JavaDoc[typeArray.length];
623         }
624
625         for (int i = 0; i < typeArray.length; i++) {
626             int type = typeArray[i];
627             switch (type) {
628                 case ProjectionQueryDecoder.TYPE_THIS: //this
629
resArray[i] = lastOID;
630                     break;
631                 case ProjectionQueryDecoder.TYPE_FIELD: //field
632
currentRsIndex = getFieldData(
633                             (FieldMetaData)typeObjectArray[i], rs,
634                             currentRsIndex, resArray, i);
635                     break;
636                 case ProjectionQueryDecoder.TYPE_AGGRETATE: //aggregate or computed field
637

638                     currentRsIndex = getFieldData(typeCodes[i], rs,
639                             currentRsIndex, resArray, i);
640
641
642                     break;
643                 case ProjectionQueryDecoder.TYPE_EXP:
644                     currentRsIndex = getFieldData(typeCodes[i], rs,
645                             currentRsIndex, resArray, i);
646                     break;
647                 case ProjectionQueryDecoder.TYPE_VAR:
648                     ClassMetaData cmd = (ClassMetaData) typeObjectArray[i];
649                     JdbcOID oid = (JdbcOID) cmd.createOID(false);
650                     if (oid.copyKeyFields(rs, currentRsIndex)) {
651                         resArray[i] = oid;
652                     } else {
653                         resArray[i] = null;
654                     }
655                     currentRsIndex += ((JdbcClass)cmd.storeClass).table.pk.length;
656                     break;
657                 default:
658                     throw BindingSupportImpl.getInstance().internal("");
659             }
660         }
661
662         if (typeArray.length == 1) {
663             results.addRow(resArray[0]);
664         } else {
665             results.addRow(resArray);
666         }
667     }
668
669     private int getFieldData(int typeCode, ResultSet rs, int rsIndex,
670             Object JavaDoc[] dataRow, int dataIndex) throws SQLException {
671         switch (typeCode) {
672             case MDStatics.BYTEW:
673                 dataRow[dataIndex] = new Byte JavaDoc(rs.getByte(rsIndex));
674                 break;
675             case MDStatics.SHORTW:
676                 dataRow[dataIndex] = new Short JavaDoc(rs.getShort(rsIndex));
677                 break;
678             case MDStatics.INTW:
679                 dataRow[dataIndex] = new Integer JavaDoc(rs.getInt(rsIndex));
680                 break;
681             case MDStatics.LONGW:
682                 dataRow[dataIndex] = new Long JavaDoc(rs.getLong(rsIndex));
683                 break;
684             case MDStatics.FLOATW:
685                 dataRow[dataIndex] = new Float JavaDoc(rs.getFloat(rsIndex));
686                 break;
687             case MDStatics.DOUBLEW:
688                 dataRow[dataIndex] = new Double JavaDoc(rs.getDouble(rsIndex));
689                 break;
690             case MDStatics.STRING:
691                 dataRow[dataIndex] = rs.getString(rsIndex);
692                 break;
693             case MDStatics.BIGDECIMAL:
694                 dataRow[dataIndex] = new BigDecimal JavaDoc(rs.getDouble(rsIndex));
695                 break;
696             case MDStatics.BIGINTEGER:
697                 dataRow[dataIndex] = new BigInteger JavaDoc(rs.getString(rsIndex));
698                 break;
699             default:
700                 throw BindingSupportImpl.getInstance().internal(
701                         "Unhandled type '" + typeCode + "'");
702         }
703         return rsIndex + 1;
704     }
705
706     public int getFieldData(FieldMetaData fmd, ResultSet rs, int firstCol,
707             Object JavaDoc[] dataRow, int dataIndex) throws SQLException {
708         JdbcField f = (JdbcField)fmd.storeField;
709         if (f instanceof JdbcSimpleField) {
710             JdbcColumn c = ((JdbcSimpleField)f).col;
711             if (Debug.DEBUG) {
712                 if (!cq.isProjectionQuery() && !c.name.toUpperCase().equals(
713                         rs.getMetaData().getColumnName(firstCol).toUpperCase())) {
714                     throw BindingSupportImpl.getInstance().internal(
715                             "Reading the wrong column: \nrs field = "
716                             + rs.getMetaData().getColumnName(firstCol) + "\nmetaData field = " + c.name);
717                 }
718             }
719             if (c.converter != null) {
720                 dataRow[dataIndex] = c.converter.get(rs, firstCol++, c );
721             } else {
722                 dataRow[dataIndex] = JdbcUtils.get(rs, firstCol++, c.javaTypeCode,
723                         c.scale );
724                 if (rs.wasNull()) {
725                     dataRow[dataIndex] = null;
726                 }
727             }
728         } else if (f instanceof JdbcRefField) {
729             JdbcRefField rf = (JdbcRefField)f;
730             JdbcOID oid = (JdbcOID)rf.targetClass.createOID(false);
731             if (oid.copyKeyFields(rs, firstCol)) {
732                 dataRow[dataIndex] = oid;
733             } else {
734                 dataRow[dataIndex] = null;
735             }
736             firstCol += rf.cols.length;
737         } else if (f instanceof JdbcPolyRefField) {
738             dataRow[dataIndex] = JdbcGenericState.getPolyRefOID(f, rs, firstCol);
739             firstCol += ((JdbcPolyRefField)f).cols.length;
740         } else {
741             throw BindingSupportImpl.getInstance().internal("not implemented");
742         }
743         return firstCol;
744     }
745
746     public void doParallelFetch(boolean forUpdate, StateContainer container) {
747         this.forUpdate = forUpdate;
748         try {
749             if (cq.isParColFetchEnabled()) {
750                 processParallelFetch(cq.fgDs.getJoinStruct(), 1, true, null, container);
751             }
752         } catch (Exception JavaDoc e) {
753             throw sm.handleException(e);
754         }
755     }
756
757     private JdbcJDOQLCompiler getQComp() {
758         if (qCompiler == null) {
759             qCompiler = new JdbcJDOQLCompiler(sm);
760         } else {
761             qCompiler.reinit();
762         }
763         return qCompiler;
764     }
765
766     /**
767      * Execute a query for a collection.
768      */

769     private void exectureQ(SqlStruct sqlStruct,
770             ColFieldHolder colHolder) throws SQLException {
771         sqlStruct.updateSql(sm.getSqlDriver(), params, forUpdate, false);
772         String JavaDoc sql = sqlStruct.getSql();
773         PreparedStatement p2Ps = con.prepareStatement(sql);
774         sqlStruct.setParamsOnPS(jmd, sm.getSqlDriver(), p2Ps, params, sql);
775         colHolder.ps = p2Ps;
776         colHolder.rs = p2Ps.executeQuery();
777     }
778
779     private SqlStruct createSqlStruct(ColFieldHolder colHolder) {
780         final SqlStruct sqlStruct = new SqlStruct();
781         sqlStruct.jdoqlFilter = "PQ: " + cq.getQueryDetails().getFilter();
782
783         JdbcJDOQLCompiler qComp = getQComp();
784         SelectExp root = (SelectExp)qComp.compileParallelFetch(
785                 cq.getQueryDetails());
786         SelectExp se = colHolder.createSE(sm, root);
787
788         //add the root table pk to the front of the select list
789
SqlExp e = JdbcColumn.toSqlExp(((JdbcClass)cmd.storeClass).table.pk, root,
790                 root.selectList);
791         root.selectList = e;
792
793         root.appendOrderByExp(se.orderByList);
794
795         JdbcJDOQLCompiler.doFinalSql(sqlStruct, root, sm.getSqlDriver());
796         JdbcJDOQLCompiler.compileParams(qComp.getQParser(), sqlStruct);
797         return sqlStruct;
798     }
799
800     /**
801      * This must be called on a root JoinStructure.
802      */

803     private void processParallelFetch(JoinStructure rootJs, int level, boolean valueJoin,
804             ColFieldHolder parent, StateContainer container) throws SQLException {
805         if (rootJs == null) return;
806         if (!rootJs.rootJoinStructure) throw BindingSupportImpl.getInstance().internal("");
807         rootJs.finish();
808
809         List l = rootJs.colJoinStructs;
810         if (l == null) return;
811         for (int i = 0; i < l.size(); i++) {
812             JoinStructure js2 = (JoinStructure)l.get(i);
813             ColFHKey key = new ColFHKey(level, valueJoin, js2);
814             ColFieldHolder colFHolder = (ColFieldHolder)colHMap.get(key);
815             if (colFHolder == null) {
816                 colFHolder = new ColFieldHolder(parent, valueJoin, js2);
817                 colHMap.put(key, colFHolder);
818
819                 //Leave this here -Jaco-
820
// SqlStruct sqlStr = compiledQuery.get(key);
821
// try {
822
// if (sqlStr == null) {
823
// sqlStr = createSqlStruct(colFHolder, js2);
824
// compiledQuery.add(key, sqlStr);
825
// }
826
// exectureQ(sqlStr, js2, colFHolder);
827
// } finally {
828
// if (sqlStr != null) sqlStr.returnToPool();
829
// }
830

831
832                 if (level == 1 && js2.parent == rootJs
833                         && js2.fgField == rootJs.fetchGroup.crossJoinedCollectionField
834                         && cq.isCrossJoinAllowed()) {
835                     //crossjoined
836
colFHolder.valueJs = cq.fgDs.valueJs;
837 // colFHolder.returnState = JdbcCollectionField.STATUS_CLOSED;
838
colFHolder.returnState = JdbcCollectionField.STATUS_VALID_ROWS
839                                     + JdbcCollectionField.STATUS_DATA_ADDED;
840                     colFHolder.crossJoinedField = true;
841                 } else {
842                     SqlStruct sqlStr = cq.get(key);
843                     if (sqlStr == null) {
844                         sqlStr = createSqlStruct(colFHolder);
845                         exectureQ(sqlStr, colFHolder);
846                         cq.add(key, sqlStr);
847                     } else {
848                         synchronized (sqlStr) {
849                             exectureQ(sqlStr, colFHolder);
850                         }
851                     }
852                 }
853             }
854
855             if ((colFHolder.returnState & JdbcCollectionField.STATUS_CLOSED)
856                     == JdbcCollectionField.STATUS_CLOSED) {
857 // System.out.println("--- CONTINUE ON STATUS_CLOSED");
858
continue;
859             }
860             
861             //fetch the coll
862
if (!colFHolder.crossJoinedField) {
863                 colFHolder.returnState = ((JdbcCollectionField)js2.fgField.fmd.storeField).
864                         fetchWithFilter(sm, container, js2.fgField,
865                                 colFHolder.rs, forUpdate, getLastOID(),
866                                 colFHolder.lastOIDs, cmd, colFHolder);
867             }
868
869             if ((colFHolder.returnState & JdbcCollectionField.STATUS_VALID_ROWS)
870                     != JdbcCollectionField.STATUS_VALID_ROWS) {
871 // System.out.println("--- CONTINUE ON STATUS_VALID_ROWS");
872
continue;
873             }
874
875             if ((colFHolder.returnState & JdbcCollectionField.STATUS_DATA_ADDED)
876                     != JdbcCollectionField.STATUS_DATA_ADDED) {
877 // System.out.println("--- CONTINUE ON STATUS_DATA_ADDED");
878
continue;
879             }
880
881             //for a normal collection
882
if (js2.fgField.nextFetchGroup != null) {
883 // System.out.println("--- DOING VALUE FIELD");
884
processParallelFetch(colFHolder.valueJs, level + 1, true, colFHolder, container);
885             }
886
887             //for a map with pc keys
888
if (js2.fgField.nextKeyFetchGroup != null) {
889 // System.out.println("--- DOING KEY FIELD");
890
processParallelFetch(colFHolder.keyJs, level + 1, false, colFHolder, container);
891             }
892         }
893     }
894
895     /**
896      * If the cache can be checked for results represented by this query.
897      */

898     public boolean isCachedResultsOk() {
899         return isCacheble && status == STATUS_NOT_STARTED;
900     }
901
902     public void setQResult(QueryResultContainer qContainer) {
903         if (Debug.DEBUG) {
904             if (status != STATUS_NOT_STARTED) {
905                 throw BindingSupportImpl.getInstance().internal(
906                         "query already started");
907             }
908         }
909         status = STATUS_BUSY;
910         if (cq.isRandomAccess()) {
911             if (!absolute(0)) {
912                 status = STATUS_FINISHED;
913                 qContainer.qFinished = true;
914             }
915         } else {
916             if (!next(0)) {
917                 status = STATUS_FINISHED;
918                 qContainer.qFinished = true;
919             }
920         }
921     }
922
923     public boolean isNotStarted() {
924         return status == STATUS_NOT_STARTED;
925     }
926
927     public void getAbsolute(ApplicationContext context,
928             QueryResultContainer qContainer, int index, int fetchAmount) {
929         if (isNotStarted()) {
930             executeQueryImp();
931             setQResult(qContainer);
932         }
933
934         resetRelativeResultCount();
935         if (Debug.DEBUG) {
936             if (!isRandomAccess()) {
937                 throw BindingSupportImpl.getInstance().internal("getAbsolute may only " +
938                         "be called on randomAccess queries");
939             }
940         }
941         if (absolute(index)) {
942             if (addNextResult(context, qContainer, fetchAmount)) {
943                 status = STATUS_FINISHED;
944             }
945             doParallelFetch(forUpdate, qContainer.container);
946         }
947
948         if (status == STATUS_FINISHED) {
949             qContainer.qFinished = true;
950         }
951     }
952
953     /**
954      * Get the next batch of results. Returns true if there are no more
955      * (i.e. this query should be closed).
956      */

957     public boolean nextBatch(ApplicationContext context, int skipAmount,
958             QueryResultContainer qContainer) {
959         prepare(qContainer);
960         updateCacheble();
961
962
963         //get the rs at the correct spot
964
if (skipAmount != 0) {
965             setNonCacheble();
966             if (!next(skipAmount - 1)) {
967                 status = STATUS_FINISHED;
968             }
969         }
970
971         if (status != STATUS_FINISHED) {
972             if (addNextResult(context, qContainer, cq.getQueryResultBatchSize())) {
973                 status = STATUS_FINISHED;
974             }
975
976             if (qRCache != null) {
977                 qContainer.addResultsTo(qRCache,
978                         cq.isCopyResultsForCache());
979             }
980         }
981         doParallelFetch(forUpdate, qContainer.container);
982         if (status == STATUS_FINISHED) {
983             qContainer.qFinished = true;
984         }
985         return status == STATUS_FINISHED;
986     }
987
988     private void prepare(QueryResultContainer qContainer) {
989         if (isNotStarted()) {
990             updateCacheble();
991             executeQueryImp();
992             setQResult(qContainer);
993             if (isCacheble()) {
994                 qRCache = new CachedQueryResult();
995             }
996         } else {
997             resetRelativeResultCount();
998         }
999     }
1000
1001    /**
1002     * This is called to recheck if this results is still cacheble.
1003     */

1004    public void updateCacheble() {
1005        if (isCacheble) {
1006            if (sm.isActive() && !sm.isOptimistic() || sm.isFlushed()
1007                    || !sm.getCache().isQueryCacheEnabled()) {
1008                setNonCacheble();
1009                return;
1010            }
1011        }
1012    }
1013
1014    public boolean isFinished() {
1015        return status == STATUS_FINISHED;
1016    }
1017
1018    public boolean isCacheble() {
1019        return isCacheble;
1020    }
1021
1022    public QueryDetails getQueryDetails() {
1023        return cq.getQueryDetails();
1024    }
1025
1026    private void executeQueryImp() {
1027        executePrepare(sm.isForUpdate(), sm.getSqlDriver());
1028        executeActual();
1029    }
1030
1031    private void executePrepare(boolean forUpdate, SqlDriver sqlDriver) {
1032        JdbcCompiledQuery cq = getJdbcCompiledQuery();
1033        ClassMetaData cmd = cq.getCmd();
1034        boolean randomAccess = cq.isRandomAccess();
1035        if (randomAccess && !sqlDriver.isScrollableResultSetSupported()) {
1036            throw BindingSupportImpl.getInstance().datastore("Random access not supported for " +
1037                    sqlDriver.getName() + " using JDBC driver " +
1038                    sm.getJdbcConnectionSource().getDriverName());
1039        }
1040        init(cmd, randomAccess);
1041        Object JavaDoc[] params = getParams();
1042        boolean ok = false;
1043        try {
1044            con = sm.con();
1045            if (isEJBQLHack()) {
1046
1047                ((JdbcQueryResultEJBQL)this).executePrepareHack(params);
1048
1049            } else if (!cq.isSqlQuery()) {
1050                // this sync block can be removed when compiledQuery is no longer shared
1051
SqlStruct sqlStr = cq.getSqlStruct();
1052                synchronized (sqlStr) {
1053                    sqlStr.updateSql(sqlDriver, params, forUpdate,
1054                            false);
1055                    String JavaDoc sql = sql = cq.getSql();
1056                    if (randomAccess) {
1057                        ps = con.prepareStatement(sql,
1058                                ResultSet.TYPE_SCROLL_INSENSITIVE,
1059                                ResultSet.CONCUR_READ_ONLY);
1060                    } else {
1061                        ps = con.prepareStatement(sql);
1062                    }
1063                    sqlStr.setParamsOnPS(jmd, sqlDriver, ps,
1064                            params, sql);
1065                }
1066
1067            } else if (cq.isStoredProc()) {
1068                CallableStatement cs = con.prepareCall(
1069                        cq.getQueryDetails().getFilter());
1070                ps = cs;
1071                int[] sqlTypes = cq.getSqlTypes();
1072                int[] paramDir = cq.getParamDirection();
1073
1074                final int count = sqlTypes.length;
1075                for (int i = 0; i < count; i++) {
1076                    if (paramDir[i] == JdbcCompiledQuery.PARAM_IN) {
1077                        cs.setObject(i + 1, params[i], sqlTypes[i]);
1078                    } else if (paramDir[i] == JdbcCompiledQuery.PARAM_OUT) {
1079                        cs.registerOutParameter(i + 1, sqlTypes[i]);
1080                    } else if (paramDir[i] == JdbcCompiledQuery.PARAM_OUT_CURSOR) {
1081                        cs.registerOutParameter(i + 1, -10);
1082                    }
1083                }
1084
1085            } else if (cq.isDirectSql()) {
1086                ps = con.prepareStatement(cq.getQueryDetails().getFilter());
1087                int[] sqlTypes = cq.getSqlTypes();
1088                final int count = sqlTypes.length;
1089                for (int i = 0; i < count; i++) {
1090                    ps.setObject(i + 1, params[i], sqlTypes[i]);
1091                }
1092            } else {
1093                throw BindingSupportImpl.getInstance().internal("Undefined query type");
1094            }
1095
1096            int maxRows = cq.getMaxRows();
1097            ps.setMaxRows(maxRows < 0 ? 0 : maxRows);
1098            ok = true;
1099        } catch (SQLException x) {
1100            throw sm.handleException(x);
1101        } finally {
1102            if (!ok) {
1103                try {
1104                    close();
1105                } catch (Exception JavaDoc x) {
1106                    // ignore as we are already in an exception
1107
}
1108            }
1109        }
1110    }
1111
1112    private void executeActual() {
1113        JdbcCompiledQuery cq = getJdbcCompiledQuery();
1114
1115        int maxRows = cq.getMaxRows();
1116
1117        boolean ok = false;
1118        try {
1119            if (sm.getSqlDriver().isFetchSizeSupported()) {
1120                int fetchSize = cq.getQueryResultBatchSize();
1121                if (maxRows > 0 && fetchSize > maxRows) fetchSize = maxRows;
1122                if (fetchSize > 0) ps.setFetchSize(fetchSize);
1123            }
1124
1125            try {
1126                if (isEJBQLHack()) {
1127
1128                    ((JdbcQueryResultEJBQL)this).executeActualHack();
1129
1130                } else if (cq.isSqlQuery()) {
1131                    if (cq.isStoredProc() && sm.getSqlDriver().isOracleStoreProcs()) {
1132                        ps.executeUpdate();
1133                        int[] paramDir = cq.getParamDirection();
1134                        boolean cursorSet = false;
1135                        QueryDetails queryDetails = cq.getQueryDetails();
1136                        final int count = queryDetails.getParamCount();
1137                        for (int i = 0; i < count; i++) {
1138                            if (paramDir[i] == JdbcCompiledQuery.PARAM_OUT_CURSOR) {
1139                                if (cursorSet) {
1140                                    throw BindingSupportImpl.getInstance().invalidOperation(
1141                                            "Query may have only one OUT parameter");
1142                                }
1143                                rs = (ResultSet)((CallableStatement)
1144                                        ps).getObject(i + 1);
1145                                cq.getMappingInfo(rs);
1146                                cursorSet = true;
1147                            }
1148                        }
1149                        if (!cursorSet) {
1150                            rs = (ResultSet)((CallableStatement)
1151                                    ps).getObject(count + 1);
1152                            cq.getMappingInfo(rs);
1153                        }
1154                    } else {
1155                        rs = ps.executeQuery();
1156                        cq.getMappingInfo(rs);
1157                    }
1158                    /**
1159                     * Check for a valid pk mapping
1160                     */

1161                    if (!cq.getMappingInfo(rs).isPkValid()
1162                            && cq.getQueryDetails().getCandidateClass() != null) {
1163                        throw BindingSupportImpl.getInstance().invalidOperation(
1164                                "Candidate class '"
1165                                + cq.getQueryDetails().getCandidateClass().getName()
1166                                + "' was specified, "
1167                                + "but the ResultSet does not contain any/all of the pk columns.");
1168                    }
1169                } else {
1170                    rs = ps.executeQuery();
1171                }
1172            } catch (SQLException e) {
1173                throw sm.handleException("Query failed: " + JdbcUtils.toString(
1174                        e) + "\n" +
1175                        JdbcUtils.getPreparedStatementInfo(sql, ps),
1176                        e);
1177            }
1178
1179            ok = true;
1180        } catch (SQLException e) {
1181            sm.handleException(e);
1182        } finally {
1183            if (!ok) {
1184                try {
1185                    close();
1186                } catch (Exception JavaDoc x) {
1187                    // ignore as we are already in an exception
1188
}
1189            }
1190        }
1191    }
1192
1193    /**
1194     * If at any stage it is detected that the results may not be cached this
1195     * this method is called.
1196     */

1197    public void setNonCacheble() {
1198        isCacheble = false;
1199        qRCache = null;
1200    }
1201
1202    /**
1203     * Todo get rid of this horrible hack when we refactor all the query stuff
1204     */

1205    public boolean isEJBQLHack() {
1206        return false;
1207    }
1208
1209    public void getAllResults(ApplicationContext context,
1210            QueryResultContainer container, boolean forUpdate) {
1211        try {
1212            executeQueryImp();
1213            resetRelativeResultCount();
1214
1215            if (next(0)) {
1216                addNextResult(context, container, -1);
1217            }
1218            doParallelFetch(forUpdate, container.container);
1219        } finally {
1220            close();
1221        }
1222    }
1223
1224    /**
1225     * This is a key for a {@link ColFieldHolder}.
1226     */

1227    public class ColFHKey {
1228
1229        private int level;
1230        private boolean valueJoin;
1231        private JoinStructure js;
1232
1233        public ColFHKey(int level, boolean valueJoin, JoinStructure js) {
1234            this.level = level;
1235            this.valueJoin = valueJoin;
1236            this.js = js;
1237        }
1238
1239        public boolean equals(Object JavaDoc o) {
1240            if (this == o) return true;
1241            if (!(o instanceof ColFHKey)) return false;
1242
1243            final ColFHKey colFHKey = (ColFHKey)o;
1244
1245            if (level != colFHKey.level) return false;
1246            if (valueJoin != colFHKey.valueJoin) return false;
1247            if (!js.equals(colFHKey.js)) return false;
1248
1249            return true;
1250        }
1251
1252        public int hashCode() {
1253            int result;
1254            result = level;
1255            result = 29 * result + (valueJoin ? 1 : 0);
1256            result = 29 * result + js.hashCode();
1257            return result;
1258        }
1259
1260        public void dump() {
1261            System.out.println("\n\n --JdbcQueryResult$ColFHKey.dump");
1262            System.out.println("level = " + level);
1263            System.out.println("valueJoin = " + valueJoin);
1264            System.out.println("js = " + js);
1265        }
1266
1267    }
1268}
1269
1270
Popular Tags