/**
   * Produces single-node and distributed plans for testCase and compares plan and scan range
   * results. Appends the actual single-node and distributed plan as well as the printed scan ranges
   * to actualOutput, along with the requisite section header. locations to
   * actualScanRangeLocations; compares both to the appropriate sections of 'testCase'.
   */
  private void RunTestCase(
      TestCase testCase,
      StringBuilder errorLog,
      StringBuilder actualOutput,
      String dbName,
      TQueryOptions options)
      throws CatalogException {

    if (options == null) {
      options = defaultQueryOptions();
    } else {
      options = mergeQueryOptions(defaultQueryOptions(), options);
    }

    String query = testCase.getQuery();
    LOG.info("running query " + query);
    if (query.isEmpty()) {
      throw new IllegalStateException(
          "Cannot plan empty query in line: " + testCase.getStartingLineNum());
    }
    TQueryCtx queryCtx = TestUtils.createQueryContext(dbName, System.getProperty("user.name"));
    queryCtx.request.query_options = options;
    // single-node plan and scan range locations
    testSingleNodePlan(testCase, queryCtx, errorLog, actualOutput);
    testDistributedPlan(testCase, queryCtx, errorLog, actualOutput);
    testColumnLineageOutput(testCase, queryCtx, errorLog, actualOutput);
  }
Example #2
0
 /**
  * Produces distributed plan for testCase and compares actual plan with expected plan. If testCase
  * contains no expected distributed plan then this function is a no-op.
  */
 private void testDistributedPlan(
     TestCase testCase,
     TQueryContext queryCtxt,
     StringBuilder errorLog,
     StringBuilder actualOutput)
     throws CatalogException {
   ArrayList<String> expectedPlan = testCase.getSectionContents(Section.DISTRIBUTEDPLAN);
   // Test case has no expected distributed plan. Do not test it.
   if (expectedPlan == null || expectedPlan.isEmpty()) return;
   String query = testCase.getQuery();
   String expectedErrorMsg = getExpectedErrorMessage(expectedPlan);
   queryCtxt.request.getQuery_options().setNum_nodes(ImpalaInternalServiceConstants.NUM_NODES_ALL);
   queryCtxt.request.setStmt(query);
   boolean isImplemented = expectedErrorMsg == null;
   StringBuilder explainBuilder = new StringBuilder();
   actualOutput.append(Section.DISTRIBUTEDPLAN.getHeader() + "\n");
   TExecRequest execRequest = null;
   try {
     // distributed plan
     execRequest = frontend_.createExecRequest(queryCtxt, explainBuilder);
     String explainStr = removeExplainHeader(explainBuilder.toString());
     actualOutput.append(explainStr);
     if (!isImplemented) {
       errorLog.append(
           "query produced DISTRIBUTEDPLAN\nquery=" + query + "\nplan=\n" + explainStr);
     } else {
       LOG.info("distributed plan: " + explainStr);
       String result =
           TestUtils.compareOutput(Lists.newArrayList(explainStr.split("\n")), expectedPlan, true);
       if (!result.isEmpty()) {
         errorLog.append(
             "section "
                 + Section.DISTRIBUTEDPLAN.toString()
                 + " of query:\n"
                 + query
                 + "\n"
                 + result);
       }
     }
   } catch (ImpalaException e) {
     if (e instanceof AnalysisException) {
       errorLog.append("query:\n" + query + "\nanalysis error: " + e.getMessage() + "\n");
       return;
     } else if (e instanceof InternalException) {
       errorLog.append("query:\n" + query + "\ninternal error: " + e.getMessage() + "\n");
       return;
     }
     if (e instanceof NotImplementedException) {
       handleNotImplException(query, expectedErrorMsg, errorLog, actualOutput, e);
     } else if (e instanceof CatalogException) {
       throw (CatalogException) e;
     } else {
       errorLog.append("query:\n" + query + "\nunhandled exception: " + e.getMessage() + "\n");
     }
   }
 }
 private void testColumnLineageOutput(
     TestCase testCase, TQueryCtx queryCtx, StringBuilder errorLog, StringBuilder actualOutput)
     throws CatalogException {
   ArrayList<String> expectedLineage = testCase.getSectionContents(Section.LINEAGE);
   if (expectedLineage == null || expectedLineage.isEmpty()) return;
   String query = testCase.getQuery();
   queryCtx.request.getQuery_options().setNum_nodes(1);
   queryCtx.request.setStmt(query);
   StringBuilder explainBuilder = new StringBuilder();
   TExecRequest execRequest = null;
   String lineageGraph = null;
   try {
     execRequest = frontend_.createExecRequest(queryCtx, explainBuilder);
     if (execRequest.isSetQuery_exec_request()) {
       lineageGraph = execRequest.query_exec_request.lineage_graph;
     } else if (execRequest.isSetCatalog_op_request()) {
       lineageGraph = execRequest.catalog_op_request.lineage_graph;
     }
   } catch (ImpalaException e) {
     if (e instanceof AnalysisException) {
       errorLog.append("query:\n" + query + "\nanalysis error: " + e.getMessage() + "\n");
       return;
     } else if (e instanceof InternalException) {
       errorLog.append("query:\n" + query + "\ninternal error: " + e.getMessage() + "\n");
       return;
     }
     if (e instanceof NotImplementedException) {
       handleNotImplException(query, "", errorLog, actualOutput, e);
     } else if (e instanceof CatalogException) {
       throw (CatalogException) e;
     } else {
       errorLog.append("query:\n" + query + "\nunhandled exception: " + e.getMessage() + "\n");
     }
   }
   LOG.info("lineage graph: " + lineageGraph);
   ArrayList<String> expected = testCase.getSectionContents(Section.LINEAGE);
   if (expected.size() > 0 && lineageGraph != null) {
     String serializedGraph = Joiner.on("\n").join(expected);
     ColumnLineageGraph expectedGraph = ColumnLineageGraph.createFromJSON(serializedGraph);
     ColumnLineageGraph outputGraph = ColumnLineageGraph.createFromJSON(lineageGraph);
     if (expectedGraph == null || outputGraph == null || !outputGraph.equals(expectedGraph)) {
       StringBuilder lineageError = new StringBuilder();
       lineageError.append("section " + Section.LINEAGE + " of query:\n" + query + "\n");
       lineageError.append("Output:\n");
       lineageError.append(lineageGraph + "\n");
       lineageError.append("Expected:\n");
       lineageError.append(serializedGraph + "\n");
       errorLog.append(lineageError.toString());
     }
     actualOutput.append(Section.LINEAGE.getHeader());
     actualOutput.append(TestUtils.prettyPrintJson(lineageGraph) + "\n");
   }
 }
Example #4
0
 /**
  * Produces single-node and distributed plans for testCase and compares plan and scan range
  * results. Appends the actual single-node and distributed plan as well as the printed scan ranges
  * to actualOutput, along with the requisite section header. locations to
  * actualScanRangeLocations; compares both to the appropriate sections of 'testCase'.
  */
 private void RunTestCase(
     TestCase testCase, StringBuilder errorLog, StringBuilder actualOutput, String dbName)
     throws CatalogException {
   String query = testCase.getQuery();
   LOG.info("running query " + query);
   TQueryContext queryCtxt = TestUtils.createQueryContext(dbName, System.getProperty("user.name"));
   queryCtxt.request.query_options.setExplain_level(TExplainLevel.STANDARD);
   queryCtxt.request.query_options.allow_unsupported_formats = true;
   // single-node plan and scan range locations
   testSingleNodePlan(testCase, queryCtxt, errorLog, actualOutput);
   // distributed plan
   testDistributedPlan(testCase, queryCtxt, errorLog, actualOutput);
 }
Example #5
0
  private void runPlannerTestFile(String testFile, String dbName) {
    String fileName = testDir_ + "/" + testFile + ".test";
    TestFileParser queryFileParser = new TestFileParser(fileName);
    StringBuilder actualOutput = new StringBuilder();

    queryFileParser.parseFile();
    StringBuilder errorLog = new StringBuilder();
    for (TestCase testCase : queryFileParser.getTestCases()) {
      actualOutput.append(testCase.getSectionAsString(Section.QUERY, true, "\n"));
      actualOutput.append("\n");
      try {
        RunTestCase(testCase, errorLog, actualOutput, dbName);
      } catch (CatalogException e) {
        errorLog.append(
            String.format("Failed to plan query\n%s\n%s", testCase.getQuery(), e.getMessage()));
      }
      actualOutput.append("====\n");
    }

    // Create the actual output file
    if (GENERATE_OUTPUT_FILE) {
      try {
        File outDirFile = new File(outDir_);
        outDirFile.mkdirs();
        FileWriter fw = new FileWriter(outDir_ + testFile + ".test");
        fw.write(actualOutput.toString());
        fw.close();
      } catch (IOException e) {
        errorLog.append("Unable to create output file: " + e.getMessage());
      }
    }

    if (errorLog.length() != 0) {
      fail(errorLog.toString());
    }
  }
Example #6
0
  /**
   * Produces single-node plan for testCase and compares actual plan with expected plan, as well as
   * the scan range locations. If testCase contains no expected single-node plan then this function
   * is a no-op.
   */
  private void testSingleNodePlan(
      TestCase testCase,
      TQueryContext queryCtxt,
      StringBuilder errorLog,
      StringBuilder actualOutput)
      throws CatalogException {
    ArrayList<String> expectedPlan = testCase.getSectionContents(Section.PLAN);
    // Test case has no expected single-node plan. Do not test it.
    if (expectedPlan == null || expectedPlan.isEmpty()) return;
    String query = testCase.getQuery();
    String expectedErrorMsg = getExpectedErrorMessage(expectedPlan);
    queryCtxt.request.getQuery_options().setNum_nodes(1);
    queryCtxt.request.setStmt(query);
    boolean isImplemented = expectedErrorMsg == null;
    StringBuilder explainBuilder = new StringBuilder();

    TExecRequest execRequest = null;
    String locationsStr = null;
    actualOutput.append(Section.PLAN.getHeader() + "\n");
    try {
      execRequest = frontend_.createExecRequest(queryCtxt, explainBuilder);
      String explainStr = removeExplainHeader(explainBuilder.toString());
      actualOutput.append(explainStr);
      if (!isImplemented) {
        errorLog.append("query produced PLAN\nquery=" + query + "\nplan=\n" + explainStr);
      } else {
        LOG.info("single-node plan: " + explainStr);
        String result =
            TestUtils.compareOutput(Lists.newArrayList(explainStr.split("\n")), expectedPlan, true);
        if (!result.isEmpty()) {
          errorLog.append(
              "section " + Section.PLAN.toString() + " of query:\n" + query + "\n" + result);
        }
        // Query exec request may not be set for DDL, e.g., CTAS.
        if (execRequest.isSetQuery_exec_request()) {
          locationsStr = PrintScanRangeLocations(execRequest.query_exec_request).toString();
        }
      }
    } catch (ImpalaException e) {
      if (e instanceof AnalysisException) {
        errorLog.append("query:\n" + query + "\nanalysis error: " + e.getMessage() + "\n");
        return;
      } else if (e instanceof InternalException) {
        errorLog.append("query:\n" + query + "\ninternal error: " + e.getMessage() + "\n");
        return;
      }
      if (e instanceof NotImplementedException) {
        handleNotImplException(query, expectedErrorMsg, errorLog, actualOutput, e);
      } else if (e instanceof CatalogException) {
        // TODO: do we need to rethrow?
        throw (CatalogException) e;
      } else {
        errorLog.append("query:\n" + query + "\nunhandled exception: " + e.getMessage() + "\n");
      }
    }

    // compare scan range locations
    LOG.info("scan range locations: " + locationsStr);
    ArrayList<String> expectedLocations = testCase.getSectionContents(Section.SCANRANGELOCATIONS);

    if (expectedLocations.size() > 0 && locationsStr != null) {
      // Locations' order does not matter.
      String result =
          TestUtils.compareOutput(
              Lists.newArrayList(locationsStr.split("\n")), expectedLocations, false);
      if (!result.isEmpty()) {
        errorLog.append(
            "section " + Section.SCANRANGELOCATIONS + " of query:\n" + query + "\n" + result);
      }
      actualOutput.append(Section.SCANRANGELOCATIONS.getHeader() + "\n");
      actualOutput.append(locationsStr);
      // TODO: check that scan range locations are identical in both cases
    }
  }
  /**
   * Produces single-node plan for testCase and compares actual plan with expected plan, as well as
   * the scan range locations. If testCase contains no expected single-node plan then this function
   * is a no-op.
   */
  private void testSingleNodePlan(
      TestCase testCase, TQueryCtx queryCtx, StringBuilder errorLog, StringBuilder actualOutput)
      throws CatalogException {
    ArrayList<String> expectedPlan = testCase.getSectionContents(Section.PLAN);
    // Test case has no expected single-node plan. Do not test it.
    if (expectedPlan == null || expectedPlan.isEmpty()) return;
    String query = testCase.getQuery();
    String expectedErrorMsg = getExpectedErrorMessage(expectedPlan);
    queryCtx.request.getQuery_options().setNum_nodes(1);
    queryCtx.request.setStmt(query);
    boolean isImplemented = expectedErrorMsg == null;
    StringBuilder explainBuilder = new StringBuilder();

    TExecRequest execRequest = null;
    String locationsStr = null;
    actualOutput.append(Section.PLAN.getHeader() + "\n");
    try {
      execRequest = frontend_.createExecRequest(queryCtx, explainBuilder);
      buildMaps(execRequest.query_exec_request);
      String explainStr = removeExplainHeader(explainBuilder.toString());
      actualOutput.append(explainStr);
      if (!isImplemented) {
        errorLog.append("query produced PLAN\nquery=" + query + "\nplan=\n" + explainStr);
      } else {
        LOG.info("single-node plan: " + explainStr);
        String result =
            TestUtils.compareOutput(Lists.newArrayList(explainStr.split("\n")), expectedPlan, true);
        if (!result.isEmpty()) {
          errorLog.append(
              "section " + Section.PLAN.toString() + " of query:\n" + query + "\n" + result);
        }
        // Query exec request may not be set for DDL, e.g., CTAS.
        if (execRequest.isSetQuery_exec_request()) {
          testHdfsPartitionsReferenced(execRequest.query_exec_request, query, errorLog);
          locationsStr = PrintScanRangeLocations(execRequest.query_exec_request).toString();
        }
      }
    } catch (ImpalaException e) {
      if (e instanceof AnalysisException) {
        errorLog.append("query:\n" + query + "\nanalysis error: " + e.getMessage() + "\n");
        return;
      } else if (e instanceof InternalException) {
        errorLog.append("query:\n" + query + "\ninternal error: " + e.getMessage() + "\n");
        return;
      }
      if (e instanceof NotImplementedException) {
        handleNotImplException(query, expectedErrorMsg, errorLog, actualOutput, e);
      } else if (e instanceof CatalogException) {
        // TODO: do we need to rethrow?
        throw (CatalogException) e;
      } else {
        errorLog.append("query:\n" + query + "\nunhandled exception: " + e.getMessage() + "\n");
      }
    }

    // compare scan range locations
    LOG.info("scan range locations: " + locationsStr);
    ArrayList<String> expectedLocations = testCase.getSectionContents(Section.SCANRANGELOCATIONS);

    if (expectedLocations.size() > 0 && locationsStr != null) {
      // Locations' order does not matter.
      String result =
          TestUtils.compareOutput(
              Lists.newArrayList(locationsStr.split("\n")), expectedLocations, false);
      if (!result.isEmpty()) {
        errorLog.append(
            "section " + Section.SCANRANGELOCATIONS + " of query:\n" + query + "\n" + result);
      }
      actualOutput.append(Section.SCANRANGELOCATIONS.getHeader() + "\n");
      // Print the locations out sorted since the order is random and messed up
      // the diffs. The values in locationStr contains "Node X" labels as well
      // as paths.
      ArrayList<String> locations = Lists.newArrayList(locationsStr.split("\n"));
      ArrayList<String> perNodeLocations = Lists.newArrayList();

      for (int i = 0; i < locations.size(); ++i) {
        if (locations.get(i).startsWith("NODE")) {
          if (!perNodeLocations.isEmpty()) {
            Collections.sort(perNodeLocations);
            actualOutput.append(Joiner.on("\n").join(perNodeLocations)).append("\n");
            perNodeLocations.clear();
          }
          actualOutput.append(locations.get(i)).append("\n");
        } else {
          perNodeLocations.add(locations.get(i));
        }
      }

      if (!perNodeLocations.isEmpty()) {
        Collections.sort(perNodeLocations);
        actualOutput.append(Joiner.on("\n").join(perNodeLocations)).append("\n");
      }

      // TODO: check that scan range locations are identical in both cases
    }
  }