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.FieldMetaData;
014: import com.versant.core.metadata.parser.JdoField;
015: import com.versant.core.jdbc.sql.JdbcNameGenerator;
016: import com.versant.core.jdbc.sql.SqlDriver;
017: import com.versant.core.jdbc.sql.exp.ColumnExp;
018: import com.versant.core.jdbc.sql.exp.SelectExp;
019: import com.versant.core.jdbc.sql.exp.SqlExp;
020: import com.versant.core.jdbc.query.JdbcJDOQLCompiler;
021: import com.versant.core.jdbc.fetch.FetchOp;
022: import com.versant.core.jdbc.fetch.FetchSpec;
023: import com.versant.core.jdbc.fetch.FetchOptions;
024: import com.versant.core.server.PersistGraph;
025: import com.versant.core.util.CharBuf;
026: import com.versant.core.jdo.query.Node;
027: import com.versant.core.common.State;
028:
029: import java.io.Serializable;
030: import java.io.PrintStream;
031: import java.util.ArrayList;
032: import java.util.HashSet;
033: import java.sql.PreparedStatement;
034: import java.sql.SQLException;
035: import java.sql.Connection;
036:
037: import com.versant.core.common.*;
038:
039: /**
040: * Extra meta data for a field stored in JDBC. There are different subclasses
041: * for different types of fields (e.g. simple, persistent class reference,
042: * mem etc.).
043: */
044: public abstract class JdbcField implements Serializable {
045:
046: /**
047: * Do not join to resolve this field.
048: */
049: public static final int USE_JOIN_NO = 1;
050: /**
051: * Use an outer join to resolve this field.
052: */
053: public static final int USE_JOIN_OUTER = 2;
054: /**
055: * Use an inner join to resolve this field.
056: */
057: public static final int USE_JOIN_INNER = 3;
058:
059: /**
060: * Our JDO field.
061: */
062: public FieldMetaData fmd;
063: /**
064: * Our state fieldNo.
065: */
066: public int stateFieldNo;
067: /**
068: * @keep Our main table columns.
069: */
070: public JdbcColumn[] mainTableCols;
071: /**
072: * Our main table columns that need to be updated and inserted.
073: */
074: public JdbcColumn[] mainTableColsForUpdate;
075: /**
076: * Our main table.
077: */
078: public JdbcTable mainTable;
079: /**
080: * Is this a fake field created to store some extra data (e.g. row version
081: * column values).
082: */
083: public boolean fake;
084: /**
085: * Should a join be done to pick up the fields for referenced classes
086: * when this field is read? This only makes sense for fields that
087: * reference other PC classes in some way.
088: */
089: public int useJoin;
090: /**
091: * Should this field be included in the where clause when using changed
092: * optimistic locking? This default to false for fields mapped to columns
093: * that have equalityTest false.
094: */
095: public boolean includeForChangedLocking;
096:
097: /**
098: * Make sure all of this fields main table columns have names.
099: */
100: public void nameColumns(String tableName, JdbcNameGenerator nameGen) {
101: }
102:
103: /**
104: * Add all of this fields main table constraints to cons.
105: */
106: public void addConstraints(ArrayList cons) {
107: }
108:
109: /**
110: * Init the mainTableCols field to all our main table columns.
111: */
112: public void initMainTableCols() {
113: }
114:
115: /**
116: * Init the mainTableColsForUpdate field to all our main table columns
117: * that are for update.
118: */
119: public void initMainTableColsForUpdate() {
120: if (mainTableCols == null)
121: return;
122:
123: // extract all the columns that should be updated and inserted into
124: // mainTableColsForUpdate
125: mainTableColsForUpdate = new JdbcColumn[mainTableCols.length];
126: int c = 0;
127: for (int i = 0; i < mainTableCols.length; i++) {
128: JdbcColumn col = mainTableCols[i];
129: if (col.isForUpdate())
130: mainTableColsForUpdate[c++] = col;
131: }
132: if (c == 0) {
133: mainTableColsForUpdate = null;
134: } else if (c < mainTableCols.length) {
135: JdbcColumn[] a = new JdbcColumn[c];
136: System.arraycopy(mainTableColsForUpdate, 0, a, 0, c);
137: mainTableColsForUpdate = a;
138: }
139: }
140:
141: /**
142: * Flatten all of this fields main table columns to a.
143: */
144: public void addMainTableCols(ArrayList a) {
145: }
146:
147: /**
148: * Set the table field on all our main table columns.
149: */
150: public void setMainTable(JdbcTable table) {
151: mainTable = table;
152: }
153:
154: public String toString() {
155: String n = getClass().getName();
156: n = n.substring(n.lastIndexOf('.') + 1);
157: return n + " " + fmd.type.getName() + " "
158: + fmd.classMetaData.getShortName() + "." + fmd.name
159: + (fake ? " FAKE" : "");
160: }
161:
162: /**
163: * Convert a useJoin field value to a String.
164: */
165: public static String toUseJoinString(int useJoin) {
166: switch (useJoin) {
167: case USE_JOIN_NO:
168: return "NO";
169: case USE_JOIN_INNER:
170: return "INNER";
171: case USE_JOIN_OUTER:
172: return "OUTER";
173: }
174: return "unknown(" + useJoin + ")";
175: }
176:
177: /**
178: * Get context information for this field from its .jdo meta data or
179: * the .jdo meta data of its class.
180: */
181: public String getContext() {
182: JdoField jf = fmd.jdoField;
183: if (jf != null)
184: return jf.getContext();
185: return fmd.classMetaData.jdoClass.getContext();
186: }
187:
188: /**
189: * Add all tables that belong to this field to the set.
190: */
191: public void getTables(HashSet tables) {
192: // nothing to do
193: }
194:
195: /**
196: * Get the useKeyJoin value for this field. This is only valid for maps.
197: */
198: public int getUseKeyJoin() {
199: return 0;
200: }
201:
202: public void dump() {
203: dump(Debug.OUT, "");
204: }
205:
206: public void dump(PrintStream out, String indent) {
207: out.println(indent + this );
208: String is = indent + " ";
209: out.println(is + "useJoin " + toUseJoinString(useJoin));
210: out.println(is + "stateFieldNo " + stateFieldNo);
211: if (mainTableCols != null) {
212: out
213: .println(is + mainTableCols.length
214: + " mainTableCols(s)");
215: for (int i = 0; i < mainTableCols.length; i++) {
216: out.println(is + "[" + i + "] " + mainTableCols[i]);
217: }
218: }
219: if (mainTableColsForUpdate != null) {
220: out.println(is + mainTableColsForUpdate.length
221: + " mainTableColsForUpdate(s)");
222: for (int i = 0; i < mainTableColsForUpdate.length; i++) {
223: out.println(is + "[" + i + "] "
224: + mainTableColsForUpdate[i]);
225: }
226: }
227: }
228:
229: /**
230: * Append part of an update statement for us to s (e.g col = ?). This
231: * must return true if a replacable parameter was <b>not</b> added (e.g.
232: * columns using Oracle LOBs which put in empty_clob() or whatever).
233: */
234: public boolean appendUpdate(CharBuf s, State state) {
235: return false;
236: }
237:
238: /**
239: * Append part of a where clause for us to s (e.g cola = ? and colb = ?).
240: * This is used for generating the where clause for changed locking.
241: */
242: public void appendWhere(CharBuf s, SqlDriver sqlDriver) {
243: }
244:
245: /**
246: * Append part of a is null where clause for us to s (e.g cola is null
247: * and colb is null).
248: * This is used for generating the where clause for changed locking.
249: */
250: public void appendWhereIsNull(CharBuf s, SqlDriver sqlDriver) {
251: }
252:
253: /**
254: * Append part of the insert list for us to s (e.g. cola, colb)).
255: */
256: public void appendInsertColumnList(CharBuf s) {
257: }
258:
259: /**
260: * Append part of the insert value list for us to s (e.g. ?, ?)). This
261: * must return true if a replacable parameter was <b>not</b> added (e.g.
262: * columns using Oracle LOBs which put in empty_clob() or whatever).
263: */
264: public boolean appendInsertValueList(CharBuf s, State state) {
265: return false;
266: }
267:
268: /**
269: * Convert this field into a list of ColumnExp's or null if this is
270: * not possible.
271: */
272: public ColumnExp toColumnExp(SelectExp se, boolean joinToSuper) {
273: return null;
274: }
275:
276: /**
277: * Convert this field into a list of ColumnExp's to be compared to
278: * a null literal. This should only include non-shared columns i.e.
279: * columns that are updated. If all columns are shared then all should
280: * be included.
281: */
282: public ColumnExp toColumnExpForNullLiteralCompare(SelectExp se) {
283: return toColumnExp(se, true);
284: }
285:
286: /**
287: * Convert this field into an isEmpty expression.
288: */
289: public SqlExp toIsEmptySqlExp(JdbcJDOQLCompiler comp, SelectExp root) {
290: throw BindingSupportImpl.getInstance().runtime(
291: "isEmpty() may not be called on " + fmd.getQName());
292: }
293:
294: /**
295: * Convert this field into an contains expression.
296: */
297: public SqlExp toContainsSqlExp(JdbcJDOQLCompiler comp,
298: SelectExp root, Node args) {
299: throw BindingSupportImpl.getInstance().runtime(
300: "contains(...) may not be called on " + fmd.getQName());
301: }
302:
303: /**
304: * Convert this field into an containsKey expression.
305: */
306: public SqlExp toContainsKeySqlExp(JdbcJDOQLCompiler comp,
307: SelectExp root, Node args) {
308: throw BindingSupportImpl.getInstance().runtime(
309: "containsKey(...) may not be called on "
310: + fmd.getQName());
311: }
312:
313: /**
314: * Set this field on a PreparedStatement. This is used to set parameters
315: * for queries.
316: *
317: * @return Index of the parameter after the last one we set in ps
318: */
319: public int setQueryParam(PreparedStatement ps, int firstParam,
320: Object value) throws SQLException {
321: throw BindingSupportImpl.getInstance().internal(
322: "set called on " + this );
323: }
324:
325: /**
326: * Persist pass 2 field for a block of graph entries all with
327: * the same class. The same ps'es can be used for all entries in the block.
328: */
329: public void persistPass2Block(PersistGraph graph, int blockStart,
330: int blockEnd, CharBuf s, Connection con,
331: boolean batchInserts, boolean batchUpdates)
332: throws SQLException {
333: throw BindingSupportImpl.getInstance().internal(
334: "persistPass2Block called on " + this );
335: }
336:
337: /**
338: * Delete a pass 2 field for a block of graph entries all with
339: * the same class. The same ps'es can be used for all entries in the block.
340: */
341: public void deletePass2Block(DeletePacket graph, int blockStart,
342: int blockEnd, CharBuf s, Connection con, boolean batch)
343: throws SQLException {
344: throw BindingSupportImpl.getInstance().internal(
345: "deletePass2Block called on " + this );
346: }
347:
348: /**
349: * Does this field require the sucky Oracle LOB support on insert/update?
350: */
351: public boolean isOracleStyleLOB() {
352: return false;
353: }
354:
355: /**
356: * Make sure all the indexes on our link tables (if any) have names,
357: */
358: public void nameLinkTableIndexes(JdbcNameGenerator namegen) {
359: }
360:
361: /**
362: * If there a columnName in our main table columns array then return it
363: * else return null.
364: */
365: public JdbcColumn findMainTableColumn(String columnName) {
366: if (mainTableCols == null)
367: return null;
368: for (int i = mainTableCols.length - 1; i >= 0; i--) {
369: JdbcColumn c = mainTableCols[i];
370: if (c.name.equals(columnName))
371: return c;
372: }
373: return null;
374: }
375:
376: /**
377: * Get the current SqlDriver.
378: */
379: public SqlDriver getSqlDriver() {
380: return ((JdbcClass) fmd.classMetaData.storeClass).sqlDriver;
381: }
382:
383: /**
384: * Map an exception using the current SqlDriver.
385: */
386: public RuntimeException mapException(Throwable cause, String message) {
387: return getSqlDriver().mapException(cause, message, true);
388: }
389:
390: /**
391: * Create a list of ColumnExp's for this field or null if it has no
392: * columns stored in any of the tables for its owning class.
393: */
394: public ColumnExp createOwningTableColumnExpList(SelectExp se) {
395: return null;
396: }
397:
398: /**
399: * Adjust spec so this field will be fetched.
400: */
401: public void prepareFetch(FetchSpec spec, FetchOptions options) {
402: }
403:
404: }
|