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.common.Debug;
014: import com.versant.core.common.NotImplementedException;
015: import com.versant.core.metadata.MDStaticUtils;
016: import com.versant.core.jdbc.JdbcConverter;
017: import com.versant.core.jdbc.JdbcConverterFactory;
018: import com.versant.core.jdbc.JdbcTypeRegistry;
019: import com.versant.core.jdbc.JdbcUtils;
020: import com.versant.core.jdbc.sql.JdbcNameGenerator;
021: import com.versant.core.jdbc.sql.SqlDriver;
022: import com.versant.core.jdbc.sql.exp.*;
023: import com.versant.core.util.CharBuf;
024:
025: import java.io.Serializable;
026: import java.sql.PreparedStatement;
027: import java.sql.ResultSet;
028: import java.sql.SQLException;
029:
030: import com.versant.core.common.BindingSupportImpl;
031:
032: /**
033: * A column in a JDBC table.
034: */
035: public final class JdbcColumn implements Serializable {
036:
037: /**
038: * The table this column belongs to.
039: */
040: public transient JdbcTable table;
041: /**
042: * The java type of this column. This is used to select converters
043: * etc and when the state of this column needs to be stored. For a column
044: * for a persistent field this will just be the type of the field.
045: */
046: public transient Class javaType;
047: /**
048: * The java type code of this column from MDStatics.
049: *
050: * @see com.versant.core.metadata.MDStatics
051: */
052: public int javaTypeCode;
053: /**
054: * Is this column part of the primary key for its table?
055: */
056: public boolean pk;
057: /**
058: * Is this column part of a foreign key reference to another table?
059: */
060: public boolean foreignKey;
061: /**
062: * Is this column part of an index?
063: */
064: public boolean partOfIndex;
065: /**
066: * Is this an autoincrement column?
067: */
068: public boolean autoinc;
069: /**
070: * The JDBC name of this column.
071: */
072: public String name;
073: /**
074: * The JDBC type of this column from java.sql.Types.
075: */
076: public int jdbcType;
077: /**
078: * The actual SQL type of this column for create scripts etc.
079: */
080: public String sqlType;
081:
082: /**
083: * The length (or precision) of this column.
084: */
085: public int length;
086: /**
087: * The scale of this column (number of digits after the decimal point).
088: */
089: public int scale;
090: /**
091: * Does this column allow nulls?
092: */
093: public boolean nulls;
094: /**
095: * Can values from this column be compared for equality with Java values?
096: * Non-exact data types should have false here (e.g. float and double).
097: */
098: public boolean equalityTest;
099: /**
100: * Should this column not be created when generating the schema? This
101: * is used when columns are shared between one or more fields.
102: */
103: public boolean shared;
104: /**
105: * This is responsible for getting a suitable java value for this column
106: * from a ResultSet and setting the java value of this column on a
107: * PreparedStatement. It may be null in which case the ResultSet should
108: * be accessed directly using one of the getXXX or setXXX methods.
109: */
110: public transient JdbcConverter converter;
111: /**
112: * The name of the field this column references. This is used for columns
113: * that reference composite primary key classes to identify which
114: * primary key field the column is for.
115: */
116: public transient JdbcSimpleField refField;
117: /**
118: * Comment info for the SQL script (e.g. what field this column is for).
119: */
120: public String comment;
121:
122: public JdbcColumn() {
123: }
124:
125: public JdbcColumn(JdbcJavaTypeMapping m,
126: JdbcTypeRegistry jdbcTypeRegistry) {
127: sqlType = m.getSqlType();
128: if (sqlType == null) {
129: throw BindingSupportImpl.getInstance().internal(
130: "sqlType is null: " + m);
131: }
132:
133: setJavaType(m.getJavaType());
134: jdbcType = m.getJdbcType();
135: length = m.getLength();
136: scale = m.getScale();
137: nulls = m.getNulls() != JdbcJavaTypeMapping.FALSE;
138: equalityTest = m.getEqualityTest() != JdbcJavaTypeMapping.FALSE;
139: setShared(m.getShared() == JdbcJavaTypeMapping.TRUE);
140: JdbcConverterFactory cf = m.getConverterFactory();
141: if (cf != null) {
142: converter = cf.createJdbcConverter(this , null,
143: jdbcTypeRegistry);
144: if (converter != null && converter.isOracleStyleLOB())
145: equalityTest = false;
146: }
147: }
148:
149: /**
150: * Set our properties based on the info in the mapping. This is useful
151: * when a column is based on another column instead of being created
152: * by resolving the mapping (e.g. reference fields).
153: */
154: public void updateFrom(JdbcJavaTypeMapping m,
155: JdbcTypeRegistry jdbcTypeRegistry) {
156: if (m.getSqlType() != null)
157: sqlType = m.getSqlType();
158: if (m.getJavaType() != null)
159: setJavaType(m.getJavaType());
160: if (m.getJdbcType() != 0)
161: jdbcType = m.getJdbcType();
162: if (m.getLength() != -1)
163: length = m.getLength();
164: if (m.getScale() != -1)
165: scale = m.getScale();
166: if (m.getNulls() != JdbcJavaTypeMapping.NOT_SET) {
167: nulls = m.getNulls() != JdbcJavaTypeMapping.FALSE;
168: }
169: if (m.getEqualityTest() != JdbcJavaTypeMapping.NOT_SET) {
170: equalityTest = m.getEqualityTest() != JdbcJavaTypeMapping.FALSE;
171: }
172: if (m.getShared() != JdbcJavaTypeMapping.NOT_SET) {
173: setShared(m.getShared() != JdbcJavaTypeMapping.FALSE);
174: }
175: JdbcConverterFactory cf = m.getConverterFactory();
176: if (cf != null) {
177: converter = cf.createJdbcConverter(this , null,
178: jdbcTypeRegistry);
179: if (converter != null && converter.isOracleStyleLOB())
180: equalityTest = false;
181: }
182: }
183:
184: /**
185: * Set the javaType and the javaTypeCode.
186: */
187: public void setJavaType(Class javaType) {
188: this .javaType = javaType;
189: javaTypeCode = MDStaticUtils.toTypeCode(javaType);
190: }
191:
192: public Class getJavaType() {
193: return javaType;
194: }
195:
196: /**
197: * If the column or its component columns have names then add them to
198: * nameGen.
199: *
200: * @throws IllegalArgumentException if any names are invalid
201: */
202: public void addColumnNames(String tableName,
203: JdbcNameGenerator nameGen) throws IllegalArgumentException {
204: if (name != null)
205: nameGen.addColumnName(tableName, name);
206: }
207:
208: /**
209: * Get the names of all our component columns.
210: */
211: public String[] getColumnNames() {
212: return new String[] { name };
213: }
214:
215: /**
216: * Get the names of all our component columns into the array.
217: */
218: public void getColumnNames(String[] names) {
219: names[0] = name;
220: }
221:
222: /**
223: * Set the names of all our component columns from the array.
224: */
225: public void setColumnNames(String[] names) {
226: name = names[0];
227: }
228:
229: /**
230: * Set our table field and recursively all our columns.
231: */
232: public void setTable(JdbcTable t) {
233: table = t;
234: }
235:
236: /**
237: * Duplicate this column but leave the name, table and refField fields of
238: * the duplicates null.
239: */
240: public JdbcColumn copy() {
241: JdbcColumn d = new JdbcColumn();
242: d.javaType = javaType;
243: d.pk = pk;
244: d.javaTypeCode = javaTypeCode;
245: d.jdbcType = jdbcType;
246: d.sqlType = sqlType;
247: d.length = length;
248: d.scale = scale;
249: d.nulls = nulls;
250: d.equalityTest = equalityTest;
251: d.setShared(shared);
252: d.converter = converter;
253: return d;
254: }
255:
256: /**
257: * Combine two arrays of JdbcColumn's into one. If b is null then a is
258: * returned as is.
259: */
260: public static JdbcColumn[] concat(JdbcColumn[] a, JdbcColumn[] b) {
261: if (b == null)
262: return a;
263: if (a == null)
264: return b;
265: int na = a.length;
266: int nb = b.length;
267: JdbcColumn[] ans = new JdbcColumn[na + nb];
268: System.arraycopy(a, 0, ans, 0, na);
269: System.arraycopy(b, 0, ans, na, nb);
270: return ans;
271: }
272:
273: /**
274: * Combine an arrays of JdbcColumn's and a single column into one. If
275: * the single column is null then the array is returned as is.
276: */
277: public static JdbcColumn[] concat(JdbcColumn[] a, JdbcColumn b) {
278: if (b == null)
279: return a;
280: int na = a.length;
281: JdbcColumn[] ans = new JdbcColumn[na + 1];
282: System.arraycopy(a, 0, ans, 0, na);
283: ans[na] = b;
284: return ans;
285: }
286:
287: /**
288: * Flatten cols into a list of expressions to select all the cols.
289: */
290: public static SqlExp toSqlExp(JdbcColumn[] cols, SelectExp se) {
291: SqlExp list = new ColumnExp(cols[0], se, null);
292: SqlExp e = list;
293: int nc = cols.length;
294: for (int i = 1; i < nc; i++) {
295: e = e.next = new ColumnExp(cols[i], se, null);
296: }
297: return list;
298: }
299:
300: /**
301: * Flatten cols into a list of expressions to select all the cols. Add the sqlExp
302: * to the end of the created list.
303: */
304: public static SqlExp toSqlExp(JdbcColumn[] cols, SelectExp se,
305: SqlExp sList) {
306: SqlExp list = new ColumnExp(cols[0], se, null);
307: SqlExp e = list;
308: int nc = cols.length;
309: for (int i = 1; i < nc; i++) {
310: e = e.next = new ColumnExp(cols[i], se, null);
311: }
312: e.next = sList;
313: return list;
314: }
315:
316: /**
317: * Get a list of expressions to select all our simple cols.
318: */
319: public SqlExp toSqlExp(SelectExp se) {
320: return new ColumnExp(this , SelectExp.createJoinToSuperTable(se,
321: table), null);
322: }
323:
324: /**
325: * If the column or its component columns have names then add them to
326: * nameGen.
327: *
328: * @throws IllegalArgumentException if any names are invalid
329: */
330: public static void addColumnNames(String tableName,
331: JdbcColumn[] cols, JdbcNameGenerator namegen)
332: throws IllegalArgumentException {
333: for (int i = 0; i < cols.length; i++) {
334: cols[i].addColumnNames(tableName, namegen);
335: }
336: }
337:
338: /**
339: * Get the names of all the simple columns in an array of columns.
340: */
341: public static String[] getColumnNames(JdbcColumn[] cols) {
342: if (cols == null) {
343: return new String[0];
344: }
345: int n = cols.length;
346: String[] ans = new String[n];
347: for (int i = 0; i < n; i++) {
348: ans[i] = cols[i].name;
349: }
350: return ans;
351: }
352:
353: /**
354: * Set the names of all the simple columns in an array of columns.
355: */
356: public static void setColumnNames(JdbcColumn[] cols, String[] names) {
357: if (cols == null) {
358: return;
359: }
360: int n = cols.length;
361: for (int i = 0; i < n; i++) {
362: cols[i].name = names[i];
363: }
364: }
365:
366: /**
367: * Format an array of columns as a comma separated String of the names.
368: */
369: public static String toNameString(JdbcColumn[] cols) {
370: StringBuffer s = new StringBuffer();
371: int len = cols.length;
372: for (int i = 0; i < len; i++) {
373: if (i > 0)
374: s.append(", ");
375: s.append(cols[i].name);
376: }
377: return s.toString();
378: }
379:
380: /**
381: * Set the nulls value for all of our columns.
382: */
383: public void setNulls(boolean nulls) {
384: this .nulls = nulls;
385: }
386:
387: /**
388: * Get a 'cola = ? [and colb = ?]' expression for an array of simple
389: * columns from se.
390: */
391: public static SqlExp createEqualsParamExp(JdbcColumn[] scols,
392: SelectExp se) {
393: int nc = scols.length;
394: if (nc == 1) {
395: return scols[0].createEqualsParamExp(se);
396: } else {
397: SqlExp list = scols[0].createEqualsParamExp(se);
398: SqlExp pos = list;
399: for (int i = 1; i < nc; i++) {
400: pos = pos.next = scols[i].createEqualsParamExp(se);
401: }
402: return new AndExp(list);
403: }
404: }
405:
406: public static InExp createInParamExp(JdbcColumn[] scols,
407: SelectExp se, int size) {
408: if (scols.length > 1) {
409: throw BindingSupportImpl
410: .getInstance()
411: .notImplemented(
412: "'In' expressions is not support on multi-pk classes");
413: }
414:
415: ColumnExp columnExp = new ColumnExp(scols[0], se, null);
416: ParamExp rootParam = new ParamExp(scols[0].jdbcType, null);
417: columnExp.next = rootParam;
418: for (int i = 0; i < (size - 1); i++) {
419: rootParam.next = new ParamExp(scols[0].jdbcType, null);
420: rootParam = (ParamExp) rootParam.next;
421: }
422: return new InExp(columnExp);
423: }
424:
425: /**
426: * /**
427: * Append a 'cola = ? [and colb = ?' string for an array of simple cols
428: * to s.
429: */
430: public static void appendEqualsParam(CharBuf s, JdbcColumn[] cols,
431: SqlDriver driver) {
432: int nc = cols.length;
433: JdbcColumn sc = cols[0];
434: s.append(sc.name);
435: s.append(' ');
436: s.append('=');
437: s.append(' ');
438: driver.appendWhereParam(s, sc);
439: for (int i = 1; i < nc; i++) {
440: s.append(" AND ");
441: sc = cols[i];
442: s.append(sc.name);
443: s.append(' ');
444: s.append('=');
445: s.append(' ');
446: driver.appendWhereParam(s, sc);
447: }
448: }
449:
450: /**
451: * Get the value of this column from a ResultSet.
452: */
453: public Object get(ResultSet rs, int index) throws SQLException {
454: if (converter == null) {
455: return JdbcUtils.get(rs, index, javaTypeCode, scale);
456: } else {
457: return converter.get(rs, index, this );
458: }
459: }
460:
461: /**
462: * Set a value for this column on a PreparedStatement.
463: */
464: public void set(PreparedStatement ps, int index, Object value)
465: throws SQLException {
466: if (converter == null) {
467: JdbcUtils.set(ps, index, value, javaTypeCode, jdbcType);
468: } else {
469: converter.set(ps, index, this , value);
470: }
471: }
472:
473: /**
474: * Set a value for this column on a PreparedStatement.
475: */
476: public void set(PreparedStatement ps, int index, int value)
477: throws SQLException {
478: if (converter == null) {
479: JdbcUtils.set(ps, index, value, javaTypeCode, jdbcType);
480: } else {
481: converter.set(ps, index, this , value);
482: }
483: }
484:
485: /**
486: * Get the value of this column from the ResultSet as an int.
487: */
488: public int getInt(ResultSet rs, int index) throws SQLException {
489: if (converter == null) {
490: return rs.getInt(index);
491: } else {
492: return ((Integer) converter.get(rs, index, this ))
493: .intValue();
494: }
495: }
496:
497: /**
498: * Append a comma list of our column name(s) to s.
499: */
500: public void appendNames(CharBuf s) {
501: s.append(name);
502: }
503:
504: /**
505: * Append a comma param list to s (e.g. ?, ?).
506: */
507: public void appendParams(CharBuf s) {
508: s.append('?');
509: }
510:
511: /**
512: * Create a col = ? expression.
513: */
514: public SqlExp createEqualsParamExp(SelectExp se) {
515: return new BinaryOpExp(new ColumnExp(this , se, null),
516: BinaryOpExp.EQUAL, new ParamExp(jdbcType, null));
517: }
518:
519: /**
520: * Must this column be included in update and insert statements?
521: */
522: public boolean isForUpdate() {
523: return !shared;
524: }
525:
526: public String toString() {
527: StringBuffer s = new StringBuffer();
528: s.append(javaType == null ? "(null javaType)" : javaType
529: .getName());
530: s.append(' ');
531: s.append(name);
532: s.append(' ');
533: s.append(sqlType);
534: s.append('[');
535: s.append(JdbcTypes.toString(jdbcType));
536: s.append(']');
537: if (length != 0 || scale != 0) {
538: s.append('(');
539: s.append(length);
540: if (scale != 0) {
541: s.append(',');
542: s.append(scale);
543: }
544: s.append(')');
545: }
546: s.append(nulls ? " null" : " not null");
547: if (refField != null) {
548: s.append(" ref ");
549: s.append(refField);
550: }
551: s.append(shared ? " shared" : "");
552: s.append(autoinc ? " autoinc" : "");
553: if (Debug.DEBUG) {
554: s.append(" 0x");
555: s
556: .append(Integer.toHexString(System
557: .identityHashCode(this )));
558: }
559: return s.toString();
560: }
561:
562: public void setShared(boolean shared) {
563: this .shared = shared;
564: }
565:
566: public String getShortName() {
567: StringBuffer buffer = new StringBuffer(name);
568: buffer.append(" ");
569: buffer.append(sqlType);
570: if (length > 0) {
571: buffer.append("(");
572: buffer.append(length);
573: buffer.append(")");
574: }
575: return buffer.toString();
576: }
577:
578: public String getTypeString() {
579: StringBuffer buffer = new StringBuffer();
580: buffer.append(sqlType);
581: if (length > 0) {
582: buffer.append("(");
583: buffer.append(length);
584: buffer.append(")");
585: }
586: return buffer.toString();
587: }
588:
589: /**
590: * Create a Literal Exp for a classIdColumn
591: */
592: public LiteralExp createClassIdLiteralExp(Object val) {
593: return new LiteralExp(JdbcTypes.getLiteralType(javaTypeCode),
594: val.toString());
595: }
596:
597: public boolean equals(Object o) {
598: if (this == o)
599: return true;
600: if (!(o instanceof JdbcColumn))
601: return false;
602:
603: final JdbcColumn jdbcColumn = (JdbcColumn) o;
604:
605: if (name != null ? !name.equals(jdbcColumn.name)
606: : jdbcColumn.name != null)
607: return false;
608: if (table != null ? !table.equals(jdbcColumn.table)
609: : jdbcColumn.table != null)
610: return false;
611:
612: return true;
613: }
614:
615: public int hashCode() {
616: int result;
617: result = (table != null ? table.hashCode() : 0);
618: result = 29 * result + (name != null ? name.hashCode() : 0);
619: return result;
620: }
621: }
|