001: /*
002: * ResultInfo.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.storage;
013:
014: import java.sql.Connection;
015: import java.sql.DatabaseMetaData;
016: import java.sql.ResultSet;
017: import java.sql.ResultSetMetaData;
018: import java.sql.SQLException;
019: import java.sql.Types;
020: import java.util.Collection;
021: import java.util.Iterator;
022: import java.util.List;
023:
024: import workbench.db.ColumnIdentifier;
025: import workbench.db.DbMetadata;
026: import workbench.db.TableIdentifier;
027: import workbench.db.WbConnection;
028: import workbench.log.LogMgr;
029: import workbench.util.SqlUtil;
030: import workbench.util.StringUtil;
031:
032: /**
033: * A class to cache the meta information of a ResultSet
034: * @author support@sql-workbench.net
035: */
036: public class ResultInfo {
037: // Cached ResultSetMetaData information
038: final private ColumnIdentifier[] columns;
039: final private int colCount;
040: private int realColumns;
041: private TableIdentifier updateTable;
042:
043: public ResultInfo(ColumnIdentifier[] cols) {
044: this .colCount = cols.length;
045: this .columns = new ColumnIdentifier[this .colCount];
046: for (int i = 0; i < colCount; i++) {
047: this .columns[i] = cols[i].createCopy();
048: }
049: }
050:
051: public ResultInfo(String[] colNames, int[] colTypes, int[] colSizes) {
052: this (colNames, colTypes, colSizes, null);
053: }
054:
055: public ResultInfo(String[] colNames, int[] colTypes,
056: int[] colSizes, String[] columnClasses) {
057: this .colCount = colNames.length;
058: this .columns = new ColumnIdentifier[this .colCount];
059: for (int i = 0; i < colCount; i++) {
060: ColumnIdentifier col = new ColumnIdentifier(colNames[i]);
061: if (colSizes != null)
062: col.setColumnSize(colSizes[i]);
063: col.setDataType(colTypes[i]);
064: if (columnClasses != null)
065: col.setColumnClassName(columnClasses[i]);
066: this .columns[i] = col;
067: }
068: }
069:
070: public ResultInfo(TableIdentifier table, WbConnection conn)
071: throws SQLException {
072: DbMetadata meta = conn.getMetadata();
073: // If the TableIdentifier has no type, we need to find
074: // the type. getTableColumns() uses the type "TABLE" if no
075: // type is passed. This constructor is mainly used when
076: // exporting data, which might not come from a real
077: // table, but could also be a VIEW or a SYNONYM
078: if (table.getType() == null) {
079: String type = meta.getObjectType(table);
080: table.setType(type);
081: }
082: List<ColumnIdentifier> cols = meta.getTableColumns(table);
083: this .columns = new ColumnIdentifier[cols.size()];
084: int i = 0;
085: for (ColumnIdentifier col : cols) {
086: columns[i] = col;
087: i++;
088: }
089: this .colCount = this .columns.length;
090: }
091:
092: public ResultInfo(ResultSetMetaData metaData,
093: WbConnection sourceConnection) throws SQLException {
094: this .colCount = metaData.getColumnCount();
095: this .columns = new ColumnIdentifier[this .colCount];
096: DbMetadata dbMeta = (sourceConnection == null ? null
097: : sourceConnection.getMetadata());
098:
099: for (int i = 0; i < this .colCount; i++) {
100: String name = metaData.getColumnName(i + 1);
101: boolean realColumn = true;
102: if (name != null && name.trim().length() > 0) {
103: this .realColumns++;
104: } else {
105: name = "Col" + (i + 1);
106: realColumn = false;
107: }
108:
109: int type = metaData.getColumnType(i + 1);
110: if (dbMeta != null)
111: type = dbMeta.fixColumnType(type); // currently only for Oracle's DATE type
112: ColumnIdentifier col = new ColumnIdentifier(name);
113: col.setDataType(type);
114: col.setUpdateable(realColumn);
115: try {
116: int nullInfo = metaData.isNullable(i + 1);
117: col
118: .setIsNullable(nullInfo != ResultSetMetaData.columnNoNulls);
119: } catch (Throwable th) {
120: LogMgr.logWarning("ResultInfo.initMetadata()",
121: "Error when checking nullable for column : "
122: + name, th);
123: }
124:
125: String typename = null;
126: try {
127: typename = metaData.getColumnTypeName(i + 1);
128: } catch (Exception e) {
129: typename = null;
130: }
131:
132: if (StringUtil.isEmptyString(typename)) {
133: // use the Java name if the driver did not return a type name for this column
134: typename = SqlUtil.getTypeName(col.getDataType());
135: }
136:
137: col.setColumnTypeName(typename);
138:
139: int scale = 0;
140: int prec = 0;
141:
142: // Some JDBC drivers (e.g. Oracle, MySQL) do not like
143: // getPrecision or getScale() on all column types, so we only call
144: // it for number data types (the only ones were it seems to make sense)
145: try {
146: if (SqlUtil.isNumberType(type))
147: scale = metaData.getScale(i + 1);
148: } catch (Throwable th) {
149: //LogMgr.logError("ResultInfo.<init>", "Error when obtaining scale for column " + name, th);
150: scale = 0;
151: }
152:
153: try {
154: prec = metaData.getPrecision(i + 1);
155: } catch (Throwable th) {
156: //LogMgr.logError("ResultInfo.<init>", "Error when obtaining precision for column " + name, th);
157: prec = 0;
158: }
159:
160: int size = 0;
161: try {
162: size = metaData.getColumnDisplaySize(i + 1);
163: } catch (Exception e) {
164: size = prec;
165: }
166:
167: col.setDecimalDigits(scale);
168: String dbmsType = SqlUtil.getSqlTypeDisplay(typename, col
169: .getDataType(), prec, scale);
170: if (type == Types.VARCHAR) {
171: // HSQL reports the VARCHAR size in displaySize()
172: if (sourceConnection != null
173: && sourceConnection.getDbSettings()
174: .reportsRealSizeAsDisplaySize()) {
175: dbmsType = SqlUtil.getSqlTypeDisplay(typename, col
176: .getDataType(), size, 0);
177: col.setColumnSize(size);
178: } else {
179: // all others seem to report the VARCHAR size in precision
180: col.setColumnSize(prec);
181: }
182: } else {
183: col.setColumnSize(size);
184: }
185: col.setDbmsType(dbmsType);
186:
187: try {
188: String cls = metaData.getColumnClassName(i + 1);
189: col.setColumnClassName(cls);
190: } catch (Throwable e) {
191: col.setColumnClassName("java.lang.Object");
192: }
193: this .columns[i] = col;
194: }
195: }
196:
197: public ColumnIdentifier getColumn(int i) {
198: return this .columns[i];
199: }
200:
201: public ColumnIdentifier[] getColumns() {
202: return this .columns;
203: }
204:
205: public boolean isNullable(int col) {
206: return this .columns[col].isNullable();
207: }
208:
209: public void resetPkColumns() {
210: for (int i = 0; i < this .colCount; i++) {
211: this .columns[i].setIsPkColumn(false);
212: }
213: }
214:
215: public void setIsPkColumn(String column, boolean flag) {
216: int index = this .findColumn(column);
217: if (index > -1)
218: this .setIsPkColumn(index, flag);
219: }
220:
221: public void setIsPkColumn(int col, boolean flag) {
222: this .columns[col].setIsPkColumn(flag);
223: }
224:
225: public boolean hasUpdateableColumns() {
226: return this .realColumns > 0;
227: }
228:
229: public boolean isUpdateable(int col) {
230: return this .columns[col].isUpdateable();
231: }
232:
233: public void setIsNullable(int col, boolean flag) {
234: this .columns[col].setIsNullable(flag);
235: }
236:
237: public void setUpdateable(int col, boolean flag) {
238: this .columns[col].setUpdateable(flag);
239: }
240:
241: public boolean isPkColumn(int col) {
242: return this .columns[col].isPkColumn();
243: }
244:
245: public void setPKColumns(ColumnIdentifier[] cols) {
246: for (int i = 0; i < cols.length; i++) {
247: String name = cols[i].getColumnName();
248: int col = this .findColumn(name);
249: if (col > -1) {
250: boolean pk = cols[i].isPkColumn();
251: this .columns[col].setIsPkColumn(pk);
252: }
253: }
254: }
255:
256: public boolean hasPkColumns() {
257: for (int i = 0; i < this .colCount; i++) {
258: if (this .columns[i].isPkColumn()) {
259: return true;
260: }
261: }
262: return false;
263: }
264:
265: public void setUpdateTable(TableIdentifier table) {
266: this .updateTable = table;
267: }
268:
269: public TableIdentifier getUpdateTable() {
270: return this .updateTable;
271: }
272:
273: public int getColumnSize(int col) {
274: return this .columns[col].getColumnSize();
275: }
276:
277: public void setColumnSizes(int[] sizes) {
278: if (sizes == null)
279: return;
280: if (sizes.length != this .colCount)
281: return;
282: for (int i = 0; i < this .colCount; i++) {
283: this .columns[i].setColumnSize(sizes[i]);
284: }
285: }
286:
287: public int getColumnType(int i) {
288: if (i >= this .columns.length)
289: return Types.OTHER;
290: return this .columns[i].getDataType();
291: }
292:
293: public void setColumnClassName(int i, String name) {
294: this .columns[i].setColumnClassName(name);
295: }
296:
297: public String getColumnClassName(int i) {
298: String className = this .columns[i].getColumnClassName();
299: if (className != null)
300: return className;
301: return this .getColumnClass(i).getName();
302: }
303:
304: public String getColumnName(int i) {
305: return this .columns[i].getColumnName();
306: }
307:
308: public String getDbmsTypeName(int i) {
309: return this .columns[i].getDbmsType();
310: }
311:
312: public int getColumnCount() {
313: return this .colCount;
314: }
315:
316: public Class getColumnClass(int aColumn) {
317: if (aColumn > this .colCount)
318: return null;
319: return this .columns[aColumn].getColumnClass();
320: }
321:
322: public void readPkDefinition(WbConnection aConnection)
323: throws SQLException {
324: if (aConnection == null)
325: return;
326: if (this .updateTable == null)
327: return;
328:
329: Connection sqlConn = aConnection.getSqlConnection();
330: DatabaseMetaData meta = sqlConn.getMetaData();
331: String table = aConnection.getMetadata().adjustObjectnameCase(
332: this .updateTable.getTableName());
333: String schema = aConnection.getMetadata().adjustObjectnameCase(
334: this .updateTable.getSchema());
335:
336: resetPkColumns();
337:
338: ResultSet rs = meta.getPrimaryKeys(null, schema, table);
339: boolean found = this .readPkColumns(rs);
340:
341: if (!found) {
342: found = readPkColumnsFromMapping(aConnection);
343: }
344:
345: return;
346: }
347:
348: public int findColumn(String name) {
349: if (name == null)
350: return -1;
351:
352: String plain = StringUtil.trimQuotes(name);
353:
354: for (int i = 0; i < this .colCount; i++) {
355: String col = StringUtil.trimQuotes(this .getColumnName(i));
356: if (plain.equalsIgnoreCase(StringUtil.trimQuotes(col))) {
357: return i;
358: }
359: }
360:
361: return -1;
362: }
363:
364: public boolean readPkColumnsFromMapping(WbConnection con) {
365: if (this .updateTable == null)
366: return false;
367: Collection cols = PkMapping.getInstance().getPKColumns(con,
368: this .updateTable.createCopy());
369: if (cols == null)
370: return false;
371: Iterator itr = cols.iterator();
372: boolean found = false;
373: while (itr.hasNext()) {
374: String col = (String) itr.next();
375: int index = this .findColumn(col);
376: if (index > -1) {
377: this .setIsPkColumn(index, true);
378: found = true;
379: }
380: }
381: if (found) {
382: LogMgr
383: .logInfo("ResultInfo.readPkColumnsFromMapping()",
384: "Using pk definition for "
385: + updateTable.getTableName()
386: + " from mapping file: "
387: + StringUtil.listToString(cols,
388: ',', false));
389: }
390: return found;
391: }
392:
393: private boolean readPkColumns(ResultSet rs) {
394: boolean found = false;
395: try {
396: while (rs.next()) {
397: String col = rs.getString("COLUMN_NAME");
398: int index = this .findColumn(col);
399: if (index > -1) {
400: this .setIsPkColumn(index, true);
401: found = true;
402: }
403: }
404: } catch (Exception e) {
405: LogMgr.logError("ResultInfo.readPkColumns()",
406: "Error when reading ResultSet for key columns", e);
407: } finally {
408: SqlUtil.closeResult(rs);
409: }
410: return found;
411: }
412:
413: }
|