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.*;
014: import com.versant.core.metadata.FetchGroup;
015: import com.versant.core.metadata.FetchGroupField;
016: import com.versant.core.metadata.FieldMetaData;
017: import com.versant.core.metadata.ClassMetaData;
018: import com.versant.core.jdbc.metadata.*;
019: import com.versant.core.server.PersistGraph;
020:
021: import java.sql.PreparedStatement;
022: import java.sql.SQLException;
023: import java.sql.ResultSet;
024:
025: /**
026: * Adds JDBC specific methods to GenericState.
027: */
028: public class JdbcGenericState extends GenericState implements JdbcState {
029:
030: public JdbcGenericState() {
031: }
032:
033: public JdbcGenericState(ClassMetaData cmd) {
034: super (cmd);
035: }
036:
037: /**
038: * Return 0 if state has the same field numbers as us, less than 0 we are
039: * less than it or greater than 0 if we are greater than it. The definition
040: * of less than and greater than is up to the state implementation but
041: * must be detirministic. For fields that are stored using Oracle style
042: * LOBs then the nullness of the value must also be considered in the
043: * comparison i.e. states with field x null and not null respectively
044: * are different.
045: *
046: * @param state State to compare to (will be for same class)
047: */
048: public int compareToPass1(State state) {
049: JdbcGenericState s = (JdbcGenericState) state;
050: checkCmd();
051: boolean[] sf = s.filled;
052: for (int i = 0; i < filled.length; i++) {
053: if (!cmd.stateFields[i].primaryField)
054: continue;
055: boolean a = filled[i];
056: boolean b = sf[i];
057: if (a && !b)
058: return -1;
059: if (!a && b)
060: return +1;
061: if (a) {
062: // both fields are filled so if the field is a LOB consider
063: // value nullness
064: JdbcField jdbcField = (JdbcField) cmd.stateFields[i].storeField;
065: if (jdbcField.isOracleStyleLOB()) {
066: a = isNull(i);
067: b = s.isNull(i);
068: if (a && !b)
069: return -1;
070: if (!a && b)
071: return +1;
072: }
073: }
074: }
075: return 0;
076: }
077:
078: /**
079: * Populate this State from the given ResultSet. The firstCol parameter
080: * specifies the column index of the first column to read from rs. All
081: * persistent fields in the fetch group must be read in order.
082: */
083: public void copyPass1Fields(ResultSet rs, FetchGroup fetchGroup,
084: int firstCol) {
085: FetchGroupField[] a = fetchGroup.fields;
086: for (int i = 0; i < a.length; i++) {
087: try {
088: FieldMetaData fmd = a[i].fmd;
089: if (!fmd.primaryField)
090: continue;
091: JdbcField f = (JdbcField) fmd.storeField;
092: int fieldNo = f.stateFieldNo;
093: firstCol = getFieldData(f, rs, firstCol, fieldNo);
094: filled[fieldNo] = true;
095: } catch (Exception e) {
096: throw BindingSupportImpl.getInstance().datastore(
097: "Error reading field " + a[i].fmd.getQName()
098: + " from ResultSet: " + e, e);
099: }
100: }
101: }
102:
103: public void copyPass1Fields(ResultSet rs, JdbcField[] fields) {
104: JdbcField f = null;
105: for (int i = 0; i < fields.length; i++) {
106: try {
107: f = fields[i];
108: if (f == null)
109: continue;
110: //ignore fields that are not in the statefields
111: if (f.stateFieldNo < cmd.stateFields.length
112: && cmd.stateFields[f.stateFieldNo] == f.fmd) {
113: getFieldData(f, rs, i + 1, f.stateFieldNo);
114: filled[f.stateFieldNo] = true;
115: }
116: } catch (SQLException e) {
117: throw BindingSupportImpl.getInstance().datastore(
118: "Error reading field " + f == null ? "" : f.fmd
119: .getQName()
120: + " from ResultSet: " + e, e);
121: }
122: }
123: }
124:
125: private int getFieldData(JdbcField f, ResultSet rs, int firstCol,
126: int fieldNo) throws SQLException {
127: if (f instanceof JdbcSimpleField) {
128: JdbcColumn c = ((JdbcSimpleField) f).col;
129: if (Debug.DEBUG) {
130: if (!c.name.toUpperCase().equals(
131: rs.getMetaData().getColumnName(firstCol)
132: .toUpperCase())) {
133: throw BindingSupportImpl.getInstance().internal(
134: "Reading the wrong column: "
135: + firstCol
136: + " \nrs field = "
137: + rs.getMetaData().getColumnName(
138: firstCol)
139: + "\nmetaData field = " + c.name);
140: }
141: }
142: if (c.converter != null) {
143: data[fieldNo] = c.converter.get(rs, firstCol++, c);
144: } else {
145: data[fieldNo] = JdbcUtils.get(rs, firstCol++,
146: c.javaTypeCode, c.scale);
147: if (rs.wasNull()) {
148: data[fieldNo] = null;
149: }
150: }
151: } else if (f instanceof JdbcRefField) {
152: JdbcRefField rf = (JdbcRefField) f;
153: JdbcOID oid = (JdbcOID) rf.targetClass.createOID(false);
154: if (oid.copyKeyFields(rs, firstCol)) {
155: data[fieldNo] = oid;
156: } else {
157: data[fieldNo] = null;
158: }
159: firstCol += rf.cols.length;
160: } else if (f instanceof JdbcPolyRefField) {
161: data[fieldNo] = getPolyRefOID(f, rs, firstCol);
162: firstCol += ((JdbcPolyRefField) f).cols.length;
163: } else {
164: throw BindingSupportImpl.getInstance().internal(
165: "not implemented");
166: }
167: return firstCol;
168: }
169:
170: /**
171: * Set parameters on a PrepareStatement from this State. The firstParam
172: * parameter specifies the column index of the first parameter to set.
173: * Entries in fieldNos that are less than 0 should be skipped.
174: *
175: * @param firstFieldNo The index of the first state field to set
176: * @param lastFieldNo The index of the last state field to set + 1
177: * @param tableNo Set fields with table == jdbcClass.allTables[tableNo]
178: * @return the index of the last param set + 1
179: */
180: public int setParams(PreparedStatement ps, int[] stateFieldNos,
181: int firstFieldNo, int lastFieldNo, int firstParam,
182: PersistGraph pGraph, int tableNo) throws SQLException {
183: JdbcTable table = ((JdbcClass) cmd.storeClass).allTables[tableNo];
184: for (int i = firstFieldNo; i < lastFieldNo; i++) {
185: int fieldNo = stateFieldNos[i];
186: if (fieldNo >= 0) {
187: JdbcField field = (JdbcField) cmd.stateFields[fieldNo].storeField;
188: if (field.mainTable == table) {
189: firstParam = setFieldData(field, ps, firstParam,
190: fieldNo);
191: }
192: }
193: }
194: return firstParam;
195: }
196:
197: /**
198: * Set parameters on a PrepareStatement from this State for fields that
199: * are not null. The firstParam parameter specifies the column index of
200: * the first parameter. This will not be called for classes that are not
201: * stored by the JdbcDataStore or that do not use changed optimistic
202: * locking. Entries in fieldNos that are less than 0 should be skipped.
203: *
204: * @param firstFieldNo The index of the first field to set
205: * @param lastFieldNo The index of the last field to set + 1
206: * @param tableNo
207: * @return the index of the last param set + 1
208: */
209: public int setParamsChangedAndNotNull(PreparedStatement ps,
210: int[] fieldNos, int firstFieldNo, int lastFieldNo,
211: int firstParam, PersistGraph pGraph, int tableNo)
212: throws SQLException {
213: checkCmd();
214: JdbcTable table = ((JdbcClass) cmd.storeClass).allTables[tableNo];
215: for (int i = firstFieldNo; i < lastFieldNo; i++) {
216: int fieldNo = fieldNos[i];
217: if (fieldNo < 0 || data[fieldNo] == null)
218: continue;
219: JdbcField field = (JdbcField) cmd.stateFields[fieldNo].storeField;
220: if (field.includeForChangedLocking
221: && field.mainTable == table) {
222: firstParam = setFieldData(field, ps, firstParam,
223: fieldNo);
224: }
225: }
226: return firstParam;
227: }
228:
229: /**
230: * Set parameters on a PrepareStatement from the optimistic locking field
231: * for the class for this State. The firstParam parameter specifies the
232: * column index of the first parameter to set.
233: *
234: * @return the index of the last param set + 1
235: * @throws javax.jdo.JDOFatalInternalException
236: * if there is no such field
237: * @see JdbcClass#optimisticLockingField
238: */
239: public int setOptimisticLockingParams(PreparedStatement ps,
240: int firstParam) throws SQLException {
241: checkCmd();
242: JdbcSimpleField f = ((JdbcClass) cmd.storeClass).optimisticLockingField;
243: if (f == null) {
244: throw BindingSupportImpl
245: .getInstance()
246: .internal(
247: "setOptimisticLockingParams "
248: + "called for non-optimistic locking class: "
249: + cmd);
250: }
251: int fieldNo = f.stateFieldNo;
252: // carl do not put this test into generated State class
253: if (!containsField(fieldNo)) {
254: throw BindingSupportImpl
255: .getInstance()
256: .internal(
257: "setOptimisticLockingParams "
258: + "called for state not containing optimistic locking field: "
259: + cmd.qname + " " + f);
260: }
261: JdbcColumn c = f.col;
262: if (c.converter != null) {
263: c.converter.set(ps, firstParam++, c, data[fieldNo]);
264: } else {
265: JdbcUtils.set(ps, firstParam++, data[fieldNo],
266: c.javaTypeCode, c.jdbcType);
267: }
268: return firstParam;
269: }
270:
271: private int setFieldData(JdbcField f, PreparedStatement ps,
272: int firstParam, int fieldNo) throws SQLException {
273: if (f instanceof JdbcSimpleField) {
274: JdbcColumn c = ((JdbcSimpleField) f).col;
275: if (c.isForUpdate()) {
276: if (c.converter != null) {
277: c.converter.set(ps, firstParam++, c, data[fieldNo]);
278: } else {
279: JdbcUtils.set(ps, firstParam++, data[fieldNo],
280: c.javaTypeCode, c.jdbcType);
281: }
282: }
283: } else if (f instanceof JdbcRefField) {
284: OID oid = (OID) data[fieldNo];
285: if (oid == null || (oid = oid.getRealOID()) == null) {
286: firstParam = setRefFieldToNull(f.mainTableCols, ps,
287: firstParam);
288: } else {
289: firstParam = ((JdbcOID) oid).setParams(ps, firstParam,
290: f.mainTableCols);
291: }
292: } else if (f instanceof JdbcPolyRefField) {
293: firstParam = setPolyRefData(f, (OID) data[fieldNo], cmd,
294: ps, firstParam);
295: } else {
296: throw BindingSupportImpl.getInstance().internal(
297: "not implemented");
298: }
299: return firstParam;
300: }
301:
302: /**
303: * Call the set(rs,...) method on each of the converters for the first
304: * numFieldNos entries in stateFieldNos. This is used to handle Oracle
305: * style LOB columns.
306: *
307: * @param firstCol The first column in rs to use
308: * @see com.versant.core.jdbc.JdbcConverter#set
309: */
310: public void setOracleStyleLOBs(ResultSet rs, int[] stateFieldNos,
311: int numFieldNos, int firstCol) throws SQLException {
312: for (int i = 0; i < numFieldNos; i++) {
313: int fieldNo = stateFieldNos[i];
314: JdbcField jdbcField = (JdbcField) cmd.stateFields[fieldNo].storeField;
315: JdbcColumn c = ((JdbcSimpleField) jdbcField).col;
316: c.converter.set(rs, firstCol++, c, data[fieldNo]);
317: }
318: }
319:
320: private com.versant.core.common.OID getPolyRefOID(
321: com.versant.core.metadata.FieldMetaData fmd,
322: java.sql.ResultSet rs, int firstCol)
323: throws java.sql.SQLException {
324: return getPolyRefOID((JdbcField) fmd.storeField, rs, firstCol);
325: }
326:
327: public static com.versant.core.common.OID getPolyRefOID(
328: com.versant.core.jdbc.metadata.JdbcField f,
329: java.sql.ResultSet rs, int firstCol)
330: throws java.sql.SQLException {
331: com.versant.core.jdbc.metadata.JdbcPolyRefField pf = (com.versant.core.jdbc.metadata.JdbcPolyRefField) f;
332: return pf.getData(rs, firstCol);
333: }
334:
335: private int setPolyRefData(
336: com.versant.core.metadata.FieldMetaData fmd,
337: com.versant.core.common.OID oid,
338: com.versant.core.metadata.ClassMetaData cmd,
339: java.sql.PreparedStatement ps, int firstParam)
340: throws java.sql.SQLException {
341: return setPolyRefData((JdbcField) fmd.storeField, oid, cmd, ps,
342: firstParam);
343: }
344:
345: public static int setPolyRefData(
346: com.versant.core.jdbc.metadata.JdbcField f,
347: com.versant.core.common.OID oid,
348: com.versant.core.metadata.ClassMetaData cmd,
349: java.sql.PreparedStatement ps, int firstParam)
350: throws java.sql.SQLException {
351: com.versant.core.jdbc.metadata.JdbcPolyRefField pf = (com.versant.core.jdbc.metadata.JdbcPolyRefField) f;
352: return pf.setData(ps, firstParam, oid);
353: }
354:
355: public static int setRefFieldToNull(JdbcColumn[] cols,
356: PreparedStatement ps, int firstParam) throws SQLException {
357: int nc = cols.length;
358: for (int i = 0; i < nc; i++) {
359: JdbcColumn col = cols[i];
360: if (col.isForUpdate()) {
361: ps.setNull(firstParam++, col.jdbcType);
362: }
363: }
364: return firstParam;
365: }
366:
367: public boolean hasSameNullFields(State state, State mask) {
368: checkCmd();
369: JdbcClass jc = (JdbcClass) cmd.storeClass;
370: if (jc.optimisticLocking != JdbcClass.OPTIMISTIC_LOCKING_CHANGED) {
371: return true;
372: }
373: JdbcGenericState s = (JdbcGenericState) state;
374: JdbcGenericState ms = (JdbcGenericState) mask;
375: int n = filled.length;
376: for (int i = 0; i < n; i++) {
377: if (ms.filled[i]) {
378: if ((data[i] == null) != (s.data[i] == null))
379: return false;
380: }
381: }
382: return true;
383: }
384:
385: }
|