@Test(expected = Exception.class)
 public void testEmptyFolderThrowsTableNotFound() throws Exception {
   final String table = String.format("%s/empty", TestTools.getTestResourcesPath());
   final String query = String.format("select * from dfs.`%s`", table);
   try {
     testNoResult(query);
   } catch (Exception ex) {
     final String pattern = String.format("%s' not found", table).toLowerCase();
     final boolean isTableNotFound = ex.getMessage().toLowerCase().contains(pattern);
     assertTrue(isTableNotFound);
     throw ex;
   }
 }
Exemple #2
0
public class DrillTest {
  //  private static final Logger logger = org.slf4j.LoggerFactory.getLogger(DrillTest.class);

  protected static final ObjectMapper objectMapper;

  static {
    System.setProperty("line.separator", "\n");
    objectMapper = new ObjectMapper();
  }

  static final SystemManager manager = new SystemManager();

  static final Logger testReporter =
      org.slf4j.LoggerFactory.getLogger("org.apache.drill.TestReporter");
  static final TestLogReporter LOG_OUTCOME = new TestLogReporter();

  static MemWatcher memWatcher;
  static String className;

  @Rule public final TestRule TIMEOUT = TestTools.getTimeoutRule(50000);
  @Rule public final TestLogReporter logOutcome = LOG_OUTCOME;

  @Rule public final TestRule REPEAT_RULE = TestTools.getRepeatRule(false);

  @Rule public TestName TEST_NAME = new TestName();

  @Before
  public void printID() throws Exception {
    System.out.printf("Running %s#%s\n", getClass().getName(), TEST_NAME.getMethodName());
  }

  @BeforeClass
  public static void initDrillTest() throws Exception {
    memWatcher = new MemWatcher();
  }

  @AfterClass
  public static void finiDrillTest() throws InterruptedException {
    testReporter.info(
        String.format("Test Class done (%s): %s.", memWatcher.getMemString(true), className));
    LOG_OUTCOME.sleepIfFailure();
  }

  protected static class MemWatcher {
    private long startDirect;
    private long startHeap;
    private long startNonHeap;

    public MemWatcher() {
      startDirect = manager.getMemDirect();
      startHeap = manager.getMemHeap();
      startNonHeap = manager.getMemNonHeap();
    }

    public Object getMemString() {
      return getMemString(false);
    }

    public String getMemString(boolean runGC) {
      if (runGC) {
        Runtime.getRuntime().gc();
      }
      long endDirect = manager.getMemDirect();
      long endHeap = manager.getMemHeap();
      long endNonHeap = manager.getMemNonHeap();
      return String.format(
          "d: %s(%s), h: %s(%s), nh: %s(%s)", //
          DrillStringUtils.readable(endDirect - startDirect),
          DrillStringUtils.readable(endDirect), //
          DrillStringUtils.readable(endHeap - startHeap),
          DrillStringUtils.readable(endHeap), //
          DrillStringUtils.readable(endNonHeap - startNonHeap),
          DrillStringUtils.readable(endNonHeap) //
          );
    }
  }

  private static class TestLogReporter extends TestWatcher {

    private MemWatcher memWatcher;
    private int failureCount = 0;

    @Override
    protected void starting(Description description) {
      super.starting(description);
      className = description.getClassName();
      memWatcher = new MemWatcher();
    }

    @Override
    protected void failed(Throwable e, Description description) {
      testReporter.error(
          String.format(
              "Test Failed (%s): %s", memWatcher.getMemString(), description.getDisplayName()),
          e);
      failureCount++;
    }

    @Override
    public void succeeded(Description description) {
      testReporter.info(
          String.format(
              "Test Succeeded (%s): %s", memWatcher.getMemString(), description.getDisplayName()));
    }

    public void sleepIfFailure() throws InterruptedException {
      if (failureCount > 0) {
        Thread.sleep(2000);
        failureCount = 0;
      } else {
        // pause to get logger to catch up.
        Thread.sleep(250);
      }
    }
  }

  public static String escapeJsonString(String original) {
    try {
      return objectMapper.writeValueAsString(original);
    } catch (JsonProcessingException e) {
      return original;
    }
  }

  private static class SystemManager {

    final BufferPoolMXBean directBean;
    final MemoryMXBean memoryBean;

    public SystemManager() {
      memoryBean = ManagementFactory.getMemoryMXBean();
      BufferPoolMXBean localBean = null;
      List<BufferPoolMXBean> pools = ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
      for (BufferPoolMXBean b : pools) {
        if (b.getName().equals("direct")) {
          localBean = b;
        }
      }
      directBean = localBean;
    }

    public long getMemDirect() {
      return directBean.getMemoryUsed();
    }

    public long getMemHeap() {
      return memoryBean.getHeapMemoryUsage().getUsed();
    }

    public long getMemNonHeap() {
      return memoryBean.getNonHeapMemoryUsage().getUsed();
    }
  }
}
public class PlanningBase extends ExecTest {
  static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(PlanningBase.class);

  @Rule public final TestRule TIMEOUT = TestTools.getTimeoutRule(10000);

  @Mocked DrillbitContext dbContext;
  @Mocked QueryContext context;
  private final DrillConfig config = DrillConfig.create();

  protected void testSqlPlanFromFile(String file) throws Exception {
    testSqlPlan(getFile(file));
  }

  protected void testSqlPlan(String sqlCommands) throws Exception {
    String[] sqlStrings = sqlCommands.split(";");

    final DistributedCache cache = new LocalCache();
    cache.run();

    final SystemOptionManager opt = new SystemOptionManager(cache);
    opt.init();
    final OptionManager sess = new SessionOptionManager(opt);

    new NonStrictExpectations() {
      {
        dbContext.getMetrics();
        result = new MetricRegistry();
        dbContext.getAllocator();
        result = new TopLevelAllocator();
        dbContext.getConfig();
        result = config;
        dbContext.getOptionManager();
        result = opt;
        dbContext.getCache();
        result = cache;
      }
    };

    final StoragePluginRegistry registry = new StoragePluginRegistry(dbContext);
    registry.init();
    final FunctionImplementationRegistry functionRegistry =
        new FunctionImplementationRegistry(config);
    final SchemaPlus root = Frameworks.createRootSchema(false);
    registry.getSchemaFactory().registerSchemas(new UserSession(null, null, null), root);

    new NonStrictExpectations() {
      {
        context.getNewDefaultSchema();
        result = root;
        context.getStorage();
        result = registry;
        context.getFunctionRegistry();
        result = functionRegistry;
        context.getSession();
        result = new UserSession(null, null, null);
        context.getCurrentEndpoint();
        result = DrillbitEndpoint.getDefaultInstance();
        context.getActiveEndpoints();
        result = ImmutableList.of(DrillbitEndpoint.getDefaultInstance());
        context.getPlannerSettings();
        result = new PlannerSettings(sess);
        context.getOptions();
        result = sess;
        context.getConfig();
        result = config;
        context.getCache();
        result = cache;
      }
    };

    for (String sql : sqlStrings) {
      if (sql.trim().isEmpty()) continue;
      DrillSqlWorker worker = new DrillSqlWorker(context);
      PhysicalPlan p = worker.getPlan(sql);
    }
  }

  protected String getFile(String resource) throws IOException {
    URL url = Resources.getResource(resource);
    if (url == null) {
      throw new IOException(String.format("Unable to find path %s.", resource));
    }
    return Resources.toString(url, Charsets.UTF_8);
  }
}
public class TestLargeFileCompilation extends BaseTestQuery {
  @Rule public final TestRule TIMEOUT = TestTools.getTimeoutRule(150000); // 150secs

  private static final String LARGE_QUERY_GROUP_BY;

  private static final String LARGE_QUERY_ORDER_BY;

  private static final String LARGE_QUERY_ORDER_BY_WITH_LIMIT;

  private static final String LARGE_QUERY_FILTER;

  private static final String LARGE_QUERY_WRITER;

  private static final String LARGE_QUERY_SELECT_LIST;

  private static final int ITERATION_COUNT =
      Integer.valueOf(System.getProperty("TestLargeFileCompilation.iteration", "1"));

  private static final int NUM_PROJECT_COULMNS = 2000;

  private static final int NUM_ORDERBY_COULMNS = 500;

  private static final int NUM_GROUPBY_COULMNS = 225;

  private static final int NUM_FILTER_COULMNS = 150;

  static {
    StringBuilder sb = new StringBuilder("select\n\t");
    for (int i = 0; i < NUM_GROUPBY_COULMNS; i++) {
      sb.append("c").append(i).append(", ");
    }
    sb.append("full_name\nfrom (select\n\t");
    for (int i = 0; i < NUM_GROUPBY_COULMNS; i++) {
      sb.append("employee_id+").append(i).append(" as c").append(i).append(", ");
    }
    sb.append("full_name\nfrom cp.`employee.json`)\ngroup by\n\t");
    for (int i = 0; i < NUM_GROUPBY_COULMNS; i++) {
      sb.append("c").append(i).append(", ");
    }
    LARGE_QUERY_GROUP_BY = sb.append("full_name").toString();
  }

  static {
    StringBuilder sb = new StringBuilder("select\n\t");
    for (int i = 0; i < NUM_PROJECT_COULMNS; i++) {
      sb.append("employee_id+").append(i).append(" as col").append(i).append(", ");
    }
    sb.append("full_name\nfrom cp.`employee.json`\n\n\t");
    LARGE_QUERY_SELECT_LIST = sb.append("full_name").toString();
  }

  static {
    StringBuilder sb = new StringBuilder("select\n\t");
    for (int i = 0; i < NUM_PROJECT_COULMNS; i++) {
      sb.append("employee_id+").append(i).append(" as col").append(i).append(", ");
    }
    sb.append("full_name\nfrom cp.`employee.json`\norder by\n\t");
    for (int i = 0; i < NUM_ORDERBY_COULMNS; i++) {
      sb.append(" col").append(i).append(", ");
    }
    LARGE_QUERY_ORDER_BY = sb.append("full_name").toString();
    LARGE_QUERY_ORDER_BY_WITH_LIMIT = sb.append("\nlimit 1").toString();
  }

  static {
    StringBuilder sb =
        new StringBuilder("select *\n").append("from cp.`employee.json`\n").append("where");
    for (int i = 0; i < NUM_FILTER_COULMNS; i++) {
      sb.append(" employee_id+")
          .append(i)
          .append(" < employee_id ")
          .append(i % 2 == 0 ? "OR" : "AND");
    }
    LARGE_QUERY_FILTER = sb.append(" true").toString();
  }

  static {
    StringBuilder sb = new StringBuilder("create table %s as (select \n");
    for (int i = 0; i < NUM_PROJECT_COULMNS; i++) {
      sb.append("employee_id+").append(i).append(" as col").append(i).append(", ");
    }
    LARGE_QUERY_WRITER = sb.append("full_name\nfrom cp.`employee.json` limit 1)").toString();
  }

  @Test
  public void testTEXT_WRITER() throws Exception {
    testNoResult("alter session set `%s`='JDK'", ClassCompilerSelector.JAVA_COMPILER_OPTION);
    testNoResult("use dfs_test.tmp");
    testNoResult("alter session set `%s`='csv'", ExecConstants.OUTPUT_FORMAT_OPTION);
    testNoResult(LARGE_QUERY_WRITER, "wide_table_csv");
  }

  @Test
  public void testPARQUET_WRITER() throws Exception {
    testNoResult("alter session set `%s`='JDK'", ClassCompilerSelector.JAVA_COMPILER_OPTION);
    testNoResult("use dfs_test.tmp");
    testNoResult("alter session set `%s`='parquet'", ExecConstants.OUTPUT_FORMAT_OPTION);
    testNoResult(ITERATION_COUNT, LARGE_QUERY_WRITER, "wide_table_parquet");
  }

  @Test
  public void testGROUP_BY() throws Exception {
    testNoResult("alter session set `%s`='JDK'", ClassCompilerSelector.JAVA_COMPILER_OPTION);
    testNoResult(ITERATION_COUNT, LARGE_QUERY_GROUP_BY);
  }

  @Test
  public void testEXTERNAL_SORT() throws Exception {
    testNoResult("alter session set `%s`='JDK'", ClassCompilerSelector.JAVA_COMPILER_OPTION);
    testNoResult(ITERATION_COUNT, LARGE_QUERY_ORDER_BY);
  }

  @Test
  public void testTOP_N_SORT() throws Exception {
    testNoResult("alter session set `%s`='JDK'", ClassCompilerSelector.JAVA_COMPILER_OPTION);
    testNoResult(ITERATION_COUNT, LARGE_QUERY_ORDER_BY_WITH_LIMIT);
  }

  @Test
  public void testFILTER() throws Exception {
    testNoResult("alter session set `%s`='JDK'", ClassCompilerSelector.JAVA_COMPILER_OPTION);
    testNoResult(ITERATION_COUNT, LARGE_QUERY_FILTER);
  }

  @Test
  public void testProject() throws Exception {
    testNoResult("alter session set `%s`='JDK'", ClassCompilerSelector.JAVA_COMPILER_OPTION);
    testNoResult(ITERATION_COUNT, LARGE_QUERY_SELECT_LIST);
  }
}
public class TestWindowFunctions extends BaseTestQuery {
  static final String WORKING_PATH = TestTools.getWorkingPath();
  static final String TEST_RES_PATH = WORKING_PATH + "/src/test/resources";

  private static void throwAsUnsupportedException(UserException ex) throws Exception {
    SqlUnsupportedException.errorClassNameToException(
        ex.getOrCreatePBError(false).getException().getExceptionClass());
    throw ex;
  }

  @Test // DRILL-3196
  public void testSinglePartition() throws Exception {
    final String query =
        "select sum(n_nationKey) over(partition by n_nationKey) as col1, count(*) over(partition by n_nationKey) as col2 \n"
            + "from cp.`tpch/nation.parquet`";

    // Validate the plan
    final String[] expectedPlan = {
      "Window.*partition \\{0\\} order by \\[\\].*\\[SUM\\(\\$0\\), COUNT\\(\\)",
      "Scan.*columns=\\[`n_nationKey`\\].*"
    };
    final String[] excludedPatterns = {"Scan.*columns=\\[`\\*`\\].*"};
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPatterns);

    testBuilder()
        .sqlQuery(query)
        .unOrdered()
        .baselineColumns("col1", "col2")
        .baselineValues(0l, 1l)
        .baselineValues(1l, 1l)
        .baselineValues(2l, 1l)
        .baselineValues(3l, 1l)
        .baselineValues(4l, 1l)
        .baselineValues(5l, 1l)
        .baselineValues(6l, 1l)
        .baselineValues(7l, 1l)
        .baselineValues(8l, 1l)
        .baselineValues(9l, 1l)
        .baselineValues(10l, 1l)
        .baselineValues(11l, 1l)
        .baselineValues(12l, 1l)
        .baselineValues(13l, 1l)
        .baselineValues(14l, 1l)
        .baselineValues(15l, 1l)
        .baselineValues(16l, 1l)
        .baselineValues(17l, 1l)
        .baselineValues(18l, 1l)
        .baselineValues(19l, 1l)
        .baselineValues(20l, 1l)
        .baselineValues(21l, 1l)
        .baselineValues(22l, 1l)
        .baselineValues(23l, 1l)
        .baselineValues(24l, 1l)
        .build()
        .run();
  }

  @Test // DRILL-3196
  public void testSinglePartitionDefinedInWindowList() throws Exception {
    final String query =
        "select sum(n_nationKey) over w as col \n"
            + "from cp.`tpch/nation.parquet` \n"
            + "window w as (partition by n_nationKey order by n_nationKey)";

    // Validate the plan
    final String[] expectedPlan = {
      "Window.*partition \\{0\\} order by \\[0\\].*SUM\\(\\$0\\)",
      "Scan.*columns=\\[`n_nationKey`\\].*"
    };
    final String[] excludedPatterns = {"Scan.*columns=\\[`\\*`\\].*"};
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPatterns);

    testBuilder()
        .sqlQuery(query)
        .unOrdered()
        .baselineColumns("col")
        .baselineValues(0l)
        .baselineValues(1l)
        .baselineValues(2l)
        .baselineValues(3l)
        .baselineValues(4l)
        .baselineValues(5l)
        .baselineValues(6l)
        .baselineValues(7l)
        .baselineValues(8l)
        .baselineValues(9l)
        .baselineValues(10l)
        .baselineValues(11l)
        .baselineValues(12l)
        .baselineValues(13l)
        .baselineValues(14l)
        .baselineValues(15l)
        .baselineValues(16l)
        .baselineValues(17l)
        .baselineValues(18l)
        .baselineValues(19l)
        .baselineValues(20l)
        .baselineValues(21l)
        .baselineValues(22l)
        .baselineValues(23l)
        .baselineValues(24l)
        .build()
        .run();
  }

  @Test(expected = UnsupportedFunctionException.class) // DRILL-3182
  public void testWindowFunctionWithDistinct() throws Exception {
    try {
      final String query =
          "explain plan for select a2, count(distinct b2) over(partition by a2) \n"
              + "from cp.`tpch/nation.parquet`";

      test(query);
    } catch (UserException ex) {
      throwAsUnsupportedException(ex);
      throw ex;
    }
  }

  @Test(expected = UnsupportedFunctionException.class) // DRILL-3188
  public void testWindowFrame() throws Exception {
    try {
      final String query =
          "select a2, sum(a2) over(partition by a2 order by a2 rows between 1 preceding and 1 following ) \n"
              + "from cp.`tpch/nation.parquet` t \n"
              + "order by a2";

      test(query);
    } catch (UserException ex) {
      throwAsUnsupportedException(ex);
      throw ex;
    }
  }

  @Test(expected = UnsupportedFunctionException.class) // DRILL-3188
  public void testRowsUnboundedPreceding() throws Exception {
    try {
      final String query =
          "explain plan for select sum(n_nationKey) over(partition by n_nationKey order by n_nationKey \n"
              + "rows UNBOUNDED PRECEDING)"
              + "from cp.`tpch/nation.parquet` t \n"
              + "order by n_nationKey";

      test(query);
    } catch (UserException ex) {
      throwAsUnsupportedException(ex);
      throw ex;
    }
  }

  @Test(expected = UnsupportedFunctionException.class) // DRILL-3359
  public void testFramesDefinedInWindowClause() throws Exception {
    try {
      final String query =
          "explain plan for select sum(n_nationKey) over w \n"
              + "from cp.`tpch/nation.parquet` \n"
              + "window w as (partition by n_nationKey order by n_nationKey rows UNBOUNDED PRECEDING)";

      test(query);
    } catch (UserException ex) {
      throwAsUnsupportedException(ex);
      throw ex;
    }
  }

  @Test(expected = UnsupportedFunctionException.class) // DRILL-3326
  public void testWindowWithAlias() throws Exception {
    try {
      String query =
          "explain plan for SELECT sum(n_nationkey) OVER (PARTITION BY n_name ORDER BY n_name ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) as col2 \n"
              + "from cp.`tpch/nation.parquet`";

      test(query);
    } catch (UserException ex) {
      throwAsUnsupportedException(ex);
      throw ex;
    }
  }

  @Test(expected = UnsupportedFunctionException.class) // DRILL-3189
  public void testWindowWithAllowDisallow() throws Exception {
    try {
      final String query =
          "select sum(n_nationKey) over(partition by n_nationKey \n"
              + "rows between unbounded preceding and unbounded following disallow partial) \n"
              + "from cp.`tpch/nation.parquet` \n"
              + "order by n_nationKey";

      test(query);
    } catch (UserException ex) {
      throwAsUnsupportedException(ex);
      throw ex;
    }
  }

  @Test // DRILL-3360
  public void testWindowInWindow() throws Exception {
    thrownException.expect(
        new UserExceptionMatcher(UserBitShared.DrillPBError.ErrorType.VALIDATION));
    String query =
        "select rank() over(order by row_number() over(order by n_nationkey)) \n"
            + "from cp.`tpch/nation.parquet`";

    test(query);
  }

  @Test // DRILL-3280
  public void testMissingOverWithWindowClause() throws Exception {
    thrownException.expect(
        new UserExceptionMatcher(UserBitShared.DrillPBError.ErrorType.VALIDATION));
    String query =
        "select rank(), cume_dist() over w \n"
            + "from cp.`tpch/nation.parquet` \n"
            + "window w as (partition by n_name order by n_nationkey)";

    test(query);
  }

  @Test // DRILL-3601
  public void testLeadMissingOver() throws Exception {
    thrownException.expect(
        new UserExceptionMatcher(UserBitShared.DrillPBError.ErrorType.VALIDATION));
    String query = "select lead(n_nationkey) from cp.`tpch/nation.parquet`";

    test(query);
  }

  @Test // DRILL-3649
  public void testMissingOverWithConstant() throws Exception {
    thrownException.expect(
        new UserExceptionMatcher(UserBitShared.DrillPBError.ErrorType.VALIDATION));
    String query = "select NTILE(1) from cp.`tpch/nation.parquet`";

    test(query);
  }

  @Test // DRILL-3344
  public void testWindowGroupBy() throws Exception {
    thrownException.expect(
        new UserExceptionMatcher(UserBitShared.DrillPBError.ErrorType.VALIDATION));
    String query =
        "explain plan for SELECT max(n_nationkey) OVER (), n_name as col2 \n"
            + "from cp.`tpch/nation.parquet` \n"
            + "group by n_name";

    test(query);
  }

  @Test // DRILL-3346
  public void testWindowGroupByOnView() throws Exception {
    try {
      thrownException.expect(
          new UserExceptionMatcher(UserBitShared.DrillPBError.ErrorType.VALIDATION));
      String createView =
          "create view testWindowGroupByOnView(a, b) as \n"
              + "select n_nationkey, n_name from cp.`tpch/nation.parquet`";
      String query =
          "explain plan for SELECT max(a) OVER (), b as col2 \n"
              + "from testWindowGroupByOnView \n"
              + "group by b";

      test("use dfs_test.tmp");
      test(createView);
      test(query);
    } finally {
      test("drop view testWindowGroupByOnView");
    }
  }

  @Test // DRILL-3188
  public void testWindowFrameEquivalentToDefault() throws Exception {
    final String query1 =
        "select sum(n_nationKey) over(partition by n_nationKey order by n_nationKey) as col\n"
            + "from cp.`tpch/nation.parquet` t \n"
            + "order by n_nationKey";

    final String query2 =
        "select sum(n_nationKey) over(partition by n_nationKey order by n_nationKey \n"
            + "range between unbounded preceding and current row) as col \n"
            + "from cp.`tpch/nation.parquet` t \n"
            + "order by n_nationKey";

    final String query3 =
        "select sum(n_nationKey) over(partition by n_nationKey \n"
            + "rows BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as col \n"
            + "from cp.`tpch/nation.parquet` t \n"
            + "order by n_nationKey";

    // Validate the plan
    final String[] expectedPlan1 = {
      "Window.*partition \\{0\\} order by \\[0\\].*SUM\\(\\$0\\)",
      "Scan.*columns=\\[`n_nationKey`\\].*"
    };
    final String[] excludedPatterns1 = {"Scan.*columns=\\[`\\*`\\].*"};
    PlanTestBase.testPlanMatchingPatterns(query1, expectedPlan1, excludedPatterns1);

    testBuilder()
        .sqlQuery(query1)
        .unOrdered()
        .baselineColumns("col")
        .baselineValues(0l)
        .baselineValues(1l)
        .baselineValues(2l)
        .baselineValues(3l)
        .baselineValues(4l)
        .baselineValues(5l)
        .baselineValues(6l)
        .baselineValues(7l)
        .baselineValues(8l)
        .baselineValues(9l)
        .baselineValues(10l)
        .baselineValues(11l)
        .baselineValues(12l)
        .baselineValues(13l)
        .baselineValues(14l)
        .baselineValues(15l)
        .baselineValues(16l)
        .baselineValues(17l)
        .baselineValues(18l)
        .baselineValues(19l)
        .baselineValues(20l)
        .baselineValues(21l)
        .baselineValues(22l)
        .baselineValues(23l)
        .baselineValues(24l)
        .build()
        .run();

    final String[] expectedPlan2 = {
      "Window.*partition \\{0\\} order by \\[0\\].*SUM\\(\\$0\\)",
      "Scan.*columns=\\[`n_nationKey`\\].*"
    };
    final String[] excludedPatterns2 = {"Scan.*columns=\\[`\\*`\\].*"};
    PlanTestBase.testPlanMatchingPatterns(query2, expectedPlan2, excludedPatterns2);

    testBuilder()
        .sqlQuery(query2)
        .unOrdered()
        .baselineColumns("col")
        .baselineValues(0l)
        .baselineValues(1l)
        .baselineValues(2l)
        .baselineValues(3l)
        .baselineValues(4l)
        .baselineValues(5l)
        .baselineValues(6l)
        .baselineValues(7l)
        .baselineValues(8l)
        .baselineValues(9l)
        .baselineValues(10l)
        .baselineValues(11l)
        .baselineValues(12l)
        .baselineValues(13l)
        .baselineValues(14l)
        .baselineValues(15l)
        .baselineValues(16l)
        .baselineValues(17l)
        .baselineValues(18l)
        .baselineValues(19l)
        .baselineValues(20l)
        .baselineValues(21l)
        .baselineValues(22l)
        .baselineValues(23l)
        .baselineValues(24l)
        .build()
        .run();

    final String[] expectedPlan3 = {
      "Window.*partition \\{0\\}.*SUM\\(\\$0\\)", "Scan.*columns=\\[`n_nationKey`\\].*"
    };
    final String[] excludedPatterns3 = {"Scan.*columns=\\[`\\*`\\].*"};
    PlanTestBase.testPlanMatchingPatterns(query3, expectedPlan3, excludedPatterns3);

    testBuilder()
        .sqlQuery(query3)
        .unOrdered()
        .baselineColumns("col")
        .baselineValues(0l)
        .baselineValues(1l)
        .baselineValues(2l)
        .baselineValues(3l)
        .baselineValues(4l)
        .baselineValues(5l)
        .baselineValues(6l)
        .baselineValues(7l)
        .baselineValues(8l)
        .baselineValues(9l)
        .baselineValues(10l)
        .baselineValues(11l)
        .baselineValues(12l)
        .baselineValues(13l)
        .baselineValues(14l)
        .baselineValues(15l)
        .baselineValues(16l)
        .baselineValues(17l)
        .baselineValues(18l)
        .baselineValues(19l)
        .baselineValues(20l)
        .baselineValues(21l)
        .baselineValues(22l)
        .baselineValues(23l)
        .baselineValues(24l)
        .build()
        .run();
  }

  @Test // DRILL-3204
  public void testWindowWithJoin() throws Exception {
    final String query =
        "select sum(t1.r_regionKey) over(partition by t1.r_regionKey) as col \n"
            + "from cp.`tpch/region.parquet` t1, cp.`tpch/nation.parquet` t2 \n"
            + "where t1.r_regionKey = t2.n_nationKey \n"
            + "group by t1.r_regionKey";

    // Validate the plan
    final String[] expectedPlan = {
      "Window.*partition \\{0\\}.*SUM\\(\\$0\\)",
      "Scan.*columns=\\[`n_nationKey`\\].*",
      "Scan.*columns=\\[`n_nationKey`\\].*"
    };
    final String[] excludedPatterns = {"Scan.*columns=\\[`\\*`\\].*"};
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPatterns);

    testBuilder()
        .sqlQuery(query)
        .unOrdered()
        .baselineColumns("col")
        .baselineValues(0l)
        .baselineValues(1l)
        .baselineValues(2l)
        .baselineValues(3l)
        .baselineValues(4l)
        .build()
        .run();
  }

  @Test // DRILL-3298
  public void testCountEmptyPartitionByWithExchange() throws Exception {
    String query =
        String.format(
            "select count(*) over (order by o_orderpriority) as cnt from dfs.`%s/multilevel/parquet` where o_custkey < 100",
            TEST_RES_PATH);
    try {
      // Validate the plan
      final String[] expectedPlan = {
        "Window.*partition \\{\\} order by \\[0\\].*COUNT\\(\\)",
        "Scan.*columns=\\[`o_custkey`, `o_orderpriority`\\]"
      };
      final String[] excludedPatterns = {"Scan.*columns=\\[`\\*`\\]"};
      PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPatterns);

      testBuilder()
          .sqlQuery(query)
          .ordered()
          .baselineColumns("cnt")
          .optionSettingQueriesForTestQuery("alter session set `planner.slice_target` = 1")
          .baselineValues(1l)
          .baselineValues(4l)
          .baselineValues(4l)
          .baselineValues(4l)
          .build()
          .run();
    } finally {
      test("alter session set `planner.slice_target` = " + ExecConstants.SLICE_TARGET_DEFAULT);
    }
  }

  /* Verify the output of aggregate functions (which are reduced
   * eg: avg(x) = sum(x)/count(x)) return results of the correct
   * data type (double)
   */
  @Test
  public void testAvgVarianceWindowFunctions() throws Exception {
    final String avgQuery =
        "select avg(n_nationkey) over (partition by n_nationkey) col1 "
            + "from cp.`tpch/nation.parquet` "
            + "where n_nationkey = 1";

    // Validate the plan
    final String[] expectedPlan1 = {
      "Window.*partition \\{0\\} order by \\[\\].*SUM\\(\\$0\\), COUNT\\(\\$0\\)",
      "Scan.*columns=\\[`n_nationkey`\\]"
    };
    final String[] excludedPatterns1 = {"Scan.*columns=\\[`\\*`\\]"};
    PlanTestBase.testPlanMatchingPatterns(avgQuery, expectedPlan1, excludedPatterns1);

    testBuilder().sqlQuery(avgQuery).unOrdered().baselineColumns("col1").baselineValues(1.0d).go();

    final String varianceQuery =
        "select var_pop(n_nationkey) over (partition by n_nationkey) col1 "
            + "from cp.`tpch/nation.parquet` "
            + "where n_nationkey = 1";

    // Validate the plan
    final String[] expectedPlan2 = {
      "Window.*partition \\{0\\} order by \\[\\].*SUM\\(\\$2\\), SUM\\(\\$1\\), COUNT\\(\\$1\\)",
      "Scan.*columns=\\[`n_nationkey`\\]"
    };
    final String[] excludedPatterns2 = {"Scan.*columns=\\[`\\*`\\]"};
    PlanTestBase.testPlanMatchingPatterns(varianceQuery, expectedPlan2, excludedPatterns2);

    testBuilder()
        .sqlQuery(varianceQuery)
        .unOrdered()
        .baselineColumns("col1")
        .baselineValues(0.0d)
        .go();
  }

  @Test
  public void testWindowFunctionWithKnownType() throws Exception {
    final String query =
        "select sum(cast(col_int as int)) over (partition by col_varchar) as col1 "
            + "from cp.`jsoninput/large_int.json` limit 1";

    // Validate the plan
    final String[] expectedPlan1 = {
      "Window.*partition \\{0\\} order by \\[\\].*SUM\\(\\$1\\)",
      "Scan.*columns=\\[`col_varchar`, `col_int`\\]"
    };
    final String[] excludedPatterns1 = {"Scan.*columns=\\[`\\*`\\]"};
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan1, excludedPatterns1);

    testBuilder()
        .sqlQuery(query)
        .unOrdered()
        .baselineColumns("col1")
        .baselineValues(2147483649l)
        .go();

    final String avgQuery =
        "select avg(cast(col_int as int)) over (partition by col_varchar) as col1 "
            + "from cp.`jsoninput/large_int.json` limit 1";

    // Validate the plan
    final String[] expectedPlan2 = {
      "Window.*partition \\{0\\} order by \\[\\].*SUM\\(\\$1\\), COUNT\\(\\$1\\)",
      "Scan.*columns=\\[`col_varchar`, `col_int`\\]"
    };
    final String[] excludedPatterns2 = {"Scan.*columns=\\[`\\*`\\]"};
    PlanTestBase.testPlanMatchingPatterns(avgQuery, expectedPlan2, excludedPatterns2);

    testBuilder()
        .sqlQuery(avgQuery)
        .unOrdered()
        .baselineColumns("col1")
        .baselineValues(1.0737418245E9d)
        .go();
  }

  @Test
  public void testCompoundIdentifierInWindowDefinition() throws Exception {
    String root =
        FileUtils.getResourceAsFile("/multilevel/csv/1994/Q1/orders_94_q1.csv").toURI().toString();
    String query =
        String.format(
            "SELECT count(*) OVER w as col1, count(*) OVER w as col2 \n"
                + "FROM dfs_test.`%s` \n"
                + "WINDOW w AS (PARTITION BY columns[1] ORDER BY columns[0] DESC)",
            root);

    // Validate the plan
    final String[] expectedPlan = {
      "Window.*partition \\{1\\} order by \\[0 DESC\\].*COUNT\\(\\)",
      "Scan.*columns=\\[`columns`\\[0\\], `columns`\\[1\\]\\]"
    };
    final String[] excludedPatterns = {"Scan.*columns=\\[`\\*`\\]"};
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPatterns);

    // Validate the result
    testBuilder()
        .sqlQuery(query)
        .ordered()
        .baselineColumns("col1", "col2")
        .baselineValues((long) 1, (long) 1)
        .baselineValues((long) 1, (long) 1)
        .baselineValues((long) 1, (long) 1)
        .baselineValues((long) 1, (long) 1)
        .baselineValues((long) 1, (long) 1)
        .baselineValues((long) 1, (long) 1)
        .baselineValues((long) 1, (long) 1)
        .baselineValues((long) 1, (long) 1)
        .baselineValues((long) 1, (long) 1)
        .baselineValues((long) 1, (long) 1)
        .build()
        .run();
  }

  @Test
  public void testRankWithGroupBy() throws Exception {
    final String query =
        "select dense_rank() over (order by l_suppkey) as rank1 "
            + " from cp.`tpch/lineitem.parquet` group by l_partkey, l_suppkey order by 1 desc limit 1";

    // Validate the plan
    final String[] expectedPlan = {
      "Window.*partition \\{\\} order by \\[1\\].*DENSE_RANK\\(\\)",
      "Scan.*columns=\\[`l_partkey`, `l_suppkey`\\]"
    };
    final String[] excludedPatterns = {"Scan.*columns=\\[`\\*`\\]"};
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPatterns);

    testBuilder().sqlQuery(query).unOrdered().baselineColumns("rank1").baselineValues(100l).go();
  }

  @Test // DRILL-3404
  public void testWindowSumAggIsNotNull() throws Exception {
    String query =
        String.format(
            "select count(*) cnt from (select sum ( c1 ) over ( partition by c2 order by c1 asc nulls first ) w_sum from dfs.`%s/window/table_with_nulls.parquet` ) sub_query where w_sum is not null",
            TEST_RES_PATH);

    // Validate the plan
    final String[] expectedPlan = {
      "Window.*partition \\{1\\} order by \\[0 ASC-nulls-first\\].*SUM\\(\\$0\\)",
      "Scan.*columns=\\[`c1`, `c2`\\]"
    };
    final String[] excludedPatterns = {"Scan.*columns=\\[`\\*`\\]"};
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPatterns);

    testBuilder()
        .sqlQuery(query)
        .ordered()
        .baselineColumns("cnt")
        .baselineValues(26l)
        .build()
        .run();
  }

  @Test // DRILL-3292
  public void testWindowConstants() throws Exception {
    String query =
        "select rank() over w fn, sum(2) over w sumINTEGER, sum(employee_id) over w sumEmpId, sum(0.5) over w sumFLOAT \n"
            + "from cp.`employee.json` \n"
            + "where position_id = 2 \n"
            + "window w as(partition by position_id order by employee_id)";

    // Validate the plan
    final String[] expectedPlan = {
      "Window.*partition \\{0\\} order by \\[1\\].*RANK\\(\\), SUM\\(\\$2\\), SUM\\(\\$1\\), SUM\\(\\$3\\)",
      "Scan.*columns=\\[`position_id`, `employee_id`\\]"
    };
    final String[] excludedPatterns = {"Scan.*columns=\\[`\\*`\\]"};
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPatterns);

    testBuilder()
        .sqlQuery(query)
        .ordered()
        .baselineColumns("fn", "sumINTEGER", "sumEmpId", "sumFLOAT")
        .baselineValues(1l, 2l, 2l, 0.5)
        .baselineValues(2l, 4l, 6l, 1.0)
        .baselineValues(3l, 6l, 11l, 1.5)
        .baselineValues(4l, 8l, 31l, 2.0)
        .baselineValues(5l, 10l, 52l, 2.5)
        .baselineValues(6l, 12l, 74l, 3.0)
        .build()
        .run();
  }

  @Test // DRILL-3567
  public void testMultiplePartitions1() throws Exception {
    String root = FileUtils.getResourceAsFile("/store/text/data/t.json").toURI().toString();
    String query =
        String.format(
            "select count(*) over(partition by b1 order by c1) as count1, \n"
                + "sum(a1)  over(partition by b1 order by c1) as sum1, \n"
                + "count(*) over(partition by a1 order by c1) as count2 \n"
                + "from dfs_test.`%s`",
            root);

    // Validate the plan
    final String[] expectedPlan = {
      "Window.*partition \\{2\\} order by \\[1\\].*COUNT\\(\\)",
      "Window.*partition \\{0\\} order by \\[1\\].*COUNT\\(\\), SUM\\(\\$2\\)",
      "Scan.*columns=\\[`b1`, `c1`, `a1`\\]"
    };
    final String[] excludedPatterns = {"Scan.*columns=\\[`\\*`\\]"};
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPatterns);

    testBuilder()
        .sqlQuery(query)
        .unOrdered()
        .baselineColumns("count1", "sum1", "count2")
        .baselineValues(1l, 0l, 2l)
        .baselineValues(1l, 0l, 2l)
        .baselineValues(2l, 0l, 5l)
        .baselineValues(3l, 0l, 5l)
        .baselineValues(3l, 0l, 5l)
        .baselineValues(1l, 10l, 2l)
        .baselineValues(1l, 10l, 2l)
        .baselineValues(2l, 20l, 5l)
        .baselineValues(3l, 30l, 5l)
        .baselineValues(3l, 30l, 5l)
        .build()
        .run();
  }

  @Test // DRILL-3567
  public void testMultiplePartitions2() throws Exception {
    String root = FileUtils.getResourceAsFile("/store/text/data/t.json").toURI().toString();
    String query =
        String.format(
            "select count(*) over(partition by b1 order by c1) as count1, \n"
                + "count(*) over(partition by a1 order by c1) as count2, \n"
                + "sum(a1)  over(partition by b1 order by c1) as sum1 \n"
                + "from dfs_test.`%s`",
            root);

    // Validate the plan
    final String[] expectedPlan = {
      "Window.*partition \\{2\\} order by \\[1\\].*COUNT\\(\\)",
      "Window.*partition \\{0\\} order by \\[1\\].*COUNT\\(\\), SUM\\(\\$2\\)",
      "Scan.*columns=\\[`b1`, `c1`, `a1`\\]"
    };
    final String[] excludedPatterns = {"Scan.*columns=\\[`\\*`\\]"};
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, excludedPatterns);

    testBuilder()
        .sqlQuery(query)
        .unOrdered()
        .baselineColumns("count1", "count2", "sum1")
        .baselineValues(1l, 2l, 0l)
        .baselineValues(1l, 2l, 0l)
        .baselineValues(2l, 5l, 0l)
        .baselineValues(3l, 5l, 0l)
        .baselineValues(3l, 5l, 0l)
        .baselineValues(1l, 2l, 10l)
        .baselineValues(1l, 2l, 10l)
        .baselineValues(2l, 5l, 20l)
        .baselineValues(3l, 5l, 30l)
        .baselineValues(3l, 5l, 30l)
        .build()
        .run();
  }

  @Test // see DRILL-3574
  public void testWithAndWithoutPartitions() throws Exception {
    String root = FileUtils.getResourceAsFile("/store/text/data/t.json").toURI().toString();
    String query =
        String.format(
            "select sum(a1) over(partition by b1, c1) as s1, sum(a1) over() as s2 \n"
                + "from dfs_test.`%s` \n"
                + "order by a1",
            root);
    test("alter session set `planner.slice_target` = 1");

    // Validate the plan
    final String[] expectedPlan = {
      "Window\\(window#0=\\[window\\(partition \\{\\}.*\n" + ".*UnionExchange"
    };
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, new String[] {});

    testBuilder()
        .sqlQuery(query)
        .unOrdered()
        .baselineColumns("s1", "s2")
        .baselineValues(0l, 50l)
        .baselineValues(0l, 50l)
        .baselineValues(0l, 50l)
        .baselineValues(0l, 50l)
        .baselineValues(0l, 50l)
        .baselineValues(10l, 50l)
        .baselineValues(10l, 50l)
        .baselineValues(10l, 50l)
        .baselineValues(20l, 50l)
        .baselineValues(20l, 50l)
        .build()
        .run();
  }

  @Test // see DRILL-3657
  public void testConstantsInMultiplePartitions() throws Exception {
    String root = FileUtils.getResourceAsFile("/store/text/data/t.json").toURI().toString();
    String query =
        String.format(
            "select sum(1) over(partition by b1 order by a1) as sum1, sum(1) over(partition by a1) as sum2, rank() over(order by b1) as rank1, rank() over(order by 1) as rank2 \n"
                + "from dfs_test.`%s` \n"
                + "order by 1, 2, 3, 4",
            root);

    // Validate the plan
    final String[] expectedPlan = {
      "Window.*SUM\\(\\$3\\).*\n"
          + ".*SelectionVectorRemover.*\n"
          + ".*Sort.*\n"
          + ".*Window.*SUM\\(\\$2\\).*"
    };
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, new String[] {});

    testBuilder()
        .sqlQuery(query)
        .unOrdered()
        .baselineColumns("sum1", "sum2", "rank1", "rank2")
        .baselineValues(2l, 5l, 1l, 1l)
        .baselineValues(2l, 5l, 1l, 1l)
        .baselineValues(2l, 5l, 6l, 1l)
        .baselineValues(2l, 5l, 6l, 1l)
        .baselineValues(3l, 5l, 3l, 1l)
        .baselineValues(3l, 5l, 3l, 1l)
        .baselineValues(3l, 5l, 3l, 1l)
        .baselineValues(3l, 5l, 8l, 1l)
        .baselineValues(3l, 5l, 8l, 1l)
        .baselineValues(3l, 5l, 8l, 1l)
        .build()
        .run();
  }

  @Test // DRILL-3580
  public void testExpressionInWindowFunction() throws Exception {
    String root = FileUtils.getResourceAsFile("/store/text/data/t.json").toURI().toString();
    String query =
        String.format(
            "select a1, b1, sum(b1) over (partition by a1) as c1, sum(a1 + b1) over (partition by a1) as c2\n"
                + "from dfs_test.`%s`",
            root);

    // Validate the plan
    final String[] expectedPlan = {
      "Window\\(window#0=\\[window\\(partition \\{0\\} order by \\[\\].*\\[SUM\\(\\$1\\), SUM\\(\\$2\\)\\]"
    };
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, new String[] {});

    testBuilder()
        .sqlQuery(query)
        .unOrdered()
        .baselineColumns("a1", "b1", "c1", "c2")
        .baselineValues(0l, 1l, 8l, 8l)
        .baselineValues(0l, 1l, 8l, 8l)
        .baselineValues(0l, 2l, 8l, 8l)
        .baselineValues(0l, 2l, 8l, 8l)
        .baselineValues(0l, 2l, 8l, 8l)
        .baselineValues(10l, 3l, 21l, 71l)
        .baselineValues(10l, 3l, 21l, 71l)
        .baselineValues(10l, 5l, 21l, 71l)
        .baselineValues(10l, 5l, 21l, 71l)
        .baselineValues(10l, 5l, 21l, 71l)
        .build()
        .run();
  }

  @Test // see DRILL-3657
  public void testProjectPushPastWindow() throws Exception {
    String query =
        "select sum(n_nationkey) over(partition by 1 order by 1) as col1, \n"
            + "count(n_nationkey) over(partition by 1 order by 1) as col2 \n"
            + "from cp.`tpch/nation.parquet` \n"
            + "limit 5";

    // Validate the plan
    final String[] expectedPlan = {"Scan.*columns=\\[`n_nationkey`\\].*"};
    PlanTestBase.testPlanMatchingPatterns(query, expectedPlan, new String[] {});

    testBuilder()
        .sqlQuery(query)
        .unOrdered()
        .baselineColumns("col1", "col2")
        .baselineValues(300l, 25l)
        .baselineValues(300l, 25l)
        .baselineValues(300l, 25l)
        .baselineValues(300l, 25l)
        .baselineValues(300l, 25l)
        .build()
        .run();
  }

  @Test // DRILL-3679, DRILL-3680
  public void testWindowFunInNestSubQ() throws Exception {
    final String query =
        " select n_nationkey , n_regionkey , "
            + "        lead(n_regionkey) OVER ( PARTITION BY n_regionkey ORDER BY n_nationkey) lead_c2 "
            + " FROM (SELECT n_nationkey ,n_regionkey, "
            + "          ntile(3) over(PARTITION BY n_regionkey ORDER BY n_nationkey) "
            + "       FROM cp.`tpch/nation.parquet`) "
            + " order by n_regionkey, n_nationkey";
    test(query);

    final String baselineQuery =
        "select n_nationkey , n_regionkey , "
            + "       lead(n_regionkey) OVER ( PARTITION BY n_regionkey ORDER BY n_nationkey) lead_c2 "
            + "FROM cp.`tpch/nation.parquet`   "
            + "order by n_regionkey, n_nationkey";

    testBuilder().sqlQuery(query).ordered().sqlBaselineQuery(baselineQuery).build().run();

    final String query2 =
        " select rnum, position_id, "
            + "   ntile(4) over(order by position_id) "
            + " from (select position_id, row_number() "
            + "       over(order by position_id) as rnum "
            + "       from cp.`employee.json`)";

    final String baselineQuery2 =
        " select row_number() over(order by position_id) as rnum, "
            + "    position_id, "
            + "    ntile(4) over(order by position_id) "
            + " from cp.`employee.json`";

    testBuilder().sqlQuery(query2).ordered().sqlBaselineQuery(baselineQuery2).build().run();
  }

  @Test
  public void testStatisticalWindowFunctions() throws Exception {
    final String sqlWindowFunctionQuery =
        "select "
            + "stddev_samp(employee_id) over (partition by 1) c1, "
            + "stddev_pop(employee_id) over (partition by 1) c2, "
            + "var_samp(employee_id) over (partition by 1) c3, "
            + "var_pop(employee_id) over (partition by 1) c4 from "
            + "cp.`employee.json` limit 1";

    testBuilder()
        .sqlQuery(sqlWindowFunctionQuery)
        .unOrdered()
        .baselineColumns("c1", "c2", "c3", "c4")
        .baselineValues(
            333.56708470261117d, 333.4226520980038d, 111266.99999699896d, 111170.66493206649d)
        .build()
        .run();
  }
}
/**
 * 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.
 *
 * <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 if (isOkaySpecialCaseException(method, cause)) {
          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);
        }
      }
    }

    /**
     * Reports whether it's okay if given method throw given exception (that is not preferred
     * AlreadyClosedException with regular message).
     */
    protected boolean isOkaySpecialCaseException(Method method, Throwable cause) {
      return false;
    }

    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;
    }

    @Override
    protected Statement getJdbcObject() throws SQLException {
      return factoryConnection.createStatement();
    }

    @Override
    protected boolean isOkaySpecialCaseException(Method method, Throwable cause) {
      // New Java 8 method not supported by Avatica

      return method.getName().equals("executeLargeBatch");
    }
  } // 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;
    }

    @Override
    protected PreparedStatement getJdbcObject() throws SQLException {
      return factoryConnection.prepareStatement("VALUES 1");
    }

    @Override
    protected boolean isOkaySpecialCaseException(Method method, Throwable cause) {
      // New Java 8 method not supported by Avatica

      return method.getName().equals("executeLargeBatch");
    }
  } // 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());
    }
  }
}
public class JdbcTestActionBase extends JdbcTestBase {
  // Set a timeout unless we're debugging.
  @Rule public TestRule TIMEOUT = TestTools.getTimeoutRule(40000);

  protected static final String WORKING_PATH;

  static {
    Driver.load();
    WORKING_PATH = Paths.get("").toAbsolutePath().toString();
  }

  protected void testQuery(final String sql) throws Exception {
    testAction(
        new JdbcAction() {

          @Override
          public ResultSet getResult(Connection c) throws SQLException {
            Statement s = c.createStatement();
            ResultSet r = s.executeQuery(sql);
            return r;
          }
        });
  }

  protected void testAction(JdbcAction action) throws Exception {
    testAction(action, -1);
  }

  protected void testAction(JdbcAction action, long rowcount) throws Exception {
    int rows = 0;
    Stopwatch watch = Stopwatch.createStarted();
    ResultSet r = action.getResult(connection);
    boolean first = true;
    while (r.next()) {
      rows++;
      ResultSetMetaData md = r.getMetaData();
      if (first == true) {
        for (int i = 1; i <= md.getColumnCount(); i++) {
          System.out.print(md.getColumnName(i));
          System.out.print('\t');
        }
        System.out.println();
        first = false;
      }

      for (int i = 1; i <= md.getColumnCount(); i++) {
        System.out.print(r.getObject(i));
        System.out.print('\t');
      }
      System.out.println();
    }

    System.out.println(
        String.format("Query completed in %d millis.", watch.elapsed(TimeUnit.MILLISECONDS)));

    if (rowcount != -1) {
      Assert.assertEquals((long) rowcount, (long) rows);
    }

    System.out.println("\n\n\n");
  }

  public static interface JdbcAction {
    ResultSet getResult(Connection c) throws SQLException;
  }

  static void resetConnection() throws Exception {
    closeClient();
    openClient();
  }

  static Connection connection;

  @BeforeClass
  public static void openClient() throws Exception {
    connection =
        DriverManager.getConnection("jdbc:drill:zk=local", JdbcAssert.getDefaultProperties());
  }

  @AfterClass
  public static void closeClient() throws IOException, SQLException {
    connection.close();
  }

  public final TestRule resetWatcher =
      new TestWatcher() {
        @Override
        protected void failed(Throwable e, Description description) {
          try {
            resetConnection();
          } catch (Exception e1) {
            throw new RuntimeException("Failure while resetting client.", e1);
          }
        }
      };
}