001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Sam
028: */
029:
030: package com.caucho.jdbc;
031:
032: import com.caucho.util.L10N;
033:
034: import javax.sql.DataSource;
035: import java.sql.DatabaseMetaData;
036: import java.sql.Connection;
037: import java.sql.Types;
038: import java.sql.ResultSet;
039: import java.sql.SQLException;
040: import java.sql.Statement;
041: import java.util.logging.Level;
042: import java.util.logging.Logger;
043:
044: public class GenericMetaData extends JdbcMetaData {
045: private static final Logger log = Logger
046: .getLogger(GenericMetaData.class.getName());
047: private static final L10N L = new L10N(GenericMetaData.class);
048:
049: private String _longType;
050: private String _blobType;
051: private Boolean _supportsPositionFunction;
052: private Boolean _supportsGetGeneratedKeys;
053: private String _falseLiteral;
054:
055: public GenericMetaData(DataSource ds) {
056: super (ds);
057: }
058:
059: /**
060: * Returns the long type.
061: */
062: public String getLongType() {
063: if (_longType != null)
064: return _longType;
065:
066: Connection conn = null;
067:
068: try {
069: conn = getConnection();
070:
071: DatabaseMetaData md = conn.getMetaData();
072: ResultSet rs;
073:
074: rs = md.getTypeInfo();
075: try {
076: while (rs.next()) {
077: if (rs.getShort("DATA_TYPE") == Types.BIGINT) {
078: _longType = rs.getString("TYPE_NAME");
079:
080: return _longType;
081: }
082: }
083: } finally {
084: rs.close();
085: }
086: } catch (SQLException e) {
087: log.log(Level.FINE, e.toString(), e);
088: } finally {
089: try {
090: if (conn != null)
091: conn.close();
092: } catch (SQLException e) {
093: }
094: }
095:
096: return null;
097: }
098:
099: /**
100: * Returns the blob type.
101: */
102: public String getBlobType() {
103: if (_blobType != null)
104: return _blobType;
105:
106: Connection conn = null;
107:
108: try {
109: conn = getConnection();
110:
111: DatabaseMetaData md = conn.getMetaData();
112: ResultSet rs;
113:
114: rs = md.getTypeInfo();
115: try {
116: while (rs.next()) {
117: if (rs.getShort("DATA_TYPE") == Types.BLOB) {
118: _blobType = rs.getString("TYPE_NAME");
119:
120: return _blobType;
121: }
122: }
123: } finally {
124: rs.close();
125: }
126:
127: rs = md.getTypeInfo();
128: try {
129: while (rs.next()) {
130: int dataType = rs.getShort("DATA_TYPE");
131:
132: if (rs.getShort("DATA_TYPE") == Types.LONGVARBINARY) {
133: _blobType = rs.getString("TYPE_NAME");
134: return _blobType;
135: }
136: }
137: } finally {
138: rs.close();
139: }
140:
141: rs = md.getTypeInfo();
142: try {
143: while (rs.next()) {
144: if (rs.getShort("DATA_TYPE") == Types.BINARY) {
145: _blobType = rs.getString("TYPE_NAME");
146: return _blobType;
147: }
148: }
149: } finally {
150: rs.close();
151: }
152:
153: rs = md.getTypeInfo();
154: try {
155: while (rs.next()) {
156: if (rs.getShort("DATA_TYPE") == Types.VARBINARY) {
157: _blobType = rs.getString("TYPE_NAME");
158: return _blobType;
159: }
160: }
161: } finally {
162: rs.close();
163: }
164: } catch (SQLException e) {
165: log.log(Level.FINE, e.toString(), e);
166: } finally {
167: try {
168: if (conn != null)
169: conn.close();
170: } catch (Exception e) {
171: }
172: }
173:
174: return null;
175: }
176:
177: /**
178: * Returns the literal for FALSE.
179: */
180: public String getFalseLiteral() {
181: if (_falseLiteral != null)
182: return _falseLiteral;
183:
184: Connection conn = null;
185:
186: _falseLiteral = "0";
187:
188: try {
189: conn = getConnection();
190:
191: Statement stmt = null;
192:
193: try {
194: stmt = conn.createStatement();
195:
196: ResultSet rs = null;
197:
198: try {
199: rs = stmt.executeQuery("select false");
200:
201: _falseLiteral = "false";
202:
203: } catch (SQLException e) {
204: log.log(Level.FINER, e.toString(), e);
205: } finally {
206: if (rs != null)
207: rs.close();
208: }
209: } finally {
210: if (stmt != null)
211: stmt.close();
212: }
213: } catch (SQLException e) {
214: throw new RuntimeException(e);
215: } finally {
216: try {
217: if (conn != null)
218: conn.close();
219: } catch (Exception e) {
220: log.log(Level.FINE, e.toString(), e);
221: }
222: }
223:
224: return _falseLiteral;
225: }
226:
227: /**
228: * True if the generated keys is supported
229: */
230: public boolean supportsGetGeneratedKeys() {
231: if (_supportsGetGeneratedKeys != null)
232: return _supportsGetGeneratedKeys;
233:
234: try {
235: Connection conn = getConnection();
236:
237: try {
238: DatabaseMetaData metaData = conn.getMetaData();
239:
240: _supportsGetGeneratedKeys = metaData
241: .supportsGetGeneratedKeys();
242:
243: return _supportsGetGeneratedKeys;
244: } finally {
245: conn.close();
246: }
247: } catch (Exception e) {
248: // Possibly older drivers: UnsupportedOperationException.
249: log.log(Level.FINE, e.toString(), e);
250: return false;
251: } catch (AbstractMethodError e) {
252: // Older drivers e.g. SQLServer.
253: log.log(Level.FINE, e.toString(), e);
254: return false;
255: }
256: }
257:
258: /**
259: * Returns true if the POSITION function is supported.
260: */
261: public boolean supportsPositionFunction() {
262: if (_supportsPositionFunction != null)
263: return _supportsPositionFunction;
264:
265: Connection conn = null;
266:
267: _supportsPositionFunction = Boolean.FALSE;
268:
269: try {
270: conn = getConnection();
271:
272: Statement stmt = null;
273:
274: try {
275: stmt = conn.createStatement();
276:
277: ResultSet rs = null;
278:
279: try {
280: rs = stmt
281: .executeQuery("select position('a' in 'abc')");
282:
283: _supportsPositionFunction = Boolean.TRUE;
284:
285: } catch (SQLException e) {
286: log.log(Level.FINER, e.toString(), e);
287: } finally {
288: if (rs != null)
289: rs.close();
290: }
291: } finally {
292: if (stmt != null)
293: stmt.close();
294: }
295: } catch (SQLException e) {
296: throw new RuntimeException(e);
297: } finally {
298: try {
299: if (conn != null)
300: conn.close();
301: } catch (Exception e) {
302: log.log(Level.FINE, e.toString(), e);
303: }
304: }
305:
306: return _supportsPositionFunction;
307: }
308:
309: /**
310: * Returns true if table alias name with UPDATE is supported.
311: */
312: public boolean supportsUpdateTableAlias() {
313: return true;
314: }
315:
316: /**
317: * Returns true if table list with UPDATE is supported:
318: * UPDATE table1 a, table2 b SET ...
319: */
320: public boolean supportsUpdateTableList() {
321: // Normally, MySql is the only one which supports it.
322: return false;
323: }
324:
325: /**
326: * Returns true if identity is supported.
327: */
328: public boolean supportsIdentity() {
329: return false;
330: }
331:
332: /**
333: * Returns the identity property
334: */
335: public String createIdentitySQL(String sqlType) {
336: throw new UnsupportedOperationException("createIdentitySQL");
337: }
338:
339: /**
340: * Returns true if sequences are supported.
341: */
342: public boolean supportsSequences() {
343: return false;
344: }
345:
346: /**
347: * Returns a sequence select expression.
348: */
349: public String createSequenceSQL(String name, int size) {
350: throw new UnsupportedOperationException("createSequenceSQL");
351: }
352:
353: public String selectSequenceSQL(String name) {
354: throw new UnsupportedOperationException("selectSequenceSQL");
355: }
356:
357: /**
358: * to Return SQL for the table with the given
359: * SQL type. Takes, length, precision and scale.
360: */
361: public String getCreateColumnSQL(int sqlType, int length,
362: int precision, int scale) {
363: String type = null;
364:
365: switch (sqlType) {
366: case Types.BOOLEAN:
367: type = getCreateColumnSQLImpl(sqlType, length, precision,
368: scale);
369: if (type == null)
370: type = getCreateColumnSQLImpl(Types.BIT, length,
371: precision, scale);
372: break;
373:
374: case Types.DATE:
375: type = getCreateColumnSQLImpl(sqlType, length, precision,
376: scale);
377: if (type == null)
378: type = getCreateColumnSQLImpl(Types.TIMESTAMP, length,
379: precision, scale);
380: break;
381:
382: case Types.TIME:
383: type = getCreateColumnSQLImpl(sqlType, length, precision,
384: scale);
385: if (type == null)
386: type = getCreateColumnSQLImpl(Types.TIMESTAMP, length,
387: precision, scale);
388: break;
389:
390: case Types.DOUBLE:
391: type = getCreateColumnSQLImpl(Types.DOUBLE, length,
392: precision, scale);
393: break;
394:
395: case Types.NUMERIC:
396: type = getCreateColumnSQLImpl(Types.NUMERIC, length,
397: precision, scale);
398: break;
399:
400: default:
401: type = getCreateColumnSQLImpl(sqlType, length, precision,
402: scale);
403: break;
404: }
405:
406: if (type == null)
407: type = getDefaultCreateTableSQL(sqlType, length, precision,
408: scale);
409:
410: return type;
411: }
412:
413: /**
414: * Returns the SQL for the table with the given SQL type.
415: */
416: protected String getCreateColumnSQLImpl(int sqlType, int length,
417: int precision, int scale) {
418: Connection conn = null;
419:
420: try {
421: conn = getConnection();
422:
423: DatabaseMetaData md = conn.getMetaData();
424: ResultSet rs;
425:
426: rs = md.getTypeInfo();
427:
428: try {
429: while (rs.next()) {
430: if (rs.getShort("DATA_TYPE") == sqlType) {
431: String typeName = rs.getString("TYPE_NAME");
432: String params = rs.getString("CREATE_PARAMS");
433:
434: if (params == null || params.equals(""))
435: return typeName;
436: else if (params.startsWith("(M)")) {
437: if (length > 0)
438: return typeName + "(" + length + ")";
439: else
440: return typeName;
441: } else if (params.startsWith("(M,D)")
442: || params.equals("precision,scale")) {
443: if (precision > 0) {
444: typeName += "(" + precision;
445: if (scale > 0) {
446: typeName += "," + scale;
447: }
448: typeName += ")";
449: }
450: return typeName;
451: } else if (params.startsWith("(")) {
452: int tail = params.indexOf(')');
453:
454: if (tail > 0) {
455: String value = params
456: .substring(1, tail);
457: boolean isConstant = true;
458:
459: for (int i = 0; i < value.length(); i++) {
460: if (value.charAt(i) >= 'a'
461: && value.charAt(i) <= 'z')
462: isConstant = false;
463: else if (value.charAt(i) >= 'A'
464: && value.charAt(i) <= 'Z')
465: isConstant = false;
466: }
467:
468: if (isConstant)
469: return typeName + "(" + value + ")";
470: }
471:
472: return typeName;
473: } else {
474: return typeName;
475: }
476: }
477: }
478: } finally {
479: rs.close();
480: }
481: } catch (Exception e) {
482: log.log(Level.FINE, e.toString(), e);
483: } finally {
484: try {
485: if (conn != null)
486: conn.close();
487: } catch (Exception e) {
488: }
489: }
490:
491: return null;
492: }
493:
494: protected String getDefaultCreateTableSQL(int sqlType, int length,
495: int precision, int scale) {
496: switch (sqlType) {
497: case java.sql.Types.BOOLEAN:
498: return "CHAR";
499:
500: case java.sql.Types.BIT:
501: case java.sql.Types.TINYINT:
502: case java.sql.Types.SMALLINT:
503: case java.sql.Types.INTEGER:
504: case java.sql.Types.BIGINT:
505: return "INTEGER";
506:
507: case java.sql.Types.NUMERIC:
508: case java.sql.Types.DECIMAL:
509: String typeString = "NUMERIC";
510: if (precision > 0) {
511: typeString += "(" + precision;
512: if (scale > 0) {
513: typeString += "," + scale;
514: }
515: typeString += ")";
516: }
517: return typeString;
518:
519: case java.sql.Types.DOUBLE:
520: case java.sql.Types.FLOAT:
521: return "DOUBLE";
522:
523: case java.sql.Types.CHAR:
524: return "CHAR";
525:
526: case java.sql.Types.DATE:
527: case java.sql.Types.TIME:
528: case java.sql.Types.TIMESTAMP:
529: return "TIMESTAMP";
530:
531: default:
532: return "VARCHAR(" + length + ")";
533: }
534: }
535: }
|