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.jdbc.sql.exp.AndExp;
015: import com.versant.core.jdbc.sql.exp.SelectExp;
016: import com.versant.core.jdbc.sql.exp.SqlExp;
017: import com.versant.core.util.CharBuf;
018: import com.versant.core.jdbc.sql.exp.*;
019: import com.versant.core.jdbc.sql.JdbcNameGenerator;
020: import com.versant.core.jdbc.sql.SqlDriver;
021:
022: import java.io.PrintStream;
023: import java.io.Serializable;
024: import java.util.*;
025:
026: /**
027: * A table in a JDBC database.
028: */
029: public class JdbcTable implements Serializable, Comparable {
030:
031: /**
032: * Cached sql to delete row from table.
033: */
034: public String deleteRowSql;
035:
036: public String name;
037: public SqlDriver sqlDriver;
038: /**
039: * All the columns making up this table in 'create table' order.
040: */
041: public JdbcColumn[] cols;
042: /**
043: * The primary key of this table. Note that these may be compound columns
044: * consisting of multiple JdbcSimpleColumns.
045: */
046: public JdbcColumn[] pk;
047: /**
048: * How many simple columns make up the primary key of this table?
049: */
050: public int pkSimpleColumnCount;
051: /**
052: * The primary key of this table flattened into its JdbcColumn's.
053: */
054: public JdbcColumn[] pkSimpleCols;
055: /**
056: * The name of this tables primary key constraint.
057: */
058: public String pkConstraintName;
059: /**
060: * All the indexes.
061: */
062: public JdbcIndex[] indexes;
063: /**
064: * All the referential integrity constraints.
065: */
066: public JdbcConstraint[] constraints;
067: /**
068: * Comment info for the SQL script (e.g. class or field this table for).
069: */
070: public String comment;
071:
072: private JdbcColumn lockRowColumn;
073:
074: public JdbcTable() {
075: }
076:
077: public List getConstraintList() {
078: if (constraints == null) {
079: return Collections.EMPTY_LIST;
080: }
081: ArrayList list = new ArrayList(constraints.length);
082: for (int i = 0; i < constraints.length; i++) {
083: JdbcConstraint constraint = constraints[i];
084: list.add(constraint);
085: }
086: return list;
087: }
088:
089: public List getColumnList() {
090: if (cols == null) {
091: return Collections.EMPTY_LIST;
092: }
093: ArrayList list = new ArrayList(cols.length);
094: for (int i = 0; i < cols.length; i++) {
095: JdbcColumn col = cols[i];
096: list.add(col);
097: }
098: return list;
099: }
100:
101: public String toString() {
102: return name;
103: }
104:
105: public int hashCode() {
106: return name.hashCode();
107: }
108:
109: /**
110: * Tables with the same name and store are equal.
111: */
112: public boolean equals(Object o) {
113: if (o instanceof JdbcTable) {
114: JdbcTable t = (JdbcTable) o;
115: return name.equals(t.name);
116: } else {
117: return false;
118: }
119: }
120:
121: /**
122: * Sort by name.
123: */
124: public int compareTo(Object o) {
125: return name != null ? o != null ? name
126: .compareTo(((JdbcTable) o).name) : 1 : -1;
127: }
128:
129: /**
130: * Format the primary key as a String for debugging.
131: */
132: public String formatPkString() {
133: if (pk == null)
134: return "(null)";
135: StringBuffer s = new StringBuffer();
136: for (int i = 0; i < pk.length; i++) {
137: if (i > 0)
138: s.append(", ");
139: s.append(pk[i]);
140: }
141: return s.toString();
142: }
143:
144: public void dump() {
145: dump(Debug.OUT, "");
146: }
147:
148: public void dump(PrintStream out, String indent) {
149: out.println(indent + this + " PK " + formatPkString()
150: + " constraint " + pkConstraintName);
151: String is = indent + " ";
152: if (cols != null) {
153: out.println(is + cols.length + " col(s)");
154: for (int i = 0; i < cols.length; i++) {
155: out.println(is + "[" + i + "] " + cols[i]);
156: }
157: }
158: if (indexes != null) {
159: out.println(is + indexes.length + " index(es)");
160: for (int i = 0; i < indexes.length; i++) {
161: out.println(is + "[" + i + "] " + indexes[i]);
162: }
163: }
164: if (constraints != null) {
165: out.println(is + constraints.length + " constraint(s)");
166: for (int i = 0; i < constraints.length; i++) {
167: out.println(is + "[" + i + "] " + constraints[i]);
168: }
169: }
170: }
171:
172: /**
173: * Find a primary key column by name. Returns column found or null if
174: * none.
175: */
176: public JdbcColumn findPkColumn(String columnName) {
177: for (int i = pk.length - 1; i >= 0; i--) {
178: JdbcColumn c = pk[i];
179: if (c.name.equals(columnName))
180: return c;
181: }
182: return null;
183: }
184:
185: /**
186: * Get the names of all the JdbcColumn's in the primary key.
187: */
188: public String[] getPkNames() {
189: int pklen = pk == null ? 0 : pk.length;
190: String[] a = new String[pklen];
191: for (int i = 0; i < pklen; i++) {
192: a[i] = pk[i].name;
193: }
194: return a;
195: }
196:
197: /**
198: * Set the table reference on all of our columns to us.
199: */
200: public void setTableOnCols() {
201: for (int i = 0; i < cols.length; i++)
202: cols[i].table = this ;
203: for (int i = 0; i < pk.length; i++)
204: pk[i].table = this ;
205: }
206:
207: /**
208: * Append part of a where clause for our primary key columns to us (e.g
209: * pk1 = ? and pk2 = ?).
210: */
211: public void appendWherePK(CharBuf s) {
212: JdbcColumn.appendEqualsParam(s, pkSimpleCols, sqlDriver);
213: }
214:
215: /**
216: * Append part of an insert column name list for our primary key columns
217: * to s (e.g 'pk1, pk2').
218: */
219: public void appendInsertPKColumnList(CharBuf s) {
220: int nc = pkSimpleCols.length;
221: s.append(pkSimpleCols[0].name);
222: for (int i = 1; i < nc; i++) {
223: s.append(", ");
224: s.append(pkSimpleCols[i].name);
225: }
226: }
227:
228: /**
229: * Append part of an insert value list for our primary key columns
230: * to s (e.g '?, ?').
231: */
232: public void appendInsertPKValueList(CharBuf s) {
233: s.append("?");
234: int nc = pkSimpleCols.length;
235: for (int i = 1; i < nc; i++)
236: s.append(", ?");
237: }
238:
239: /**
240: * Append part of an insert column name list for all our columns
241: * to s (e.g 'cola, colb, colc').
242: */
243: public void appendInsertColumnList(CharBuf s) {
244: int nc = cols.length;
245: s.append(cols[0].name);
246: for (int i = 1; i < nc; i++) {
247: s.append(", ");
248: s.append(cols[i].name);
249: }
250: }
251:
252: /**
253: * Append part of an insert value list for all our columns
254: * to s (e.g '?, ?, ?').
255: */
256: public void appendInsertValueList(CharBuf s) {
257: s.append("?");
258: int nc = cols.length;
259: for (int i = 1; i < nc; i++)
260: s.append(", ?");
261: }
262:
263: /**
264: * Get a pk = ? expression for this table.
265: */
266: public SqlExp createPkEqualsParamExp(SelectExp se) {
267: int nc = pkSimpleCols.length;
268: if (nc == 1) {
269: return pkSimpleCols[0].createEqualsParamExp(se);
270: } else {
271: SqlExp list = pkSimpleCols[0].createEqualsParamExp(se);
272: SqlExp pos = list;
273: for (int i = 1; i < nc; i++) {
274: pos = pos.next = pkSimpleCols[i]
275: .createEqualsParamExp(se);
276: }
277: return new AndExp(list);
278: }
279: }
280:
281: /**
282: * Get an order by list to order by our primary key.
283: */
284: public SqlExp createOrderByPKList(SelectExp se) {
285: SqlExp ans = pkSimpleCols[0].toSqlExp(se);
286: int nc = pkSimpleCols.length;
287: if (nc > 1) {
288: SqlExp e = ans;
289: for (int i = 1; i < nc; i++) {
290: e = e.next = pkSimpleCols[i].toSqlExp(se);
291: }
292: }
293: return ans;
294: }
295:
296: /**
297: * Set the primary key of this table. This will fill in pkSimpleCols
298: * and pkSimpleColumnCount as well.
299: */
300: public void setPk(JdbcColumn[] pk) {
301: this .pk = pk;
302: pkSimpleCols = pk;
303: pkSimpleColumnCount = pkSimpleCols.length;
304: }
305:
306: /**
307: * Is there a columnName in the primary key of this table?
308: */
309: public boolean isInPrimaryKey(String columnName) {
310: for (int i = pk.length - 1; i >= 0; i--) {
311: if (pk[i].name.equals(columnName))
312: return true;
313: }
314: return false;
315: }
316:
317: /**
318: * Get columns for a create table statement. This will remove any
319: * duplicate and shared columns from cols. If there are multiple
320: * shared=false columns with the same name and one of them is a foreign
321: * key then it is used.
322: */
323: public JdbcColumn[] getColsForCreateTable() {
324: ArrayList a = new ArrayList(cols.length);
325: HashMap map = new HashMap();
326: for (int i = 0; i < cols.length; i++) {
327: JdbcColumn c = cols[i];
328: if (c.shared)
329: continue;
330: JdbcColumn o = (JdbcColumn) map.get(c.name);
331: if (o != null && (o.foreignKey || !c.foreignKey))
332: continue;
333: map.put(c.name, c);
334: a.add(c);
335: }
336: JdbcColumn[] ans = new JdbcColumn[a.size()];
337: a.toArray(ans);
338: return ans;
339: }
340:
341: /**
342: * Get the column that is the best choice for dummy update locking. This
343: * will choose a simple non-indexed non-primary key column if possible.
344: */
345: public JdbcColumn getLockRowColumn() {
346: if (lockRowColumn == null) {
347: lockRowColumn = pk[0];
348: for (int i = cols.length - 1; i >= 0; i--) {
349: lockRowColumn = chooseLockRowCol(cols[i], lockRowColumn);
350: }
351: }
352: return lockRowColumn;
353: }
354:
355: private static JdbcColumn chooseLockRowCol(JdbcColumn a,
356: JdbcColumn b) {
357: // choose a non-primary key column
358: if (!a.pk && b.pk)
359: return a;
360: if (a.pk && !b.pk)
361: return b;
362:
363: // choose an unindexed column
364: if (!a.partOfIndex && b.partOfIndex)
365: return a;
366: if (a.partOfIndex && !b.partOfIndex)
367: return b;
368:
369: // choose a non-foreign key column
370: if (!a.foreignKey && b.foreignKey)
371: return a;
372: if (a.foreignKey && !b.foreignKey)
373: return b;
374:
375: // choose the column that is cheapest to update
376: int diff = JdbcTypes.getUpdateCost(a.jdbcType)
377: - JdbcTypes.getUpdateCost(b.jdbcType);
378: if (diff < 0)
379: return a;
380: if (diff > 0)
381: return b;
382:
383: return b;
384: }
385:
386: public void addConstraints(ArrayList cons) {
387: int length = constraints != null ? constraints.length : 0;
388: int size = cons.size();
389: HashSet set = new HashSet((length + size) * 2);
390: for (int i = 0; i < length; i++) {
391: JdbcConstraint constraint = constraints[i];
392: if (!set.contains(constraint)) {
393: set.add(constraint);
394: }
395: }
396: for (int i = 0; i < size; i++) {
397: Object o = cons.get(i);
398: if (!set.contains(o)) {
399: set.add(o);
400: }
401: }
402: constraints = new JdbcConstraint[set.size()];
403: set.toArray(constraints);
404: }
405:
406: public void nameConstraints(JdbcNameGenerator nameGenerator) {
407: if (constraints != null) {
408: for (int i = 0; i < constraints.length; i++) {
409: JdbcConstraint constraint = constraints[i];
410: if (constraint.name == null) {
411: constraint.generateName(nameGenerator);
412: }
413: }
414: }
415: }
416: }
|