You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by dj...@apache.org on 2006/10/19 18:34:26 UTC

svn commit: r465673 - in /db/derby/code/trunk/java/testing/org/apache/derbyTesting: functionTests/tests/lang/PrepareExecuteDDL.java junit/BaseJDBCTestCase.java junit/JDBC.java

Author: djd
Date: Thu Oct 19 09:34:22 2006
New Revision: 465673

URL: http://svn.apache.org/viewvc?view=rev&rev=465673
Log:
DERBY-1976 New & modified utility methods for JDBC JUnit tests to aid conversions of SQL script tests.

Modified:
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/PrepareExecuteDDL.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseJDBCTestCase.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/PrepareExecuteDDL.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/PrepareExecuteDDL.java?view=diff&rev=465673&r1=465672&r2=465673
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/PrepareExecuteDDL.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/PrepareExecuteDDL.java Thu Oct 19 09:34:22 2006
@@ -156,7 +156,7 @@
 		if (isSelectStar)
 			;
 		
-		JDBC.assertDrainResults(rs);
+		JDBC.assertDrainResults(rs, -1);
 	}
 	
 	

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseJDBCTestCase.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseJDBCTestCase.java?view=diff&rev=465673&r1=465672&r2=465673
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseJDBCTestCase.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseJDBCTestCase.java Thu Oct 19 09:34:22 2006
@@ -393,7 +393,10 @@
     }
 
     /**
-     * Assert that SQLState is as expected.
+     * Assert that SQLState is as expected.  If the SQLState for
+     * the top-level exception doesn't match, look for nested
+     * exceptions and, if there are any, see if they have the
+     * desired SQLState.
      *
      * @param message message to print on failure.
      * @param expected the expected SQLState.
@@ -424,7 +427,46 @@
             // Save the SQLException
             // e.initCause(exception);
 
-            throw e;
+            if (usingDerbyNetClient())
+            {
+                /* For chained exceptions the Derby Client just concatenates
+                 * them into the exception message.  So search the message
+                 * for the desired SQLSTATE.  This isn't ideal, but it
+                 * should work...
+                 */
+                if (exception.getMessage().
+                    indexOf("SQLSTATE: " + expected) == -1)
+                {
+                    throw e;
+                }
+            }
+            else if (usingDerbyNet())
+            {
+                /* For JCC the error message is a series of tokens representing
+                 * different things like SQLSTATE, SQLCODE, nested SQL error
+                 * message, and nested SQL state.  Based on observation it
+                 * appears that the last token in the message is the SQLSTATE
+                 * of the nested exception, and it's preceded by a colon.
+                 * So using that (hopefully consistent?) rule, try to find
+                 * the target SQLSTATE.
+                 */
+                String msg = exception.getMessage();
+                if (!msg.substring(msg.lastIndexOf(":")+1)
+                    .trim().equals(expected))
+                {
+                    throw e;
+                }
+            }
+            else
+            {
+                // Check nested exceptions to see if any of them is
+                // the one we're looking for.
+                exception = exception.getNextException();
+                if (exception != null)
+                    assertSQLState(message, expected, exception);
+                else
+                    throw e;
+            }
         }
     }
 
@@ -437,6 +479,7 @@
     public static void assertSQLState(String expected, SQLException exception) {
         assertSQLState("Unexpected SQL state.", expected, exception);
     }
+
     /**
      * Assert that the query does not compile and throws
      * a SQLException with the expected state.
@@ -447,10 +490,146 @@
     public void assertCompileError(String sqlState, String query) {
 
         try {
-            prepareStatement(query).close();
+            PreparedStatement pSt = prepareStatement(query);
+            if (usingDerbyNet())
+            {
+                /* For JCC the prepares are deferred until execution,
+                 * so we have to actually execute in order to see the
+                 * expected error.  Note that we don't need to worry
+                 * about binding the parameters (if any); the compile
+                 * error should occur before the execution-time error
+                 * about unbound parameters.
+                 */
+                pSt.execute();
+            }
             fail("expected compile error: " + sqlState);
         } catch (SQLException se) {
             assertSQLState(sqlState, se);
+        }
+    }
+
+    /**
+     * Assert that the query fails (either in compilation,
+     * execution, or retrieval of results--doesn't matter)
+     * and throws a SQLException with the expected state.
+     *
+     * Assumption is that 'query' does *not* have parameters
+     * that need binding and thus can be executed using a
+     * simple Statement.execute() call.
+     * 
+     * @param sqlstate expected sql state.
+     * @param st Statement object on which to execute.
+     * @param query the query to compile and execute.
+     */
+    public static void assertStatementError(String sqlState,
+        Statement st, String query)
+    {
+        try {
+            boolean haveRS = st.execute(query);
+            fetchAndDiscardAllResults(st, haveRS);
+            fail("Expected error '" + sqlState +
+                "' but no error was thrown.");
+        } catch (SQLException se) {
+            assertSQLState(sqlState, se);
+        }
+    }
+
+    /**
+     * Assert that execution of the received PreparedStatement
+     * object fails (either in execution or when retrieving
+     * results) and throws a SQLException with the expected
+     * state.
+     * 
+     * Assumption is that "pSt" is either a PreparedStatement
+     * or a CallableStatement that has already been prepared
+     * and whose parameters (if any) have already been bound.
+     * Thus the only thing left to do is to call "execute()"
+     * and look for the expected SQLException.
+     * 
+     * @param sqlstate expected sql state.
+     * @param pSt A PreparedStatement or CallableStatement on
+     *  which to call "execute()".
+     */
+    public static void assertStatementError(String sqlState,
+        PreparedStatement pSt)
+    {
+        try {
+            boolean haveRS = pSt.execute();
+            fetchAndDiscardAllResults(pSt, haveRS);
+            fail("Expected error '" + sqlState +
+                "' but no error was thrown.");
+        } catch (SQLException se) {
+            assertSQLState(sqlState, se);
+        }
+    }
+
+    /**
+     * Take a Statement object and a SQL query, execute it
+     * via the "executeUpdate()" method, and assert that the
+     * resultant row count matches the received row count.
+     *
+     * Assumption is that 'query' does *not* have parameters
+     * that need binding and that it can be executed using a
+     * simple Statement.executeUpdate() call.
+     * 
+     * @param st Statement object on which to execute.
+     * @param expectedRC Expected row count.
+     * @param query Query to execute.
+     */
+    public static void assertUpdateCount(Statement st,
+        int expectedRC, String query) throws SQLException
+    {
+        assertEquals("Update count does not match:",
+            expectedRC, st.executeUpdate(query));
+    }
+
+    /**
+     * Assert that a call to "executeUpdate()" on the received
+     * PreparedStatement object returns a row count that matches
+     * the received row count.
+     *
+     * Assumption is that "pSt" is either a PreparedStatement
+     * or a CallableStatement that has already been prepared
+     * and whose parameters (if any) have already been bound.
+     * Also assumes the statement's SQL is such that a call
+     * executeUpdate() is allowed.  Thus the only thing left
+     * to do is to call the "executeUpdate" method.
+     * 
+     * @param pSt The PreparedStatement on which to execute.
+     * @param expectedRC The expected row count.
+     */
+    public static void assertUpdateCount(PreparedStatement pSt,
+        int expectedRC) throws SQLException
+    {
+        assertEquals("Update count does not match:",
+            expectedRC, pSt.executeUpdate());
+    }
+
+    /**
+     * Take the received Statement--on which a query has been
+     * executed--and fetch all rows of all result sets (if any)
+     * returned from execution.  The rows themselves are
+     * discarded.  This is useful when we expect there to be
+     * an error when processing the results but do not know
+     * (or care) at what point the error occurs.
+     *
+     * @param st An already-executed statement from which
+     *  we get the result set to process (if there is one).
+     * @param haveRS Whether or not the the statement's
+     *  first result is a result set (as opposed to an
+     *  update count).
+     */
+    private static void fetchAndDiscardAllResults(Statement st,
+        boolean haveRS) throws SQLException
+    {
+        ResultSet rs = null;
+        while (haveRS || (st.getUpdateCount() != -1))
+        {
+            // If we have a result set, iterate through all
+            // of the rows.
+            if (haveRS)
+                JDBC.assertDrainResults(st.getResultSet(), -1);
+            haveRS = st.getMoreResults();
         }
     }
 

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java?view=diff&rev=465673&r1=465672&r2=465673
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/JDBC.java Thu Oct 19 09:34:22 2006
@@ -316,27 +316,188 @@
 	 * Drain a single ResultSet by reading all of its
 	 * rows and columns. Each column is accessed using
 	 * getString() and asserted that the returned value
-	 * matches the state of ResultSet.wasNull().
+	 * matches the state of ResultSet.wasNull().  If
+	 * the received row count is non-negative, this method
+	 * also asserts that the number of rows in the result
+	 * set matches the received row count.
 	 * Provides simple testing of the ResultSet when the contents
 	 * are not important.
 	 * @param rs
+	 * @param expectedRows If non-negative, indicates how
+	 *  many rows we expected to see in the result set.
 	 * @throws SQLException
 	 */
-	public static void assertDrainResults(ResultSet rs)
-	    throws SQLException
+	public static void assertDrainResults(ResultSet rs,
+	    int expectedRows) throws SQLException
 	{
 		ResultSetMetaData rsmd = rs.getMetaData();
 		
+		int rows = 0;
 		while (rs.next()) {
 			for (int col = 1; col <= rsmd.getColumnCount(); col++)
 			{
 				String s = rs.getString(col);
 				Assert.assertEquals(s == null, rs.wasNull());
 			}
+			rows++;
 		}
 		rs.close();
+
+		if (rows >= 0)
+			Assert.assertEquals("Unexpected row count:", expectedRows, rows); 
 	}
 	
+    /**
+     * Takes a result set and a two-dimensional array and asserts
+     * that the two have equivalent rows and columns.  The first
+     * row in the 2-d array is expected to be the names of the
+     * columns and thus is compared to the metadata column names.
+     * Subsequent rows in the array are then compared with the
+     * corresponding rows in the result set.
+     *
+     * Will throw an assertion failure if any of the following
+     * is true:
+     *
+     *  1. Expected vs actual number of columns doesn't match
+     *  2. Expected vs actual number of rows doesn't match
+     *  3. Any column in any row of the result set does not "equal"
+     *     the corresponding column in the expected 2-d array.  If
+     *     "allAsStrings" is true then the result set value will be
+     *     retrieved as a String and compared, via the ".equals()"
+     *     method, to the corresponding object in the array (with
+     *     the assumption being that the objects in the array are all
+     *     Strings).  Otherwise the result set value will be retrieved
+     *     and compared as an Object, which is useful when asserting
+     *     the JDBC types of the columns in addition to their values.
+     *
+     * @param rs The actual result set.
+     * @param expectedRows 2-Dimensional array of objects representing
+     *  the expected result set.
+     * @param allAsStrings Whether or not to fetch (and compare) all
+     *  values from the actual result set as Strings; if false the
+     *  values will be fetched and compared as Objects.
+     */
+    public static void assertFullResultSet(ResultSet rs,
+        Object [][] expectedRows, boolean allAsStrings)
+        throws SQLException
+    {
+        int rows;
+        boolean firstRow = true;
+        ResultSetMetaData rsmd = rs.getMetaData();
+
+        // Assert that we have the right number of columns.
+        Assert.assertEquals("Unexpected column count:",
+            expectedRows[0].length, rsmd.getColumnCount());
+
+        /* Assert each row of the result set.  First row of the result
+         * result set is the column names, so we have to make sure we
+         * don't call rs.next() in that case.
+         */
+        for (rows = 0; firstRow || rs.next(); rows++)
+        {
+            /* If we have more actual rows than expected rows, don't
+             * try to assert the row.  Instead just keep iterating
+             * to see exactly how many rows the actual result set has.
+             */
+            if (rows < expectedRows.length)
+            {
+                assertRowInResultSet(rs, rsmd, rows,
+                    expectedRows[rows], allAsStrings);
+            }
+
+            firstRow = false;
+        }
+
+        // And finally, assert the row count.
+        Assert.assertEquals("Unexpected row count:", expectedRows.length, rows);
+    }
+
+    /**
+     * Assert that every column in the current row of the received
+     * result set matches the corresponding column in the received
+     * array.  If rowNum is zero then instead of using the current
+     * row of "rs", use the column names as retrieved from the result
+     * set metadata.
+     *
+     * @param rs Result set whose current row we'll check (if rowNum
+     *  is greater than zero).
+     * @param rsmd Metadata object for "rs".  Used for fetching column
+     *  names (if rowNum == 0).
+     * @param rowNum Row number (w.r.t expected rows) that we're
+     *  checking.
+     * @param expectedRow Array of objects representing the expected
+     *  values for the current row (if rowNum > 0) or else the expected
+     *  column names (if rowNum == 0).
+     * @param asStrings Whether or not to fetch and compare all values
+     *  from "rs" as Strings.
+     */
+    private static void assertRowInResultSet(ResultSet rs,
+        ResultSetMetaData rsmd, int rowNum, Object [] expectedRow,
+        boolean asStrings) throws SQLException
+    {
+        String s;
+        boolean ok;
+        Object obj = null;
+        for (int i = 0; i < expectedRow.length; i++)
+        {
+            /* First row is column names.  We didn't call rs.next()
+             * in this case, so we have to be sure we don't call
+             * "wasNull" either.  Otherwise we'll see errors in client-
+             * server mode (though embedded doesn't seem to mind).
+             */
+            if (rowNum == 0)
+            {
+                obj = rsmd.getColumnName(i+1);
+                ok = ((expectedRow[i] != null) && obj.equals(expectedRow[i]));
+            }
+            else
+            {
+                if (asStrings)
+                {
+                    /* Different clients can return different values for
+                     * boolean columns--namely, 0/1 vs false/true.  So in
+                     * order to keep things uniform, take boolean columns
+                     * and get the JDBC string version.  Note: since
+                     * Derby doesn't have a BOOLEAN type, we assume that
+                     * if the column's type is SMALLINT and the expected
+                     * value's string form is "true" or "false", then the
+                     * column is intended to be a mock boolean column.
+                     */
+                    if ((expectedRow[i] != null)
+                        && (rsmd.getColumnType(i+1) == Types.SMALLINT))
+                    {
+                        s = expectedRow[i].toString();
+                        if (s.equals("true") || s.equals("false"))
+                            obj = (rs.getShort(i+1) == 0) ? "false" : "true";
+                    }
+                    else
+                    {
+                        obj = rs.getString(i+1);
+
+                        // Trim the string before comparing.
+                        if (obj != null)
+                            obj = ((String)obj).trim();
+                    }
+                }
+                else
+                    obj = rs.getObject(i+1);
+
+                ok = (rs.wasNull() && (expectedRow[i] == null))
+                    || (!rs.wasNull()
+                        && (expectedRow[i] != null)
+                        && obj.equals(expectedRow[i]));
+            }
+
+            if (!ok)
+            {
+                Assert.fail("Column value mismatch @ column '" +
+                    rsmd.getColumnName(i+1) + "', row " + rowNum +
+                    ":\n    Expected: " + expectedRow[i] +
+                    "\n    Found:    " + obj);
+            }
+        }
+    }
+
 	/**
 	 * Escape a non-qualified name so that it is suitable
 	 * for use in a SQL query executed by JDBC.