drill-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From h.@apache.org
Subject [1/8] drill git commit: DRILL-2769: Fix most non-SQLException not-supported-yet exceptions.
Date Tue, 10 Nov 2015 21:49:36 GMT
Repository: drill
Updated Branches:
  refs/heads/master e78e28661 -> a0be3ae0a


http://git-wip-us.apache.org/repos/asf/drill/blob/e4f257b1/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/Drill2769UnsupportedReportsUseSqlExceptionTest.java
----------------------------------------------------------------------
diff --git a/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/Drill2769UnsupportedReportsUseSqlExceptionTest.java
b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/Drill2769UnsupportedReportsUseSqlExceptionTest.java
new file mode 100644
index 0000000..a673d87
--- /dev/null
+++ b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/Drill2769UnsupportedReportsUseSqlExceptionTest.java
@@ -0,0 +1,459 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.drill.jdbc.test;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.Array;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.drill.common.util.TestTools;
+import org.apache.drill.jdbc.Driver;
+import org.apache.drill.jdbc.JdbcTestBase;
+import org.apache.drill.jdbc.AlreadyClosedSqlException;
+
+
+/**
+ * Test that non-SQLException exceptions used by Drill's current version of
+ * Avatica to indicate unsupported features are wrapped in or mapped to
+ * SQLException exceptions.
+ *
+ * <p>
+ *   As of 2015-08-24, Drill's version of Avatica used non-SQLException exception
+ *   class to report that methods/features were not implemented.
+ * </p>
+ * <pre>
+ *   5 UnsupportedOperationException in ArrayImpl
+ *  29 UnsupportedOperationException in AvaticaConnection
+ *  10 Helper.todo() (RuntimeException) in AvaticaDatabaseMetaData
+ *  21 UnsupportedOperationException in AvaticaStatement
+ *   4 UnsupportedOperationException in AvaticaPreparedStatement
+ * 103 UnsupportedOperationException in AvaticaResultSet
+ * </pre>
+ */
+public class Drill2769UnsupportedReportsUseSqlExceptionTest extends JdbcTestBase {
+  private static final Logger logger =
+      getLogger(Drill2769UnsupportedReportsUseSqlExceptionTest.class);
+
+  @Rule
+  public TestRule TIMEOUT = TestTools.getTimeoutRule(180_000 /* ms */);
+
+  private static Connection connection;
+  private static Statement plainStatement;
+  private static PreparedStatement preparedStatement;
+  // No CallableStatement.
+  private static ResultSet resultSet;
+  private static ResultSetMetaData resultSetMetaData;
+  private static DatabaseMetaData databaseMetaData;
+
+
+  @BeforeClass
+  public static void setUpObjects() throws Exception {
+    // (Note: Can't use JdbcTest's connect(...) for this test class.)
+
+    connection = new Driver().connect("jdbc:drill:zk=local",
+                                      JdbcAssert.getDefaultProperties());
+
+    plainStatement = connection.createStatement();
+    preparedStatement =
+        connection.prepareStatement("VALUES 'PreparedStatement query'");
+
+    try {
+      connection.prepareCall("VALUES 'CallableStatement query'");
+      fail("Test seems to be out of date.  Was prepareCall(...) implemented?");
+    }
+    catch (SQLException | UnsupportedOperationException e) {
+      // Expected.
+    }
+    try {
+      connection.createArrayOf("INTEGER", new Object[0]);
+      fail("Test seems to be out of date.  Were arrays implemented?");
+    }
+    catch (SQLException | UnsupportedOperationException e) {
+      // Expected.
+    }
+
+    resultSet = plainStatement.executeQuery("VALUES 'plain Statement query'");
+    resultSet.next();
+
+    resultSetMetaData = resultSet.getMetaData();
+    databaseMetaData = connection.getMetaData();
+
+
+    // Self-check that member variables are set:
+    assertFalse("Test setup error", connection.isClosed());
+    assertFalse("Test setup error", plainStatement.isClosed());
+    assertFalse("Test setup error", preparedStatement.isClosed());
+    assertFalse("Test setup error", resultSet.isClosed());
+    // (No ResultSetMetaData.isClosed() or DatabaseMetaData.isClosed():)
+    assertNotNull("Test setup error", resultSetMetaData);
+    assertNotNull("Test setup error", databaseMetaData);
+  }
+
+  @AfterClass
+  public static void tearDownConnection() throws Exception {
+    connection.close();
+  }
+
+
+  /**
+   * Reflection-based checker that exceptions thrown by JDBC interfaces'
+   * implementation methods for unsupported-operation cases are SQLExceptions
+   * (not UnsupportedOperationExceptions).
+   *
+   * @param  <INTF>  JDBC interface type
+   */
+  private static class NoNonSqlExceptionsChecker<INTF> {
+    private final Class<INTF> jdbcIntf;
+    private final INTF jdbcObject;
+
+    private final StringBuilder failureLinesBuf = new StringBuilder();
+    private final StringBuilder successLinesBuf = new StringBuilder();
+
+
+    NoNonSqlExceptionsChecker(final Class<INTF> jdbcIntf,
+                              final INTF jdbcObject) {
+      this.jdbcIntf = jdbcIntf;
+      this.jdbcObject = jdbcObject;
+    }
+
+    /**
+     * Hook/factory method to allow context to provide fresh object for each
+     * method.  Needed for Statement and PrepareStatement, whose execute...
+     * methods can close the statement (at least given our minimal dummy
+     * argument values).
+     */
+    protected INTF getJdbcObject() throws SQLException {
+      return jdbcObject;
+    }
+
+    /**
+     * Gets minimal value suitable for use as actual parameter value for given
+     * formal parameter type.
+     */
+    private static Object getDummyValueForType(Class<?> type) {
+      final Object result;
+      if (! type.isPrimitive()) {
+        result = null;
+      }
+      else {
+        if (type == boolean.class) {
+          result = false;
+        }
+        else if (type == byte.class) {
+          result = (byte) 0;
+        }
+        else if (type == short.class) {
+          result = (short) 0;
+        }
+        else if (type == int.class) {
+          result = 0;
+        }
+        else if (type == long.class) {
+          result = (long) 0L;
+        }
+        else if (type == float.class) {
+          result = 0F;
+        }
+        else if (type == double.class) {
+          result = 0.0;
+        }
+        else {
+          fail("Test needs to be updated to handle type " + type);
+          result = null;  // Not executed; for "final".
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Assembles method signature text for given method.
+     */
+    private String makeLabel(Method method) {
+      String methodLabel;
+      methodLabel = jdbcIntf.getSimpleName() + "." + method.getName() + "(";
+      boolean first = true;
+      for (Class<?> paramType : method.getParameterTypes()) {
+        if (! first) {
+          methodLabel += ", ";
+        }
+        first = false;
+        methodLabel += paramType.getSimpleName();
+      }
+      methodLabel += ")";
+      return methodLabel;
+    }
+
+    /**
+     * Assembles (minimal) arguments array for given method.
+     */
+    private Object[] makeArgs(Method method) {
+      final List<Object> argsList = new ArrayList<>();
+      for (Class<?> paramType : method.getParameterTypes()) {
+        argsList.add(getDummyValueForType(paramType));
+      }
+      Object[] argsArray = argsList.toArray();
+      return argsArray;
+    }
+
+    /**
+     * Tests one method.
+     * (Disturbs members set by makeArgsAndLabel, but those shouldn't be used
+     * except by this method.)
+     */
+    private void testOneMethod(Method method) {
+      final String methodLabel = makeLabel(method);
+
+      try {
+        final INTF jdbcObject;
+        try {
+          jdbcObject = getJdbcObject();
+        } catch (SQLException e) {
+          fail("Unexpected exception: " + e + " from getJdbcObject()");
+          throw new RuntimeException("DUMMY; so compiler know block throws");
+        }
+
+        // See if method throws exception:
+        method.invoke(jdbcObject, makeArgs(method));
+
+        // If here, method didn't throw--check if it's an expected non-throwing
+        // method (e.g., an isClosed).  (If not, report error.)
+        final String resultLine = "- " + methodLabel + " didn't throw\n";
+
+        successLinesBuf.append(resultLine);
+      }
+      catch (InvocationTargetException wrapperEx) {
+        final Throwable cause = wrapperEx.getCause();
+        final String resultLine = "- " + methodLabel + " threw <" + cause + ">\n";
+
+        if (SQLException.class.isAssignableFrom(cause.getClass())
+            &&
+            ! AlreadyClosedSqlException.class.isAssignableFrom(cause.getClass())
+            ) {
+          // Good case--almost any exception should be SQLException or subclass
+          // (but make sure not accidentally closed).
+          successLinesBuf.append(resultLine);
+        }
+        else if (NullPointerException.class == cause.getClass()
+                 && (method.getName().equals("isWrapperFor")
+                     || method.getName().equals("unwrap"))) {
+          // Known good-enough case--these methods throw NullPointerException
+          // because of the way we call them (with null) and the way Avatica
+          // code implements them.
+          successLinesBuf.append(resultLine);
+        }
+        else {
+          final String badResultLine =
+              "- " + methodLabel + " threw <" + cause + "> instead"
+              + " of a " + SQLException.class.getSimpleName() + "\n";
+          logger.trace("Failure: " + resultLine);
+          failureLinesBuf.append(badResultLine);
+        }
+      }
+      catch (IllegalAccessException | IllegalArgumentException e) {
+        fail("Unexpected exception: " + e + ", cause = " + e.getCause()
+             + "  from " + method);
+      }
+    }
+
+    public void testMethods() {
+      for (Method method : jdbcIntf.getMethods()) {
+        final String methodLabel = makeLabel(method);
+        if ("close".equals(method.getName())) {
+          logger.debug("Skipping (because closes): " + methodLabel);
+        }
+        /* Uncomment to suppress calling DatabaseMetaData.getColumns(...), which
+           sometimes takes about 2 minutes, and other DatabaseMetaData methods
+           that query, collectively taking a while too:
+        else if (DatabaseMetaData.class == jdbcIntf
+                 && "getColumns".equals(method.getName())) {
+          logger.debug("Skipping (because really slow): " + methodLabel);
+        }
+        else if (DatabaseMetaData.class == jdbcIntf
+                 && ResultSet.class == method.getReturnType()) {
+          logger.debug("Skipping (because a bit slow): " + methodLabel);
+        }
+        */
+        else {
+          logger.debug("Testing method " + methodLabel);
+          testOneMethod(method);
+        }
+      }
+    }
+
+    public boolean hadAnyFailures() {
+      return 0 != failureLinesBuf.length();
+    }
+
+    public String getFailureLines() {
+      return failureLinesBuf.toString();
+    }
+
+    public String getSuccessLines() {
+      return successLinesBuf.toString();
+    }
+
+    public String getReport() {
+      final String report =
+          "Failures:\n"
+          + getFailureLines()
+          + "(Successes:\n"
+          + getSuccessLines()
+          + ")";
+      return report;
+    }
+
+  } // class NoNonSqlExceptionsChecker<INTF>
+
+
+  @Test
+  public void testConnectionMethodsThrowRight() {
+    NoNonSqlExceptionsChecker<Connection> checker =
+        new NoNonSqlExceptionsChecker<Connection>(Connection.class, connection);
+
+    checker.testMethods();
+
+    if (checker.hadAnyFailures()) {
+      System.err.println(checker.getReport());
+      fail("Non-SQLException exception error(s): \n" + checker.getReport());
+    }
+  }
+
+
+  private static class PlainStatementChecker
+      extends NoNonSqlExceptionsChecker<Statement> {
+
+    private final Connection factoryConnection;
+
+    PlainStatementChecker(Connection factoryConnection) {
+      super(Statement.class, null);
+      this.factoryConnection = factoryConnection;
+    }
+
+    protected Statement getJdbcObject() throws SQLException {
+      return factoryConnection.createStatement();
+    }
+
+  } // class PlainStatementChecker
+
+  @Test
+  public void testPlainStatementMethodsThrowRight() {
+    NoNonSqlExceptionsChecker<Statement> checker =
+        new PlainStatementChecker(connection);
+
+    checker.testMethods();
+
+    if (checker.hadAnyFailures()) {
+      fail("Non-SQLException exception error(s): \n" + checker.getReport());
+    }
+  }
+
+
+  private static class PreparedStatementChecker
+      extends NoNonSqlExceptionsChecker<PreparedStatement> {
+
+    private final Connection factoryConnection;
+
+    PreparedStatementChecker(Connection factoryConnection) {
+      super(PreparedStatement.class, null);
+      this.factoryConnection = factoryConnection;
+    }
+
+    protected PreparedStatement getJdbcObject() throws SQLException {
+      return factoryConnection.prepareStatement(null);
+    }
+
+  } // class PlainStatementChecker
+
+  @Test
+  public void testPreparedStatementMethodsThrowRight() {
+    NoNonSqlExceptionsChecker<PreparedStatement> checker =
+        new PreparedStatementChecker(connection);
+
+    checker.testMethods();
+
+    if (checker.hadAnyFailures()) {
+      fail("Non-SQLException exception error(s): \n" + checker.getReport());
+    }
+  }
+
+
+  @Test
+  public void testResultSetMethodsThrowRight() {
+    NoNonSqlExceptionsChecker<ResultSet> checker =
+        new NoNonSqlExceptionsChecker<ResultSet>(ResultSet.class, resultSet);
+
+    checker.testMethods();
+
+    if (checker.hadAnyFailures()) {
+      fail("Non-SQLException exception error(s): \n" + checker.getReport());
+    }
+  }
+
+
+  @Test
+  public void testResultSetMetaDataMethodsThrowRight() {
+    NoNonSqlExceptionsChecker<ResultSetMetaData> checker =
+        new NoNonSqlExceptionsChecker<ResultSetMetaData>(ResultSetMetaData.class,
+                                                         resultSetMetaData);
+
+    checker.testMethods();
+
+    if (checker.hadAnyFailures()) {
+      fail("Non-SQLException exception error(s): \n" + checker.getReport());
+    }
+  }
+
+
+  @Test
+  public void testDatabaseMetaDataMethodsThrowRight() {
+    NoNonSqlExceptionsChecker<DatabaseMetaData> checker =
+        new NoNonSqlExceptionsChecker<DatabaseMetaData>(DatabaseMetaData.class,
+                                                        databaseMetaData);
+
+    checker.testMethods();
+
+    if (checker.hadAnyFailures()) {
+      fail("Non-SQLException exception error(s): \n" + checker.getReport());
+    }
+  }
+
+}


Mime
View raw message