/** * Test that implicit schema creation of other schemas besides the initial default schema is still * transactional. */ public void testOtherImplicitSchemaCreation() throws SQLException { Connection c1 = openUserConnection("newuser"); c1.setAutoCommit(false); Statement s1 = c1.createStatement(); // Will auto-create schema OTHERSCHEMA: s1.executeUpdate("create table otherschema.t1(i int)"); s1.close(); JDBC.assertSingleValueResultSet( c1.createStatement() .executeQuery( "select schemaname from sys.sysschemas " + "where schemaname='OTHERSCHEMA'"), "OTHERSCHEMA"); c1.rollback(); JDBC.assertEmpty( c1.createStatement() .executeQuery( "select schemaname from sys.sysschemas " + "where schemaname='OTHERSCHEMA'")); c1.rollback(); c1.close(); }
/** * Test that we recover from self locking in the auto-create nested transaction (cf solution for * DERBY-48). */ public void testDerby48SelfLockingRecovery() throws SQLException { Connection c1 = openUserConnection("newuser"); c1.setAutoCommit(false); c1.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); Statement s1 = c1.createStatement(); // Set read locks in parent transaction s1.executeQuery("select count(*) from sys.sysschemas"); // ..which conflicts with the auto-create in a subtransaction // which will self-lock here, but should recover to try again // in outer transaction: s1.executeUpdate("create table t1(i int)"); JDBC.assertSingleValueResultSet( s1.executeQuery("select schemaname from sys.sysschemas " + "where schemaname='NEWUSER'"), "NEWUSER"); c1.rollback(); // Since the fallback does the auto-create of the schema in // the outer transaction, a rollback will remove it: JDBC.assertEmpty(s1.executeQuery("select * from sys.sysschemas where schemaname='NEWUSER'")); c1.rollback(); }
/** Test whether we can reconnect. */ public void testConnectRepeatedly() throws SQLException { String url = "jdbc:splice:wombat;create=true"; Connection con = DriverManager.getConnection(url); Statement st = con.createStatement(); JDBC.assertSingleValueResultSet(st.executeQuery("values 1"), "1"); st.close(); con.close(); // Can we reconnect? con = DriverManager.getConnection(url); st = con.createStatement(); JDBC.assertSingleValueResultSet(st.executeQuery("values 1"), "1"); st.close(); con.close(); }
public static Test suite() { if (JDBC.vmSupportsJSR169()) // Test uses DriverManager which // is not supported with JSR169 return new TestSuite("empty ConnectTest:DriverManager not supported"); else { TestSuite suite = new TestSuite("ConnectTest suite"); suite.addTest(TestConfiguration.defaultSuite(ConnectTest.class)); // Derby2026 test uses explicit client connection so not relevant to embedded suite.addTest( TestConfiguration.clientServerDecorator( new ConnectTest("clientTestDerby2026LoginTimeout"))); return new CleanDatabaseTestSetup(suite); } }
/** Reproduces hang seen in DERBY-48 */ public void testDerby48testNewSchemaHang() throws SQLException { Connection c1 = openUserConnection("newuser"); c1.setAutoCommit(false); Statement s1 = c1.createStatement(); // Will auto-create schema NEWUSER: s1.executeUpdate("create table t1(i int)"); s1.close(); // DERBY-48: The next connect causes a hang on write lock the // new schema row being created by c1 that is not yet // committed if the fix for DERBY-48 is not yet in place. // The fix makes the the auto-create happen in a nested transaction // which commit immediately, so the hang should not be present. Connection c2 = null; try { c2 = openUserConnection("newuser"); } catch (SQLException e) { if (e.getSQLState().equals(LOCK_TIMEOUT)) { c1.rollback(); c1.close(); fail("DERBY-48 still seen", e); } else { throw e; } } c1.rollback(); // Since the auto-create happened in a nested transaction // which has committed, the schema should still be around // after the rollback. Note that this is a side-effect of the // fix for DERBY-48, not required behavior for SQL, but it is // user visible behavior, so we test it here to make sure that // patch works as intended: JDBC.assertSingleValueResultSet( c1.createStatement() .executeQuery("select schemaname from sys.sysschemas " + "where schemaname='NEWUSER'"), "NEWUSER"); c1.rollback(); c1.close(); c2.close(); }
/** * Test that we do get to see the self locking in the auto-create nested transaction (cf solution * for DERBY-48) when deadlock detection is on, i.e. 40XL2 (LOCK_TIMEOUT_LOG) rather than 40XL1 * (LOCK_TIMEOUT) happens. * * <p>After fix for DERBY-5564 LOCK_TIMEOUT will be returned whether diagnostics are on or not. */ public void testDerby48SelfLockingRecoveryDeadlockDetectionOn() throws SQLException { Connection c1 = openUserConnection("newuser"); c1.setAutoCommit(false); c1.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); Statement s1 = c1.createStatement(); // Set read locks in parent transaction s1.executeQuery("select count(*) from sys.sysschemas"); // ..which conflicts with the auto-create in a subtransaction // which will self-lock here, but should throw now: // in outer transaction: try { s1.executeUpdate("create table t1(i int)"); fail("Expected exception " + LOCK_TIMEOUT); } catch (SQLException e) { assertSQLState("Expected state: ", LOCK_TIMEOUT, e); } JDBC.assertEmpty(s1.executeQuery("select * from sys.sysschemas where schemaname='NEWUSER'")); c1.rollback(); }
public void testDerby3043CheckConstraint() throws SQLException { // Demonstrate the DERBY-3043 workaround: if the table name is // schema-qualified, check constraints do not cause a problem, // and the named schema is automatically created if it does // not yet exist: Connection c0 = openUserConnection("frogs"); Statement s0 = c0.createStatement(); JDBC.assertEmpty(s0.executeQuery("select * from sys.sysschemas where schemaname='FROGS'")); JDBC.assertEmpty(s0.executeQuery("select * from sys.sysschemas where schemaname='NOSUCH'")); // A simple example, which should work whether or not the // DERBY-3043 fix is in place s0.executeUpdate( "create table frogs.users2(username varchar(16) " + "CHECK(LENGTH(username)>7))"); // Demonstrate that any schema is lazy-created, not just the // default schema which matches the username: s0.executeUpdate( "create table nosuch.users(username varchar(16) " + "CHECK(LENGTH(username)>7))"); // Schemas FROGS and NOSUCH have been lazy-created: JDBC.assertSingleValueResultSet( s0.executeQuery("select schemaname from sys.sysschemas " + "where schemaname='FROGS'"), "FROGS"); JDBC.assertSingleValueResultSet( s0.executeQuery("select schemaname from sys.sysschemas " + "where schemaname='NOSUCH'"), "NOSUCH"); c0.close(); // Now verify that the test cases from DERBY-3043 pass: Connection c1 = openUserConnection("blogs"); Statement s1 = c1.createStatement(); // At the beginning, the schema 'blogs' does not exist. JDBC.assertEmpty(s1.executeQuery("select * from sys.sysschemas where schemaname='BLOGS'")); // Should work, but without the DERBY-3043 fix will get a // "Schema blogs does not exist" error s1.executeUpdate("create table users(username varchar(16) " + "CHECK(LENGTH(username)>7))"); // Another slightly more complicated example, which requires // the DERBY-3043 fix again to work. s1.executeUpdate( "CREATE TABLE BLOGSCOM__BLOGS__USERS(" + "PK INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY," + "username VARCHAR(16) NOT NULL " + " CONSTRAINT BLOGSCOM__BLOGS__USERS_UNIQUE_username UNIQUE " + " CONSTRAINT BLOGSCOM__BLOGS__USERS_PASSWORD_username " + " CHECK(LENGTH(username)>7)," + "password VARCHAR (32672) NOT NULL , " + "PRIMARY KEY(PK))"); // Schema BLOGS should have been lazy-created: JDBC.assertSingleValueResultSet( s1.executeQuery("select schemaname from sys.sysschemas " + "where schemaname='BLOGS'"), "BLOGS"); c1.close(); }
/** * JUnit test which checks that only expected methods throw SQLFeatureNotSupporteException. As * currently compiled, this class does not object to a handful of mandatory LOB-supporting methods * which Derby does not implement. You can expose these methods by setting the STRICT_ENFORCEMENT * constant to true. */ public class UnsupportedVetter extends BaseJDBCTestCase { ///////////////////////////////////////////////////////////// // // CONSTANTS // ///////////////////////////////////////////////////////////// public static final String SQL_PACKAGE_NAME = "java.sql"; private static final boolean STRICT_ENFORCEMENT = false; ///////////////////////////////////////////////////////////// // // STATE // ///////////////////////////////////////////////////////////// // // Table of methods which are allowed to raise // SQLFeatureNotSupportedException. Derived from the 1.6 Javadoc. // private static Exclusions[] rawExcludables = new Exclusions[] { new Exclusions( java.sql.Connection.class, new MD[] { new MD("createArrayOf", new Class[] {String.class, Object[].class}), new MD("createNClob", new Class[] {}), new MD("createSQLXML", new Class[] {}), new MD("createStruct", new Class[] {String.class, Object[].class}), new MD("getNetworkTimeout", new Class[] {}, JDBC.vmSupportsJDBC41()), new MD("getTypeMap", new Class[] {}), new MD("prepareStatement", new Class[] {String.class, int[].class}), new MD("prepareStatement", new Class[] {String.class, String[].class}), new MD( "setNetworkTimeout", new Class[] {java.util.concurrent.Executor.class, int.class}, JDBC.vmSupportsJDBC41()), new MD("setTypeMap", new Class[] {Map.class}), }), new Exclusions( java.sql.Statement.class, new MD[] { new MD("cancel", new Class[] {}), new MD("execute", new Class[] {String.class, int[].class}), new MD("execute", new Class[] {String.class, String[].class}), new MD("executeUpdate", new Class[] {String.class, int[].class}), new MD("executeUpdate", new Class[] {String.class, String[].class}) }), new Exclusions( java.sql.PreparedStatement.class, new MD[] { new MD("setArray", new Class[] {int.class, java.sql.Array.class}), new MD("setNCharacterStream", new Class[] {int.class, java.io.Reader.class}), new MD( "setNCharacterStream", new Class[] {int.class, java.io.Reader.class, long.class}), new MD("setNClob", new Class[] {int.class, NClob.class}), new MD("setNClob", new Class[] {int.class, java.io.Reader.class}), new MD("setNClob", new Class[] {int.class, java.io.Reader.class, long.class}), new MD("setNString", new Class[] {int.class, String.class}), new MD("setRef", new Class[] {int.class, Ref.class}), new MD("setRowId", new Class[] {int.class, RowId.class}), new MD("setSQLXML", new Class[] {int.class, SQLXML.class}), new MD("setURL", new Class[] {int.class, URL.class}), new MD("setNull", new Class[] {int.class, int.class, String.class}), new MD("setUnicodeStream", new Class[] {int.class, InputStream.class, int.class}), }), new Exclusions( java.sql.CallableStatement.class, new MD[] { // // THE FOLLOWING METHODS ARE MANDATORY ACCORDING TO THE // JDBC SPEC. HOWEVER, DERBY DOES NOT IMPLEMENT THEM IN ONE // OR THE OTHER OF OUR CLIENTS. // new FD("getBlob", new Class[] {int.class}), new FD("getClob", new Class[] {int.class}), // // According to the JDBC4 spec and javadoc, the following // methods are optional and do not have to be implemented. // new MD("getArray", new Class[] {int.class}), new MD("getArray", new Class[] {String.class}), new MD("getBigDecimal", new Class[] {String.class}), new MD("getBoolean", new Class[] {String.class}), new MD("getBlob", new Class[] {String.class}), new MD("getBoolean", new Class[] {String.class}), new MD("getByte", new Class[] {String.class}), new MD("getBytes", new Class[] {String.class}), new MD("getCharacterStream", new Class[] {String.class}), new MD("getClob", new Class[] {String.class}), new MD("getDate", new Class[] {String.class}), new MD("getDate", new Class[] {String.class, Calendar.class}), new MD("getDouble", new Class[] {String.class}), new MD("getFloat", new Class[] {String.class}), new MD("getInt", new Class[] {String.class}), new MD("getLong", new Class[] {String.class}), new MD("getNCharacterStream", new Class[] {int.class}), new MD("getNCharacterStream", new Class[] {String.class}), new MD("getNClob", new Class[] {int.class}), new MD("getNClob", new Class[] {String.class}), new MD("getNString", new Class[] {int.class}), new MD("getNString", new Class[] {String.class}), new MD("getObject", new Class[] {String.class}), new MD("getObject", new Class[] {String.class, Class.class}, JDBC.vmSupportsJDBC41()), new MD("getRef", new Class[] {int.class}), new MD("getRef", new Class[] {String.class}), new MD("getRowId", new Class[] {int.class}), new MD("getRowId", new Class[] {String.class}), new MD("getShort", new Class[] {String.class}), new MD("getSQLXML", new Class[] {int.class}), new MD("getSQLXML", new Class[] {String.class}), new MD("getString", new Class[] {String.class}), new MD("getTime", new Class[] {String.class}), new MD("getTime", new Class[] {String.class, java.util.Calendar.class}), new MD("getTimestamp", new Class[] {String.class}), new MD("getTimestamp", new Class[] {String.class, java.util.Calendar.class}), new MD("getURL", new Class[] {int.class}), new MD("getURL", new Class[] {String.class}), new MD("registerOutParameter", new Class[] {String.class, int.class}), new MD("registerOutParameter", new Class[] {String.class, int.class, int.class}), new MD("registerOutParameter", new Class[] {String.class, int.class, String.class}), new MD("registerOutParameter", new Class[] {int.class, int.class, String.class}), new MD("setArray", new Class[] {int.class, java.sql.Array.class}), new MD("setAsciiStream", new Class[] {String.class, java.io.InputStream.class}), new MD( "setAsciiStream", new Class[] {String.class, java.io.InputStream.class, int.class}), new MD( "setAsciiStream", new Class[] {String.class, java.io.InputStream.class, long.class}), new MD("setBigDecimal", new Class[] {String.class, java.math.BigDecimal.class}), new MD("setBinaryStream", new Class[] {String.class, java.io.InputStream.class}), new MD( "setBinaryStream", new Class[] {String.class, java.io.InputStream.class, int.class}), new MD( "setBinaryStream", new Class[] {String.class, java.io.InputStream.class, long.class}), new MD("setBlob", new Class[] {String.class, java.io.InputStream.class}), new MD("setBlob", new Class[] {String.class, java.io.InputStream.class, long.class}), new MD("setBlob", new Class[] {String.class, Blob.class}), new MD("setBoolean", new Class[] {String.class, boolean.class}), new MD("setByte", new Class[] {String.class, byte.class}), new MD("setBytes", new Class[] {String.class, byte[].class}), new MD("setCharacterStream", new Class[] {String.class, java.io.Reader.class}), new MD( "setCharacterStream", new Class[] {String.class, java.io.Reader.class, int.class}), new MD( "setCharacterStream", new Class[] {String.class, java.io.Reader.class, long.class}), new MD("setClob", new Class[] {String.class, java.io.Reader.class}), new MD("setClob", new Class[] {String.class, java.io.Reader.class, long.class}), new MD("setClob", new Class[] {String.class, Clob.class}), new MD("setDate", new Class[] {String.class, java.sql.Date.class}), new MD("setDate", new Class[] {String.class, java.sql.Date.class, Calendar.class}), new MD("setDouble", new Class[] {String.class, double.class}), new MD("setFloat", new Class[] {String.class, float.class}), new MD("setInt", new Class[] {String.class, int.class}), new MD("setLong", new Class[] {String.class, long.class}), new MD( "setNCharacterStream", new Class[] {int.class, java.io.Reader.class, long.class}), new MD("setNCharacterStream", new Class[] {String.class, java.io.Reader.class}), new MD( "setNCharacterStream", new Class[] {String.class, java.io.Reader.class, long.class}), new MD("setNClob", new Class[] {int.class, java.io.Reader.class, long.class}), new MD("setNClob", new Class[] {int.class, NClob.class}), new MD("setNClob", new Class[] {String.class, java.io.Reader.class}), new MD("setNClob", new Class[] {String.class, java.io.Reader.class, long.class}), new MD("setNClob", new Class[] {String.class, NClob.class}), new MD("setNString", new Class[] {int.class, String.class}), new MD("setNString", new Class[] {String.class, String.class}), new MD("setNull", new Class[] {String.class, int.class}), new MD("setNull", new Class[] {String.class, int.class, String.class}), new MD("setObject", new Class[] {String.class, Object.class}), new MD("setObject", new Class[] {String.class, Object.class, int.class}), new MD("setObject", new Class[] {String.class, Object.class, int.class, int.class}), new MD("setRef", new Class[] {int.class, Ref.class}), new MD("setRowId", new Class[] {int.class, RowId.class}), new MD("setRowId", new Class[] {String.class, RowId.class}), new MD("setSQLXML", new Class[] {int.class, SQLXML.class}), new MD("setSQLXML", new Class[] {String.class, SQLXML.class}), new MD("setShort", new Class[] {String.class, short.class}), new MD("setString", new Class[] {String.class, String.class}), new MD("setTime", new Class[] {String.class, Time.class}), new MD("setTime", new Class[] {String.class, Time.class, Calendar.class}), new MD("setTimestamp", new Class[] {String.class, Timestamp.class}), new MD("setTimestamp", new Class[] {String.class, Timestamp.class, Calendar.class}), new MD("setURL", new Class[] {int.class, URL.class}), new MD("setURL", new Class[] {String.class, URL.class}) }), new Exclusions( java.sql.ResultSet.class, new MD[] { // // THE FOLLOWING METHODS ARE MANDATORY ACCORDING TO THE // JDBC SPEC. HOWEVER, DERBY DOES NOT IMPLEMENT THEM IN ONE // OR THE OTHER OF OUR CLIENTS. // new FD("updateNClob", new Class[] {int.class, Reader.class, long.class}), new FD("updateNClob", new Class[] {String.class, Reader.class, long.class}), // // According to the JDBC4 spec and javadoc, the following // methods are optional and do not have to be implemented. // new MD("getNCharacterStream", new Class[] {int.class}), new MD("getNCharacterStream", new Class[] {String.class}), new MD("getNString", new Class[] {int.class}), new MD("getNString", new Class[] {String.class}), new MD("getURL", new Class[] {int.class}), new MD("getURL", new Class[] {String.class}), new MD("getArray", new Class[] {int.class}), new MD("getArray", new Class[] {String.class}), new MD("getNClob", new Class[] {int.class}), new MD("getNClob", new Class[] {String.class}), new MD("getRef", new Class[] {int.class}), new MD("getRef", new Class[] {String.class}), new MD("getRowId", new Class[] {int.class}), new MD("getRowId", new Class[] {String.class}), new MD("getSQLXML", new Class[] {int.class}), new MD("getSQLXML", new Class[] {String.class}), new MD("getUnicodeStream", new Class[] {int.class}), new MD("getUnicodeStream", new Class[] {String.class}), new MD("refreshRow", new Class[] {}), new MD("updateArray", new Class[] {int.class, java.sql.Array.class}), new MD("updateArray", new Class[] {String.class, java.sql.Array.class}), new MD("updateNCharacterStream", new Class[] {int.class, java.io.Reader.class}), new MD( "updateNCharacterStream", new Class[] {int.class, java.io.Reader.class, long.class}), new MD("updateNCharacterStream", new Class[] {String.class, java.io.Reader.class}), new MD( "updateNCharacterStream", new Class[] {String.class, java.io.Reader.class, long.class}), new MD("updateNClob", new Class[] {int.class, NClob.class}), new MD("updateNClob", new Class[] {int.class, Reader.class}), new MD("updateNClob", new Class[] {String.class, NClob.class}), new MD("updateNClob", new Class[] {String.class, Reader.class}), new MD("updateNString", new Class[] {int.class, String.class}), new MD("updateNString", new Class[] {String.class, String.class}), new MD("updateRef", new Class[] {int.class, Ref.class}), new MD("updateRef", new Class[] {String.class, Ref.class}), new MD("updateRowId", new Class[] {int.class, RowId.class}), new MD("updateRowId", new Class[] {String.class, RowId.class}), new MD("updateSQLXML", new Class[] {int.class, SQLXML.class}), new MD("updateSQLXML", new Class[] {String.class, SQLXML.class}) }), // // Lance Andersen, spec lead for JDBC4, says: // If you support a datatype, then you have to implement // all of its methods. // }; // // This is the Hashtable where we keep the exclusions. // private static Hashtable<Class, HashSet<Method>> excludableMap; ///////////////////////////////////////////////////////////// // // CONSTRUCTOR // ///////////////////////////////////////////////////////////// /** Creates a new instance. */ public UnsupportedVetter() { super("UnsupportedVetter"); } ///////////////////////////////////////////////////////////// // // ENTRY POINTS // ///////////////////////////////////////////////////////////// /** Find all methods in this framework which raise SQLFeatureNotSupportedException. */ public void testSupportedMethods() throws Exception { getTestConfiguration().setVerbosity(true); HashSet<String> vanishedMethodList = new HashSet<String>(); HashSet<String> unsupportedList = new HashSet<String>(); HashSet<String> notUnderstoodList = new HashSet<String>(); // Build map of interfaces to their methods which may raise SQLFeatureNotSupportedException. initializeExcludableMap(vanishedMethodList); vetDataSource(unsupportedList, notUnderstoodList); vetConnectionPooledDataSource(unsupportedList, notUnderstoodList); vetXADataSource(unsupportedList, notUnderstoodList); // // Print methods which behave unexpectedly. // printVanishedMethodList(vanishedMethodList); printUnsupportedList(unsupportedList); printNotUnderstoodList(notUnderstoodList); assertEquals("vanishedMethodList", 0, vanishedMethodList.size()); assertEquals("unsupportedList", 0, unsupportedList.size()); assertEquals("notUnderstoodList", 0, notUnderstoodList.size()); } // // Find all the objects inside the DataSource and vet them. // private void vetDataSource(HashSet<String> unsupportedList, HashSet<String> notUnderstoodList) throws Exception { DataSource ds = JDBCDataSource.getDataSource(); Connection conn = ds.getConnection(); vetObject(ds, unsupportedList, notUnderstoodList); connectionWorkhorse(conn, unsupportedList, notUnderstoodList); } // // Find all the objects inside the ConnectionPooledDataSource and vet them. // private void vetConnectionPooledDataSource( HashSet<String> unsupportedList, HashSet<String> notUnderstoodList) throws Exception { ConnectionPoolDataSource ds = J2EEDataSource.getConnectionPoolDataSource(); PooledConnection pc = ds.getPooledConnection( getTestConfiguration().getUserName(), getTestConfiguration().getUserPassword()); Connection conn = pc.getConnection(); vetObject(ds, unsupportedList, notUnderstoodList); vetObject(pc, unsupportedList, notUnderstoodList); connectionWorkhorse(conn, unsupportedList, notUnderstoodList); } // // Find all the objects inside the XADataSource and vet them. // private void vetXADataSource(HashSet<String> unsupportedList, HashSet<String> notUnderstoodList) throws Exception { XADataSource ds = J2EEDataSource.getXADataSource(); XAConnection xaconn = ds.getXAConnection( getTestConfiguration().getUserName(), getTestConfiguration().getUserPassword()); Connection conn = xaconn.getConnection(); vetObject(ds, unsupportedList, notUnderstoodList); vetObject(xaconn, unsupportedList, notUnderstoodList); connectionWorkhorse(conn, unsupportedList, notUnderstoodList); } // // Find all the methods for java.sql objects in the Connection which raise // SQLFeatureNotSupportedException. // private void connectionWorkhorse( Connection conn, HashSet<String> unsupportedList, HashSet<String> notUnderstoodList) throws Exception { vetSavepoint(conn, unsupportedList, notUnderstoodList); vetLargeObjects(conn, unsupportedList, notUnderstoodList); DatabaseMetaData dbmd = conn.getMetaData(); PreparedStatement ps = conn.prepareStatement("select * from sys.systables where tablename = ?"); ps.setString(1, "foo"); ParameterMetaData parameterMetaData = ps.getParameterMetaData(); ResultSet rs = ps.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); Statement stmt = conn.createStatement(); CallableStatement cs = conn.prepareCall("CALL SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(0)"); ParameterMetaData csmd = cs.getParameterMetaData(); // // The vetObject() method calls all of the methods in these objects // in a deterministic order, calling the close() method last. // Inspect these objects in an order which respects the fact that // the objects are closed as a result of calling vetObject(). // vetObject(dbmd, unsupportedList, notUnderstoodList); vetObject(stmt, unsupportedList, notUnderstoodList); vetObject(csmd, unsupportedList, notUnderstoodList); vetObject(cs, unsupportedList, notUnderstoodList); vetObject(rsmd, unsupportedList, notUnderstoodList); vetObject(rs, unsupportedList, notUnderstoodList); vetObject(parameterMetaData, unsupportedList, notUnderstoodList); vetObject(ps, unsupportedList, notUnderstoodList); vetObject(conn, unsupportedList, notUnderstoodList); // No need to close the objects. They were closed by vetObject(). } // // Examine Savepoints. // private void vetSavepoint( Connection conn, HashSet<String> unsupportedList, HashSet<String> notUnderstoodList) throws Exception { conn.setAutoCommit(false); Savepoint sp = conn.setSavepoint(); vetObject(sp, unsupportedList, notUnderstoodList); conn.releaseSavepoint(sp); } // // Examine BLOBs and CLOBs. // private void vetLargeObjects( Connection conn, HashSet<String> unsupportedList, HashSet<String> notUnderstoodList) throws Exception { Statement stmt = conn.createStatement(); stmt.execute("CREATE TABLE t (id INT PRIMARY KEY, " + "b BLOB(10), c CLOB(10))"); stmt.execute( "INSERT INTO t (id, b, c) VALUES (1, " + "CAST (" + TestUtil.stringToHexLiteral("101010001101") + "AS BLOB(10)), CAST ('hello' AS CLOB(10)))"); ResultSet rs = stmt.executeQuery("SELECT id, b, c FROM t"); rs.next(); Blob blob = rs.getBlob(2); Clob clob = rs.getClob(3); vetObject(blob, unsupportedList, notUnderstoodList); vetObject(clob, unsupportedList, notUnderstoodList); stmt.close(); conn.rollback(); } ///////////////////////////////////////////////////////////// // // MINIONS // ///////////////////////////////////////////////////////////// // // Initialize the hashtable of methods which are allowed to raise // SQLFeatureNotSupportedException. // private void initializeExcludableMap(HashSet<String> vanishedMethodList) throws Exception { excludableMap = new Hashtable<Class, HashSet<Method>>(); int count = rawExcludables.length; for (int i = 0; i < count; i++) { Exclusions exclusions = rawExcludables[i]; Class<?> iface = exclusions.getInterface(); MD[] mds = exclusions.getExcludedMethods(); int exclusionCount = mds.length; HashSet<Method> excludedMethodSet = new HashSet<Method>(); for (int j = 0; j < exclusionCount; j++) { MD md = mds[j]; if (!md.requiredAtThisLevel()) { continue; } // // If we are strictly enforcing the JDBC standard, // then expose the mandatory methods which we know Derby // doesn't implement. // if (STRICT_ENFORCEMENT && !md.isOptional()) { continue; } Method method = null; try { method = iface.getMethod(md.getMethodName(), md.getArgTypes()); } catch (NoSuchMethodException e) { } if (method == null) { vanishedMethodList.add( "Method has vanished from SQL interface: " + iface.getName() + "." + md); } excludedMethodSet.add(method); } excludableMap.put(iface, excludedMethodSet); } } // // Find all the methods from java.sql interfaces which are implemented by an object // and which raise SQLFeatureNotSupportedException. // private void vetObject( Object candidate, HashSet<String> unsupportedList, HashSet<String> notUnderstoodList) throws Exception { Class myClass = candidate.getClass(); vetInterfaces(candidate, myClass, unsupportedList, notUnderstoodList); } // // Find all the java.sql interfaces implemented by a class and find // the methods in those interfaces which raise // SQLFeatureNotSupportedException when called on the passed-in candidate object. // private void vetInterfaces( Object candidate, Class myClass, HashSet<String> unsupportedList, HashSet<String> notUnderstoodList) throws Exception { Class superClass = myClass.getSuperclass(); if (superClass != null) { vetInterfaces(candidate, superClass, unsupportedList, notUnderstoodList); } // // The contract for Class.getInterfaces() states that the interfaces // come back in a deterministic order, namely, in the order that // they were declared in the "extends" clause. // Class<?>[] interfaces = myClass.getInterfaces(); int interfaceCount = interfaces.length; for (int i = 0; i < interfaceCount; i++) { Class<?> iface = interfaces[i]; if (iface.getPackage().getName().equals(SQL_PACKAGE_NAME)) { vetInterfaceMethods(candidate, iface, unsupportedList, notUnderstoodList); } vetInterfaces(candidate, iface, unsupportedList, notUnderstoodList); } } // // Examine all the methods in an interface to determine which ones // raise SQLFeatureNotSupportedException. // private void vetInterfaceMethods( Object candidate, Class iface, HashSet<String> unsupportedList, HashSet<String> notUnderstoodList) throws Exception { Method[] methods = sortMethods(iface); int methodCount = methods.length; for (int i = 0; i < methodCount; i++) { Method method = methods[i]; vetMethod(candidate, iface, method, unsupportedList, notUnderstoodList); } } // // Return the methods of an interface in a deterministic // order. Class.getMethods() does not do us this favor. // private Method[] sortMethods(Class iface) throws Exception { Method[] raw = iface.getMethods(); int count = raw.length; Method[] cooked = new Method[count]; MethodSortable[] sortables = new MethodSortable[count]; for (int i = 0; i < count; i++) { sortables[i] = new MethodSortable(raw[i]); } Arrays.sort(sortables); for (int i = 0; i < count; i++) { cooked[i] = sortables[i].getMethod(); } return cooked; } // // Examine a single method to see if it raises SQLFeatureNotSupportedException. // private void vetMethod( Object candidate, Class iface, Method method, HashSet<String> unsupportedList, HashSet<String> notUnderstoodList) throws Exception { try { method.invoke(candidate, getNullArguments(method.getParameterTypes())); // it's ok for the method to succeed } catch (Throwable e) { if (!(e instanceof InvocationTargetException)) { recordUnexpectedError(candidate, iface, method, notUnderstoodList, e); } else { Throwable cause = e.getCause(); if (cause instanceof SQLFeatureNotSupportedException) { boolean isExcludable = isExcludable(method); if (!isExcludable) { StackTraceElement[] stack = cause.getStackTrace(); int i = 0; while (i < stack.length && !stack[i].getMethodName().equals("notImplemented")) { ++i; } while (i < stack.length && stack[i].getMethodName().equals("notImplemented")) { ++i; } if (i == stack.length) { // cause.printStackTrace(); } unsupportedList.add( candidate.getClass().getName() + ": " + method + "@" + (i == stack.length ? "no source" : cause.getStackTrace()[i])); } else { } } else if (cause instanceof SQLException) { // swallow other SQLExceptions, caused by bogus args } else if (cause instanceof NullPointerException) { // swallow other NPEs, caused by bogus args } else if (cause instanceof ArrayIndexOutOfBoundsException) { // swallow these, caused by bogus args } else { recordUnexpectedError(candidate, iface, method, notUnderstoodList, cause); } } } } // // Record an unexpected error. // private void recordUnexpectedError( Object candidate, Class iface, Method method, HashSet<String> notUnderstoodList, Throwable cause) throws Exception { notUnderstoodList.add(candidate.getClass().getName() + " " + method + " raises " + cause); } // // Returns true if this method is allowed to raise SQLFeatureNotSupportedException. // private boolean isExcludable(Method method) throws Exception { Class iface = method.getDeclaringClass(); HashSet<Method> excludableMethods = excludableMap.get(iface); if (excludableMethods == null) { return false; } return excludableMethods.contains(method); } /** * Takes an array of classes and returns an array of objects with null values compatible with the * classes. Helper method for converting a parameter list to an argument list. * * @param params a <code>Class[]</code> value * @return an <code>Object[]</code> value */ private Object[] getNullArguments(Class[] params) { Object[] args = new Object[params.length]; for (int i = 0; i < params.length; i++) { args[i] = getNullValueForType(params[i]); } return args; } /** * Returns a null value compatible with the class. For instance, return <code>Boolean.FALSE</code> * for primitive booleans, 0 for primitive integers and <code>null</code> for non-primitive types. * * @param type a <code>Class</code> value * @return a null value */ private Object getNullValueForType(Class type) { if (!type.isPrimitive()) { return null; } if (type == Boolean.TYPE) { return Boolean.FALSE; } if (type == Character.TYPE) { return new Character((char) 0); } if (type == Byte.TYPE) { return new Byte((byte) 0); } if (type == Short.TYPE) { return new Short((short) 0); } if (type == Integer.TYPE) { return new Integer(0); } if (type == Long.TYPE) { return new Long(0L); } if (type == Float.TYPE) { return new Float(0f); } if (type == Double.TYPE) { return new Double(0d); } fail("Don't know how to handle type " + type); return null; // unreachable statement } // debug print the list of methods which throw SQLFeatureNotSupportedException private void printUnsupportedList(HashSet<String> unsupportedList) { int count = unsupportedList.size(); if (count == 0) { return; } println("--------------- UNSUPPORTED METHODS ------------------"); println("--"); String[] result = new String[count]; unsupportedList.toArray(result); Arrays.sort(result); for (int i = 0; i < count; i++) { println(result[i]); } } // debug print the list of methods which have disappeared from the SQL interface private void printVanishedMethodList(HashSet<String> vanishedMethodList) { int count = vanishedMethodList.size(); if (count == 0) { return; } println("--------------- VANISHED METHODS ------------------"); println("--"); String[] result = new String[count]; vanishedMethodList.toArray(result); Arrays.sort(result); for (int i = 0; i < count; i++) { println(result[i]); } } // Debug print the list of method failures which we don't understand private void printNotUnderstoodList(HashSet<String> notUnderstoodList) { int count = notUnderstoodList.size(); if (count == 0) { return; } println("\n\n"); println("--------------- NOT UNDERSTOOD METHODS ------------------"); println("--"); String[] result = new String[count]; notUnderstoodList.toArray(result); Arrays.sort(result); for (int i = 0; i < count; i++) { println(result[i]); } } ///////////////////////////////////////////////////////////// // // INNER CLASSES // ///////////////////////////////////////////////////////////// /** * Method descriptor for optional methods which Derby does not have to implement. We abbreviate * the name of this class to make arrays of these declarations compact and readable. */ public static class MD { private String _methodName; private Class[] _argTypes; private boolean _requiredAtThisLevel; /** Construct from methodName and argument types. */ public MD(String methodName, Class[] argTypes) { this(methodName, argTypes, true); } public MD(String methodName, Class[] argTypes, boolean requiredAtThisLevel) { _methodName = methodName; _argTypes = argTypes; _requiredAtThisLevel = requiredAtThisLevel; } /** Get the name of this method. */ public String getMethodName() { return _methodName; } /** Get the types of the method's arguments */ public Class[] getArgTypes() { return _argTypes; } /** Return whether this method is optional */ public boolean isOptional() { return true; } /** Return whether this method is required at the current JDBC level */ public boolean requiredAtThisLevel() { return _requiredAtThisLevel; } public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append(_methodName); buffer.append("( "); if (_argTypes != null) { int count = _argTypes.length; for (int i = 0; i < count; i++) { if (i > 0) { buffer.append(", "); } buffer.append(_argTypes[i].getName()); } } buffer.append(" )"); return buffer.toString(); } } /** * Method descriptor for mandatory methods which we know Derby does not implement. We abbreviate * the name of this class to make arrays of these declarations compact and readable. */ public static final class FD extends MD { private String _methodName; private Class[] _argTypes; /** Construct from methodName and argument types. */ public FD(String methodName, Class[] argTypes) { super(methodName, argTypes); } /** Return whether this method is optional */ public boolean isOptional() { return false; } } /** * Describes all of the methods for an interface which are allowed to raise * SQLFeatureNotSupportedException. */ public static final class Exclusions { private Class _class; private MD[] _excludedMethods; /** * Construct from the interface and descriptors for the methods which are allowed to raise * SQLFeatureNotSupportedException */ public Exclusions(Class theInterface, MD[] excludedMethods) { _class = theInterface; _excludedMethods = excludedMethods; } /** Get the interface. */ public Class getInterface() { return _class; } /** Get descriptors for the methods which may raise SQLFeatureNotSupportedException. */ public MD[] getExcludedMethods() { return _excludedMethods; } } /** * Used for sorting methods, which don't come back from Class.getMethods() in a deterministic * order. For extra credit, we put the close() method at the end of the sort order so that, when * we invoke the sorted methods, we don't accidentally invalidate the receiver. */ public static final class MethodSortable implements Comparable { private Method _method; /** Conjure out of a Method */ public MethodSortable(Method method) { _method = method; } /** Get the wrapped Method */ public Method getMethod() { return _method; } ////////////////////////////////////////////////// // // Comparable BEHAVIOR // ////////////////////////////////////////////////// public int compareTo(Object other) { MethodSortable that = (MethodSortable) other; boolean thisIsClose = this.isCloseMethod(); boolean thatIsClose = that.isCloseMethod(); // throw the close() method to the end of the sort order if (thisIsClose) { if (thatIsClose) { return 0; } else { return 1; } } else if (thatIsClose) { return -1; } return this.toString().compareTo(that.toString()); } ////////////////////////////////////////////////// // // Object OVERRIDES // ////////////////////////////////////////////////// public String toString() { return _method.toString(); } ////////////////////////////////////////////////// // // MINIONS // ////////////////////////////////////////////////// // Returns true if the wrapped method is close(). private boolean isCloseMethod() { return (toString().startsWith("close()")); } } private boolean savedVerbosity; protected void setUp() { // testSupportedMethods() sets the verbosity, so we need to save the // original verbosity here and restore it in tearDown. savedVerbosity = getTestConfiguration().isVerbose(); } protected void tearDown() throws Exception { getTestConfiguration().setVerbosity(savedVerbosity); super.tearDown(); } public static Test suite() { return TestConfiguration.defaultSuite(UnsupportedVetter.class); } }