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.common.OID;
014: import com.versant.core.metadata.FetchGroupField;
015: import com.versant.core.jdbc.metadata.JdbcCollectionField;
016: import com.versant.core.jdbc.metadata.JdbcField;
017: import com.versant.core.jdbc.metadata.JdbcRefField;
018: import com.versant.core.jdbc.metadata.JdbcClass;
019: import com.versant.core.jdbc.sql.exp.SelectExp;
020:
021: import java.sql.PreparedStatement;
022: import java.sql.ResultSet;
023: import java.sql.SQLException;
024:
025: /**
026: * This is a data structure to hold the ResultSet and the PreparedStatement
027: * for a Collection field that is being iterated over.
028: * <p/>
029: * This struct also has a ref to a parent. This creates a structrure whereby
030: * the child knows about the parent but the parent not about the child.
031: * To create the exp to query for this collection we follow the parent links all
032: * the way to the top and start from there.
033: */
034: public class ColFieldHolder {
035:
036: /**
037: * The following fields are used when the query has been executed to maintain
038: * the state.
039: */
040: public ResultSet rs;
041: public PreparedStatement ps;
042: public OID[] lastOIDs = new OID[2];
043: public int returnState;
044:
045: /**
046: * The parent.
047: */
048: public ColFieldHolder parent;
049: /**
050: *
051: */
052: public JoinStructure js;
053:
054: public JoinStructure valueJs;
055:
056: public JoinStructure keyJs;
057: /**
058: * If this collection is from the parents key of value field.
059: * This can only be false if the parent is a Map with a pc key.
060: * This field will not change once set.
061: */
062: public final boolean valueJoinToParent;
063: public boolean crossJoinedField;
064:
065: public ColFieldHolder(ColFieldHolder parent,
066: boolean valueJoinToParent, JoinStructure js) {
067: this .parent = parent;
068: this .valueJoinToParent = valueJoinToParent;
069: this .js = js;
070: }
071:
072: public void close() {
073: if (rs != null) {
074: try {
075: rs.close();
076: } catch (SQLException e) {
077: //ignore
078: }
079: }
080:
081: if (ps != null) {
082: try {
083: ps.close();
084: } catch (SQLException e) {
085: //ignore
086: }
087: }
088: }
089:
090: private SelectExp createSelectExpImp(JdbcStorageManager sm,
091: FetchGroupField fgField, SelectExp inSe, boolean valueJoin,
092: boolean justJoin, SelectExp root, boolean addRootJoin) {
093: final JdbcField jdbcField = (JdbcField) fgField.fmd.storeField;
094: if (jdbcField instanceof JdbcRefField) {
095: JdbcRefField refField = (JdbcRefField) jdbcField;
096:
097: SelectExp se = new SelectExp();
098: se.table = ((JdbcClass) refField.targetClass.storeClass).table;
099: se.jdbcField = refField;
100: inSe.addJoin(refField.cols, se.table.pk, se);
101:
102: inSe = se;
103: } else {
104: if (justJoin) {
105: inSe = ((JdbcCollectionField) jdbcField)
106: .getSelectFilterJoinExp(valueJoin, inSe, root,
107: addRootJoin);
108: } else {
109: final JdbcCollectionField jdbcCollectionField = (JdbcCollectionField) fgField.fmd.storeField;
110: SelectExp se = jdbcCollectionField.getSelectFilterExp(
111: sm, fgField, this );
112: inSe.addJoin(inSe.table.pk,
113: jdbcCollectionField.ourPkColumns, se);
114: return se;
115: }
116: }
117: return inSe;
118: }
119:
120: /**
121: * Must pass up to the root parent and start creating the exp from there.
122: *
123: * @param sm
124: * @param joinToExp The current most right side of the exp tree that we must
125: * join to.
126: * @param rootExp The exp where the query originated from. This is left
127: * unchanged and passed up the chain.
128: * @param valueJoin This is passed from our child to specify how to join to
129: */
130: private SelectExp createSEImp(JdbcStorageManager sm,
131: SelectExp joinToExp, SelectExp rootExp, boolean valueJoin,
132: boolean addRootJoin) {
133: //if there is a parent then let it do its joins first
134: if (parent != null) {
135: joinToExp = parent.createSEImp(sm, joinToExp, rootExp,
136: valueJoinToParent, false);
137: }
138:
139: //add our joins
140: return appendJoins(sm, joinToExp, valueJoin, true, rootExp,
141: addRootJoin);
142: }
143:
144: /**
145: * Join to the supplied selectExp. Join ourselve to the right of the exp.
146: *
147: * @return return the exp tree with the newly create exp on the right.
148: */
149: private SelectExp appendJoins(JdbcStorageManager sm,
150: SelectExp joinToExp, boolean valueJoin, boolean justJoin,
151: SelectExp root, boolean addRootJoin) {
152: FetchGroupField[] fgFields = js.fgFields;
153: for (int i = 0; i < fgFields.length; i++) {
154: joinToExp = createSelectExpImp(sm, fgFields[i], joinToExp,
155: valueJoin, justJoin, root, addRootJoin);
156: }
157: return joinToExp;
158: }
159:
160: /**
161: * Create a SelectExp for this instance using the supplied root exp.
162: */
163: public SelectExp createSE(JdbcStorageManager sm, SelectExp root) {
164: SelectExp rhSe = root;
165: if (parent != null) {
166: rhSe = parent.createSEImp(sm, rhSe, root,
167: valueJoinToParent, true);
168: }
169:
170: return appendJoins(sm, rhSe, true, false, root, false);
171: }
172:
173: public String toString() {
174: return this .getClass().getName() + "@"
175: + System.identityHashCode(this ) + "field "
176: + js.fgField.fmd.name + " --> " + parent;
177:
178: }
179: }
|