001: package velosurf.sql;
002:
003: import java.util.Map;
004: import java.util.HashMap;
005: import java.util.regex.Pattern;
006: import java.util.regex.Matcher;
007: import java.util.regex.PatternSyntaxException;
008: import java.sql.Statement;
009: import java.sql.ResultSet;
010: import java.sql.SQLException;
011: import java.lang.reflect.Method;
012:
013: import velosurf.util.Logger;
014:
015: /**
016: * <p>Contains specific description and behaviour of jdbc drivers.</p>
017: *
018: * <p>Main sources:
019: * <ul><li>http://www.schemaresearch.com/products/srtransport/doc/modules/jdbcconf.html
020: * <li>http://db.apache.org/torque/ and org.apache.torque.adapter classes
021: * </ul></p>
022: *
023: * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
024: */
025:
026: public class DriverInfo {
027: /**
028: * Get a driver info by url and driver class.
029: * @param url database url
030: * @param driverClass driver class
031: * @return driver infos
032: */
033: static public DriverInfo getDriverInfo(String url,
034: String driverClass) {
035: /* always try to use both infos to check for validity */
036: String vendor = null;
037: try {
038: Matcher matcher = Pattern.compile("^jdbc:([^:]+):")
039: .matcher(url);
040: if (matcher.find()) {
041: vendor = matcher.group(1);
042: } else {
043: Logger.warn("Could not guess JDBC vendor from URL "
044: + url);
045: Logger
046: .warn("Please report this issue on Velosurf bug tracker.");
047: }
048: } catch (PatternSyntaxException pse) {
049: Logger.log(pse);
050: }
051: DriverInfo ret = null, ret1 = null, ret2 = null;
052: if (vendor != null) {
053: ret1 = driverByVendor.get(vendor);
054: if (ret1 == null) {
055: Logger.warn("Velosurf doesn't know JDBC vendor '"
056: + vendor + "'. Please contribute!");
057: }
058: }
059: if (driverClass != null)
060: ret2 = driverByClass.get(driverClass);
061:
062: if (ret1 == null && ret2 == null) {
063: String msg = "No driver infos found for: ";
064: if (driverClass != null) {
065: msg += "class " + driverClass + ", ";
066: }
067: if (vendor != null) {
068: msg += "vendor " + vendor;
069: }
070: Logger.warn(msg);
071: Logger
072: .warn("Please contribute! See http://velosurf.sf.net/velosurf/docs/drivers.html");
073: } else if (ret1 != null && ret2 != null) {
074: if (ret1.equals(ret2)) {
075: ret = ret1;
076: } else {
077: Logger.warn("Driver class '" + driverClass
078: + "' and driver vendor '" + vendor
079: + "' do not match!");
080: Logger
081: .warn("Please report this issue on Velosurf bug tracker.");
082: ret = ret2;
083: }
084: } else if (ret1 != null) {
085: if (driverClass != null) {
086: Logger
087: .warn("Driver class '"
088: + driverClass
089: + "' is not referenced in Velosurf as a known driver for vendor '"
090: + vendor + "'");
091: Logger
092: .warn("Please report this issue on Velosurf bug tracker.");
093: /* not even sure this new driver will have the same behaviour... */
094: ret1.drivers = new String[] { driverClass };
095: }
096: ret = ret1;
097: } else if (ret2 != null) {
098: ret = ret2; /* already warned */
099: }
100:
101: if (ret == null) {
102: Logger.warn("Using default driver behaviour...");
103: ret = (DriverInfo) driverByVendor.get("unknown");
104: }
105: return ret;
106: }
107:
108: /**
109: * Driver info constructor.
110: * @param name name
111: * @param jdbcTag jdbc tag
112: * @param drivers array of driver class names
113: * @param pingQuery ping query (e.g. "select 1")
114: * @param caseSensivity default case sensivity policy
115: * @param schemaQuery query to change schema
116: * @param IDGenerationMethod preferred ID generation method
117: * @param lastInsertIDQuery query to get last inserted ID value
118: * @param ignorePattern ignore tables whose name matches this pattern
119: */
120: private DriverInfo(String name, String jdbcTag, String drivers[],
121: String pingQuery, String caseSensivity, String schemaQuery,
122: String IDGenerationMethod, String lastInsertIDQuery,
123: String ignorePattern) {
124: this .name = name;
125: this .jdbcTag = jdbcTag;
126: this .drivers = drivers;
127: this .pingQuery = pingQuery;
128: this .caseSensivity = caseSensivity;
129: this .schemaQuery = schemaQuery;
130: this .IDGenerationMethod = IDGenerationMethod;
131: this .lastInsertIDQuery = lastInsertIDQuery;
132: this .ignorePattern = (ignorePattern == null ? null : Pattern
133: .compile(ignorePattern));
134: // this.IDGenerationQuery = IDGenerationQuery;
135: }
136:
137: /** name of the database vendor */
138: private String name;
139: /** jdbc tag of the database vendor */
140: private String jdbcTag;
141: /** list of driver classes */
142: private String[] drivers;
143: /** ping SQL query */
144: private String pingQuery;
145: /** case-sensivity */
146: private String caseSensivity;
147: /** SQL query to set the current schema */
148: private String schemaQuery;
149: /** ID generation method */
150: private String IDGenerationMethod;
151: /** query used to retrieve the last inserted id */
152: private String lastInsertIDQuery;
153: /** ignore tables matchoing this pattern */
154: private Pattern ignorePattern;
155:
156: // not yet implemented (TODO)
157: // public String IDGenerationQuery; // ID generation query
158:
159: /**
160: * Add a new driver.
161: * @param name name
162: * @param jdbcTag jdbc tag
163: * @param drivers array of driver class names
164: * @param pingQuery ping query (e.g. "select 1")
165: * @param caseSensivity default case sensivity policy
166: * @param schemaQuery query to change schema
167: * @param IDGenerationMethod preferred ID generation method
168: * @param lastInsertIDQuery query to get last inserted ID value
169: * @param ignorePrefix ignore tables whose name matches this pattern
170: */
171: public static void addDriver(String name, String jdbcTag,
172: String drivers[], String pingQuery, String caseSensivity,
173: String schemaQuery, String IDGenerationMethod,
174: String lastInsertIDQuery, String ignorePrefix/*,String IDGenerationQuery*/) {
175: DriverInfo infos = new DriverInfo(name, jdbcTag, drivers,
176: pingQuery, caseSensivity, schemaQuery,
177: IDGenerationMethod, lastInsertIDQuery, ignorePrefix/*,IDGenerationQuery*/);
178: driverByVendor.put(jdbcTag, infos);
179: for (String clazz : drivers) {
180: driverByClass.put(clazz, infos);
181: }
182: }
183:
184: /** map jdbctag -> driver infos. */
185: static private Map<String, DriverInfo> driverByVendor = new HashMap<String, DriverInfo>();
186:
187: /** map driver class -> driver infos. */
188: static private Map<String, DriverInfo> driverByClass = new HashMap<String, DriverInfo>();
189:
190: /**
191: * Get the jdbc tag.
192: * @return jdbc tag
193: */
194: public String getJdbcTag() {
195: return jdbcTag;
196: }
197:
198: /**
199: * Get the list of driver class names.
200: * @return array of driver class names
201: */
202: public String[] getDrivers() {
203: return drivers;
204: }
205:
206: /**
207: * Get the ping query.
208: * @return ping query
209: */
210: public String getPingQuery() {
211: return pingQuery;
212: }
213:
214: /**
215: * Get case sensivity default policy.
216: * @return case sensivity default policy
217: */
218: public String getCaseSensivity() {
219: return caseSensivity;
220: }
221:
222: /**
223: * Get the schema setter query.
224: * @return schema setter query
225: */
226: public String getSchemaQuery() {
227: return schemaQuery;
228: }
229:
230: /**
231: * Get the last inserted id.
232: * @param statement source statement
233: * @return last inserted id (or -1)
234: * @throws SQLException
235: */
236: public long getLastInsertId(Statement statement)
237: throws SQLException {
238: long ret = -1;
239: if ("mysql".equalsIgnoreCase(getJdbcTag())) { /* MySql */
240: try {
241: Method lastInsertId = statement.getClass().getMethod(
242: "getLastInsertID", new Class[0]);
243: ret = ((Long) lastInsertId.invoke(statement,
244: new Object[0])).longValue();
245: } catch (Throwable e) {
246: Logger.log("Could not find last insert id: ", e);
247: }
248: } else {
249: if (lastInsertIDQuery == null) {
250: Logger
251: .error("getLastInsertID is not [yet] implemented for your dbms... Contribute!");
252: } else {
253: ResultSet rs = statement.getConnection()
254: .createStatement().executeQuery(
255: lastInsertIDQuery);
256: rs.next();
257: ret = rs.getLong(1);
258: }
259: }
260: return ret;
261: }
262:
263: /** Check whether to ignore or not this table.
264: *
265: * @param name table name
266: * @return whether to ignore this table
267: */
268: public boolean ignoreTable(String name) {
269: return ignorePattern != null
270: && ignorePattern.matcher(name).matches();
271: }
272:
273: // sources :
274: // http://www.schemaresearch.com/products/srtransport/doc/modules/jdbcconf.html
275: // http://db.apache.org/torque/ and org.apache.torque.adapter classes
276: // and Google of course
277: static {
278: addDriver("Axion", "axiondb",
279: new String[] { "org.axiondb.jdbc.AxionDriver" },
280: "select 1", "TODO", "TODO", "none", null, null);
281: addDriver("Cloudscape", "cloudscape",
282: new String[] { "COM.cloudscape.core.JDBCDriver" },
283: "select 1", "TODO", "TODO", "autoincrement",
284: "VALUES IDENTITY_VAL_LOCAL()", null);
285: addDriver("DB2", "db2", new String[] {
286: "COM.ibm.db2.jdbc.app.DB2Driver",
287: "COM.ibm.db2.jdbc.net.DB2Driver" }, "select 1", "TODO",
288: "TODO", "none", "VALUES IDENTITY_VAL_LOCAL()", null);
289: addDriver(
290: "Derby",
291: "derby",
292: new String[] { "org.apache.derby.jdbc.EmbeddedDriver" },
293: "values 1", "uppercase", "set schema $schema",
294: "autoincrement", null, null);
295: addDriver("Easysoft", "easysoft",
296: new String[] { "easysoft.sql.jobDriver" }, "select 1",
297: "TODO", "TODO", "TODO", null, null);
298: addDriver("Firebird", "firebirdsql",
299: new String[] { "org.firebirdsql.jdbc.FBDriver" },
300: "TODO", "TODO", "TODO", "TODO", null, null);
301: addDriver("Frontbase", "frontbase",
302: new String[] { "jdbc.FrontBase.FBJDriver" },
303: "select 1", "TODO", "TODO", "TODO", null, null);
304: addDriver("HSQLDB", "hsqldb", new String[] {
305: "org.hsqldb.jdbcDriver", "org.hsql.jdbcDriver" },
306: "call 1", "uppercase", "set schema $schema",
307: "autoincrement", "CALL IDENTITY()", "SYSTEM_.*");
308: addDriver("Hypersonic", "hypersonic",
309: new String[] { "org.hsql.jdbcDriver" }, "select 1",
310: "TODO", "TODO", "autoincrement", null, null);
311: addDriver("OpenBase", "openbase",
312: new String[] { "com.openbase.jdbc.ObDriver" },
313: "select 1", "TODO", "TODO", "TODO", null, null);
314: addDriver("Informix", "informix",
315: new String[] { "com.informix.jdbc.IfxDriver" },
316: "select 1", "TODO", "TODO", "none", null, null);
317: addDriver(
318: "InstantDB",
319: "instantdb",
320: new String[] { "org.enhydra.instantdb.jdbc.idbDriver" },
321: "select 1", "TODO", "TODO", "none", null, null);
322: addDriver("Interbase", "interbase",
323: new String[] { "interbase.interclient.Driver" },
324: "select 1", "TODO", "TODO", "none", null, null);
325: addDriver("ODBC", "odbc",
326: new String[] { "sun.jdbc.odbc.JdbcOdbcDriver" },
327: "select 1", "TODO", "TODO", "TODO", null, null);
328: addDriver(
329: "Sql Server",
330: "sqlserver",
331: new String[] {
332: "com.microsoft.jdbc.sqlserver.SQLServerDriver",
333: "com.jnetdirect.jsql.JSQLDriver",
334: "com.merant.datadirect.jdbc.sqlserver.SQLServerDriver" },
335: "select 1", "TODO", "TODO", "autoincrement", null, null);
336: addDriver("MySql", "mysql", new String[] {
337: "com.mysql.jdbc.Driver", "org.gjt.mm.mysql.Driver" },
338: "select 1", "sensitive", null, "autoincrement", null,
339: null);
340: addDriver("OpenBase", "",
341: new String[] { "com.openbase.jdbc.ObDriver" },
342: "select 1", "TODO", "TODO", "TODO", null, null);
343: addDriver("Oracle", "oracle",
344: new String[] { "oracle.jdbc.driver.OracleDriver" },
345: "select 1 from dual", "uppercase",
346: "alter session set current_schema = $schema",
347: "sequence", null, ".*\\/.*");
348: addDriver("PostgreSQL", "postgresql",
349: new String[] { "org.postgresql.Driver" }, "select 1",
350: "lowercase", null, "autoincrement", null, null); // also sequences, but support for autoincrement is better
351: addDriver("SapDB", "sapdb",
352: new String[] { "com.sap.dbtech.jdbc.DriverSapDB" },
353: "select 1 from dual", "uppercase", "TODO", "sequence",
354: null, null);
355: addDriver("Sybase", "sybase",
356: new String[] { "com.sybase.jdbc2.jdbc.SybDriver" },
357: "select 1", "TODO", "TODO", "autoincrement",
358: "SELECT @@IDENTITY", null);
359: addDriver("Weblogic", "weblogic",
360: new String[] { "weblogic.jdbc.pool.Driver" },
361: "select 1", "TODO", "TODO", "none", null, null);
362:
363: // unknwon driver
364: addDriver("Unknown driver", "unknown", new String[] {},
365: "select 1", "sensitive", null, "none", null, null);
366: }
367: }
|