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.metadata;
012:
013: import com.versant.core.metadata.ClassMetaData;
014: import com.versant.core.metadata.FieldMetaData;
015: import com.versant.core.common.OID;
016: import com.versant.core.jdbc.sql.JdbcNameGenerator;
017: import com.versant.core.jdbc.sql.SqlDriver;
018: import com.versant.core.jdbc.sql.exp.ColumnExp;
019: import com.versant.core.jdbc.sql.exp.SelectExp;
020: import com.versant.core.jdbc.sql.exp.SqlExp;
021: import com.versant.core.jdbc.JdbcOID;
022: import com.versant.core.util.CharBuf;
023: import com.versant.core.common.State;
024: import com.versant.core.common.Debug;
025:
026: import java.util.ArrayList;
027: import java.io.PrintStream;
028: import java.sql.PreparedStatement;
029: import java.sql.SQLException;
030:
031: import com.versant.core.common.BindingSupportImpl;
032:
033: /**
034: * A field that is a reference to another PC class.
035: */
036: public class JdbcRefField extends JdbcField {
037:
038: /**
039: * The columns used to store the primary key of the referenced (target)
040: * table. These must be in the same order as the pk array of the target
041: * table.
042: */
043: public JdbcColumn[] cols;
044: /**
045: * This is the class that we reference. Note that this may be for a
046: * different Store (e.g. LDAP).
047: */
048: public ClassMetaData targetClass;
049: /**
050: * The foreign key constraint for this reference (null if none).
051: */
052: public JdbcConstraint constraint;
053: /**
054: * If this reference is being used to complete a collection mapped using
055: * a foreign key reference then this is the collection field.
056: */
057: public JdbcFKCollectionField masterCollectionField;
058: public String constraintName;
059: public boolean createConstraint;
060:
061: public String toString() {
062: StringBuffer s = new StringBuffer();
063: s.append(super .toString());
064: switch (useJoin) {
065: case USE_JOIN_INNER:
066: s.append(" INNER");
067: case USE_JOIN_OUTER:
068: s.append(" OUTER");
069: }
070: return s.toString();
071: }
072:
073: /**
074: * Set the table field on all our main table columns.
075: */
076: public void setMainTable(JdbcTable table) {
077: super .setMainTable(table);
078: for (int i = 0; i < cols.length; i++) {
079: cols[i].setTable(table);
080: }
081: }
082:
083: /**
084: * Init the mainTableCols field to all our main table columns.
085: */
086: public void initMainTableCols() {
087: mainTableCols = cols;
088: super .initMainTableCols();
089: }
090:
091: /**
092: * Flatten all of this fields main table columns to a.
093: */
094: public void addMainTableCols(ArrayList a) {
095: int n = cols.length;
096: for (int i = 0; i < n; i++)
097: a.add(cols[i]);
098: }
099:
100: /**
101: * Make sure all of this fields main table columns have names.
102: */
103: public void nameColumns(String tableName, JdbcNameGenerator nameGen) {
104: // check if there are other fields referencing our targetClass
105: int refs = 0;
106: FieldMetaData[] fields = fmd.classMetaData.fields;
107: for (int i = fields.length - 1; i >= 0; i--) {
108: JdbcField f = (JdbcField) fields[i].storeField;
109: if (f instanceof JdbcRefField) {
110: JdbcRefField rf = (JdbcRefField) f;
111: if (rf.targetClass == targetClass) {
112: if (++refs == 2)
113: break;
114: }
115: }
116: }
117: boolean otherRefs = refs >= 2;
118:
119: // extract the current names of the columns and the
120: // matching referenced column names
121: String[] names = new String[cols.length];
122: String[] refPkNames = null;
123: JdbcClass tjc = (JdbcClass) targetClass.storeClass;
124: if (tjc != null) {
125: refPkNames = new String[cols.length];
126: for (int i = 0; i < cols.length; i++) {
127: names[i] = cols[i].name;
128: refPkNames[i] = tjc.table.pk[i].name;
129: }
130: }
131:
132: // generate the names for the unnamed columns
133: if (tjc == null) {
134: nameGen.generateRefFieldColumnNames(tableName, fmd.name,
135: names, null, null, otherRefs);
136: } else {
137: nameGen.generateRefFieldColumnNames(tableName, fmd.name,
138: names, tjc.tableName, refPkNames, otherRefs);
139: }
140:
141: // set our simple column names
142: for (int i = 0; i < cols.length; i++)
143: cols[i].name = names[i];
144: }
145:
146: /**
147: * Make sure all of this fields main table constraints have names and
148: * add them to cons.
149: */
150: public void addConstraints(ArrayList cons) {
151: if (constraint != null)
152: cons.add(constraint);
153: }
154:
155: public void dump(PrintStream out, String indent) {
156: super .dump(out, indent);
157: String is = indent + " ";
158: out.println(is + "targetClass = " + targetClass);
159: out.println(is + "constraint = " + constraint);
160: if (cols != null) {
161: out.println(is + cols.length + " cols(s)");
162: for (int i = 0; i < cols.length; i++) {
163: out.println(is + "[" + i + "] " + cols[i]);
164: }
165: }
166: }
167:
168: /**
169: * Append part of an update statement for us to s (e.g col = ?).
170: */
171: public boolean appendUpdate(CharBuf s, State state) {
172: int nc = mainTableColsForUpdate.length;
173: s.append(mainTableColsForUpdate[0].name);
174: s.append("=?");
175: for (int i = 1; i < nc; i++) {
176: s.append(", ");
177: s.append(mainTableColsForUpdate[i].name);
178: s.append("=?");
179: }
180: return false;
181: }
182:
183: /**
184: * Append part of a where clause for us to s (e.g cola = ? and colb = ?).
185: * This is used for generating the where clause for changed locking.
186: */
187: public void appendWhere(CharBuf s, SqlDriver sqlDriver) {
188: int nc = mainTableColsForUpdate.length;
189: JdbcColumn c = mainTableColsForUpdate[0];
190: s.append(c.name);
191: s.append('=');
192: sqlDriver.appendWhereParam(s, c);
193: for (int i = 1; i < nc; i++) {
194: c = mainTableColsForUpdate[i];
195: s.append(" and ");
196: s.append(c.name);
197: s.append('=');
198: sqlDriver.appendWhereParam(s, c);
199: }
200: }
201:
202: /**
203: * Append part of a is null where clause for us to s (e.g cola is null
204: * and colb is null).
205: * This is used for generating the where clause for changed locking.
206: */
207: public void appendWhereIsNull(CharBuf s, SqlDriver sqlDriver) {
208: int nc = mainTableColsForUpdate.length;
209: JdbcColumn c = mainTableColsForUpdate[0];
210: s.append(c.name);
211: s.append(" is null");
212: for (int i = 1; i < nc; i++) {
213: c = mainTableColsForUpdate[i];
214: s.append(" and ");
215: s.append(c.name);
216: s.append(" is null");
217: }
218: }
219:
220: /**
221: * Append part of the insert list for us to s (e.g. cola, colb)).
222: */
223: public void appendInsertColumnList(CharBuf s) {
224: int nc = mainTableColsForUpdate.length;
225: s.append(mainTableColsForUpdate[0].name);
226: for (int i = 1; i < nc; i++) {
227: s.append(", ");
228: s.append(mainTableColsForUpdate[i].name);
229: }
230: }
231:
232: /**
233: * Append part of the insert value list for us to s (e.g. ?, ?)). This
234: * must return true if a replacable parameter was <b>not</b> added (e.g.
235: * columns using Oracle LOBs which put in empty_clob() or whatever).
236: */
237: public boolean appendInsertValueList(CharBuf s, State state) {
238: s.append('?');
239: int nc = mainTableColsForUpdate.length;
240: for (int i = 1; i < nc; i++)
241: s.append(", ?");
242: return false;
243: }
244:
245: /**
246: * Check to see if the supplied table is a subclass table of the supplied
247: * ClassMetaData.
248: */
249: public static boolean isSubTableOf(JdbcTable table,
250: ClassMetaData cmd) {
251: if (table == ((JdbcClass) cmd.storeClass).table)
252: return true;
253: ClassMetaData[] pcSubs = cmd.pcSubclasses;
254: for (int i = 0; i < pcSubs.length; i++) {
255: ClassMetaData pcSub = pcSubs[i];
256: if (((JdbcClass) pcSub.storeClass).table == table) {
257: return true;
258: }
259: }
260: return false;
261: }
262:
263: /**
264: * Convert this field into a list of ColumnExp's or null if this is
265: * not possible.
266: */
267: public ColumnExp toColumnExp(SelectExp se, boolean joinToSuper) {
268: if (Debug.DEBUG) {
269: if (!isSubTableOf(se.table, fmd.classMetaData)) {
270: throw BindingSupportImpl
271: .getInstance()
272: .internal(
273: "The table '"
274: + se.table.name
275: + "'of the suplied selectExp is not equal or a subClass "
276: + "table of table '"
277: + ((JdbcClass) fmd.classMetaData.storeClass).table.name
278: + "'");
279: }
280: }
281: if (joinToSuper)
282: se = SelectExp.createJoinToSuperTable(se, this );
283: return createOwningTableColumnExpList(se);
284: }
285:
286: public ColumnExp createOwningTableColumnExpList(SelectExp se) {
287: ColumnExp ans = new ColumnExp(cols[0], se, this );
288: SqlExp e = ans;
289: int nc = cols.length;
290: for (int i = 1; i < nc; i++) {
291: e = e.next = new ColumnExp(cols[i], se, this );
292: }
293: return ans;
294: }
295:
296: /**
297: * Convert this field into a list of ColumnExp's to be compared to
298: * a null literal. This should only include non-shared columns i.e.
299: * columns that are updated. If all columns are shared then all should
300: * be included.
301: */
302: public ColumnExp toColumnExpForNullLiteralCompare(SelectExp se) {
303: se = SelectExp.createJoinToSuperTable(se, this );
304:
305: if (mainTableColsForUpdate == null)
306: return toColumnExp(se, true);
307: ColumnExp ans = new ColumnExp(mainTableColsForUpdate[0], se,
308: this );
309: SqlExp e = ans;
310: int nc = mainTableColsForUpdate.length;
311: for (int i = 1; i < nc; i++) {
312: e = e.next = new ColumnExp(mainTableColsForUpdate[i], se,
313: this );
314: }
315: return ans;
316: }
317:
318: /**
319: * Set this field on a PreparedStatement. This is used to set parameters
320: * for queries.
321: *
322: * @return Index of the parameter after the last one we set in ps
323: */
324: public int setQueryParam(PreparedStatement ps, int firstParam,
325: Object value) throws SQLException {
326: OID oid = (OID) value;
327: if (oid != null) {
328: firstParam = ((JdbcOID) oid).setParams(ps, firstParam);
329: } else {
330: int nc = cols.length;
331: for (int i = 0; i < nc; i++) {
332: ps.setNull(firstParam++, cols[i].jdbcType);
333: }
334: }
335: return firstParam;
336: }
337: }
|