001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdbc;
012:
013: import com.versant.core.jdbc.query.SqlStruct;
014: import com.versant.core.jdbc.query.JdbcJDOQLCompiler;
015: import com.versant.core.jdbc.metadata.*;
016: import com.versant.core.jdbc.sql.exp.SelectExp;
017: import com.versant.core.jdbc.sql.exp.SqlExp;
018: import com.versant.core.metadata.ClassMetaData;
019: import com.versant.core.metadata.ModelMetaData;
020:
021: import java.sql.SQLException;
022: import java.sql.PreparedStatement;
023: import java.sql.Connection;
024: import java.sql.ResultSet;
025: import java.util.*;
026:
027: import com.versant.core.common.*;
028: import com.versant.core.server.StateContainer;
029:
030: /**
031: * This helps to keep track of a parallel collection fetch operation.
032: */
033: public final class ParColFetchUtil {
034:
035: private final JdbcStorageManager sm;
036: private final boolean forUpdate;
037: private final StateContainer container;
038: private final ClassMetaData cmd;
039: private final ModelMetaData jmd;
040: private JdbcOID lastOID;
041:
042: public ParColFetchUtil(JdbcStorageManager sm, boolean forUpdate,
043: StateContainer container, ClassMetaData cmd, JdbcOID lastOID) {
044: this .sm = sm;
045: this .forUpdate = forUpdate;
046: this .container = container;
047: this .cmd = cmd;
048: this .lastOID = lastOID;
049: jmd = sm.getJmd();
050: }
051:
052: /**
053: * A map of {@link ColFHKey} to {@link com.versant.core.jdbc.ColFieldHolder}. A ColFieldHolder is
054: * added to the map once some or all of its results have been processed.
055: */
056: private Map colHMap = new HashMap();
057:
058: private Connection con() {
059: return sm.con();
060: }
061:
062: /**
063: * Execute a query for a collection.
064: */
065: private void exectureQ(SqlStruct sqlStruct, ColFieldHolder colHolder)
066: throws SQLException {
067: PreparedStatement p2Ps = con().prepareStatement(
068: sqlStruct.getSql());
069: lastOID.setParams(p2Ps, 1);
070: colHolder.ps = p2Ps;
071: colHolder.rs = p2Ps.executeQuery();
072: }
073:
074: private SqlStruct createSqlStruct(ColFieldHolder colHolder,
075: JoinStructure ds) {
076: final SqlStruct sqlStruct = new SqlStruct();
077:
078: // generate a join query to get the group
079: SelectExp root = new SelectExp();
080: JdbcClass jdbcClass = (JdbcClass) cmd.storeClass;
081: root.table = jdbcClass.table;
082: root.whereExp = jdbcClass.table.createPkEqualsParamExp(root);
083:
084: SelectExp se = colHolder.createSE(sm, root);
085:
086: // add the root table pk to the front of the select list
087: SqlExp e = JdbcColumn.toSqlExp(
088: ((JdbcClass) cmd.storeClass).table.pk, root,
089: root.selectList);
090: root.selectList = e;
091:
092: root.appendOrderByExp(se.orderByList);
093: JdbcJDOQLCompiler
094: .doFinalSql(sqlStruct, root, sm.getSqlDriver());
095:
096: return sqlStruct;
097: }
098:
099: /**
100: * This must be called on a root JoinStructure.
101: */
102: public void processParallelFetch(JoinStructure rootJs, int level,
103: boolean valueJoin, ColFieldHolder parent, OID oid,
104: State state, boolean doCrossJoin, ResultSet masterRs,
105: int colIndex, FgDs fgDs) throws SQLException {
106: if (rootJs == null)
107: return;
108: rootJs.finish();
109: if (!rootJs.rootJoinStructure) {
110: throw BindingSupportImpl.getInstance().internal("");
111: }
112:
113: List l = rootJs.colJoinStructs;
114: if (l == null)
115: return;
116: for (int i = 0; i < l.size(); i++) {
117: JoinStructure js2 = (JoinStructure) l.get(i);
118: ColFHKey key = new ColFHKey(level, valueJoin, js2, parent);
119: ColFieldHolder colFHolder = (ColFieldHolder) colHMap
120: .get(key);
121: if (colFHolder == null) {
122: colFHolder = new ColFieldHolder(parent, valueJoin, js2);
123: colHMap.put(key, colFHolder);
124: if (level == 1) {
125: if (js2.parent == rootJs
126: && js2.fgField == rootJs.fetchGroup.crossJoinedCollectionField
127: && doCrossJoin) {
128: int size = ((JdbcCollectionField) js2.fgField.fmd.storeField)
129: .fetchFrom(masterRs, oid, state,
130: js2.fgField, forUpdate,
131: container, false, colIndex,
132: new FetchInfo(), sm);
133:
134: colFHolder.valueJs = fgDs.valueJs;
135: colFHolder.keyJs = fgDs.keyJs;
136:
137: if (size == 0) {
138: colFHolder.returnState = JdbcCollectionField.STATUS_CLOSED;
139: } else {
140: colFHolder.returnState = JdbcCollectionField.STATUS_VALID_ROWS
141: + JdbcCollectionField.STATUS_DATA_ADDED;
142: }
143: } else {
144: //find the oid-state pair for the collections owner.
145: State ss = state;
146: OID refOID = oid;
147:
148: //if there is any intermediate refs that lead to the collection
149: if (js2.parent != rootJs) {
150: ArrayList jslist = new ArrayList();
151: JoinStructure js = js2.parent;
152: for (;;) {
153: jslist.add(js);
154: if (js.parent == rootJs)
155: break;
156: js = js.parent;
157: }
158:
159: for (int k = jslist.size() - 1; k >= 0; k--) {
160: JoinStructure tjs = (JoinStructure) jslist
161: .get(k);
162: if (Debug.DEBUG) {
163: if (!tjs.isRefField()) {
164: throw BindingSupportImpl
165: .getInstance()
166: .internal("");
167: }
168: }
169:
170: if (ss
171: .getClassMetaData(jmd)
172: .isAncestorOrSelf(
173: tjs.fgField.fmd.classMetaData)) {
174: refOID = (OID) ss
175: .getInternalObjectField(tjs.fgField.fmd.stateFieldNo);
176: if (refOID != null) {
177: ss = container.get(refOID);
178: } else {
179: ss = null;
180: break;
181: }
182: } else {
183: ss = null;
184: break;
185: }
186: }
187:
188: if (ss != null
189: && !ss
190: .getClassMetaData(jmd)
191: .isAncestorOrSelf(
192: js2.fgField.fmd.classMetaData)) {
193: ss = null;
194: }
195: } else {
196: if (ss
197: .getClassMetaData(jmd)
198: .isAncestorOrSelf(
199: js2.fgField.fmd.classMetaData)) {
200: //this is a sub class field so ignore
201: ss = state;
202: } else {
203: ss = null;
204: }
205: }
206:
207: int amount = 0;
208: if (refOID != null && ss != null) {
209: amount = sm.fetchPass2Field(refOID, ss,
210: colFHolder.js.fgField, forUpdate,
211: container, false, colFHolder);
212: }
213: if (amount <= 0) {
214: colFHolder.returnState = JdbcCollectionField.STATUS_CLOSED;
215: } else {
216: colFHolder.returnState = JdbcCollectionField.STATUS_VALID_ROWS
217: + JdbcCollectionField.STATUS_DATA_ADDED;
218: }
219: }
220: } else {
221: SqlStruct sqlStr = createSqlStruct(colFHolder, js2);
222: exectureQ(sqlStr, colFHolder);
223: }
224: }
225:
226: if ((colFHolder.returnState & JdbcCollectionField.STATUS_CLOSED) == JdbcCollectionField.STATUS_CLOSED) {
227: // System.out.println("--- CONTINUE ON STATUS_CLOSED");
228: continue;
229: }
230:
231: //fetch the coll
232: if (level > 1) {
233: colFHolder.returnState = ((JdbcCollectionField) js2.fgField.fmd.storeField)
234: .fetchWithFilter(sm, container, js2.fgField,
235: colFHolder.rs, forUpdate, lastOID,
236: colFHolder.lastOIDs, cmd, colFHolder);
237: }
238:
239: if ((colFHolder.returnState & JdbcCollectionField.STATUS_VALID_ROWS) != JdbcCollectionField.STATUS_VALID_ROWS) {
240: // System.out.println("--- CONTINUE ON STATUS_VALID_ROWS");
241: continue;
242: }
243:
244: if ((colFHolder.returnState & JdbcCollectionField.STATUS_DATA_ADDED) != JdbcCollectionField.STATUS_DATA_ADDED) {
245: // System.out.println("--- CONTINUE ON STATUS_DATA_ADDED");
246: continue;
247: }
248:
249: //for a normal collection
250: if (js2.fgField.nextFetchGroup != null) {
251: // System.out.println("--- DOING VALUE FIELD");
252: processParallelFetch(colFHolder.valueJs, level + 1,
253: true, colFHolder, oid, state, false, null,
254: colIndex, null);
255: }
256:
257: //for a map with pc keys
258: if (js2.fgField.nextKeyFetchGroup != null) {
259: // System.out.println("--- DOING KEY FIELD");
260: processParallelFetch(colFHolder.keyJs, level + 1,
261: false, colFHolder, oid, state, false, null,
262: colIndex, null);
263: }
264: }
265: }
266:
267: /**
268: * Close these results.
269: */
270: public void close() {
271: if (colHMap != null) {
272: Collection col = colHMap.values();
273: for (Iterator iterator = col.iterator(); iterator.hasNext();) {
274: ColFieldHolder holder = (ColFieldHolder) iterator
275: .next();
276: if (holder != null) {
277: holder.close();
278: }
279: }
280: colHMap.clear();
281: }
282: }
283:
284: /**
285: * This is a key for a {@link ColFieldHolder}.
286: */
287: public static class ColFHKey {
288:
289: private int level;
290: private boolean valueJoin;
291: private JoinStructure js;
292: private ColFieldHolder parent;
293:
294: public ColFHKey(int level, boolean valueJoin, JoinStructure js,
295: ColFieldHolder parent) {
296: this .level = level;
297: this .valueJoin = valueJoin;
298: this .js = js;
299: this .parent = parent;
300: }
301:
302: public boolean equals(Object o) {
303: if (this == o)
304: return true;
305: if (!(o instanceof ColFHKey))
306: return false;
307:
308: final ColFHKey colFHKey = (ColFHKey) o;
309:
310: if (parent != colFHKey.parent)
311: return false;
312: if (level != colFHKey.level)
313: return false;
314: if (valueJoin != colFHKey.valueJoin)
315: return false;
316: if (!js.equals(colFHKey.js))
317: return false;
318:
319: return true;
320: }
321:
322: public int hashCode() {
323: int result;
324: result = level;
325: result = 29 * result + (valueJoin ? 1 : 0);
326: result = 29 * result + js.hashCode();
327: return result;
328: }
329:
330: }
331: }
|