Esempio n. 1
0
  /**
   * This method behaves similarly to parse(), but allows the caller to pass in XML to avoid
   * re-parsing SQL text that has already gone through HSQL.
   *
   * @param xmlSql XML produced by previous invocation of HSQL
   */
  public void parseFromXml(VoltXMLElement xmlSQL) {
    m_recentErrorMsg = null;
    m_xmlSQL = xmlSQL;
    if (m_xmlSQL.attributes.containsKey(UPSERT_TAG)) {
      m_isUpsert = true;
    }

    m_planSelector.outputCompiledStatement(m_xmlSQL);
  }
Esempio n. 2
0
  /**
   * Auto-parameterize all of the literals in the parsed SQL statement.
   *
   * @return An opaque token representing the parsed statement with (possibly) parameterization.
   */
  public String parameterize() {
    m_paramzInfo = ParameterizationInfo.parameterize(m_xmlSQL);

    Set<Integer> paramIds = new HashSet<Integer>();
    ParameterizationInfo.findUserParametersRecursively(m_xmlSQL, paramIds);
    m_adhocUserParamsCount = paramIds.size();

    // skip plans with pre-existing parameters and plans that don't parameterize
    // assume a user knows how to cache/optimize these
    if (m_paramzInfo != null) {
      // if requested output the second version of the parsed plan
      m_planSelector.outputParameterizedCompiledStatement(m_paramzInfo.parameterizedXmlSQL);
      return m_paramzInfo.parameterizedXmlSQL.toMinString();
    }

    // fallback when parameterization is
    return m_xmlSQL.toMinString();
  }
Esempio n. 3
0
  /**
   * Parse a SQL literal statement into an unplanned, intermediate representation. This is normally
   * followed by a call to {@link this#plan(AbstractCostModel, String, String, String, String, int,
   * ScalarValueHints[]) }, but splitting these two affords an opportunity to check a cache for a
   * plan matching the auto-parameterized parsed statement.
   */
  public void parse() throws PlanningErrorException {
    // reset any error message
    m_recentErrorMsg = null;

    // Reset plan node ids to start at 1 for this plan
    AbstractPlanNode.resetPlanNodeIds();

    // determine the type of the query
    //
    // (Hmmm...  seems like this pre-processing of the SQL text
    // and subsequent placement of UPSERT_TAG should be pushed down
    // into getXMLCompiledStatement)
    m_sql = m_sql.trim();
    if (m_sql.length() > 6
        && m_sql.substring(0, 6).toUpperCase().startsWith("UPSERT")) { // ENG-7395
      m_isUpsert = true;
      m_sql = "INSERT" + m_sql.substring(6);
    }

    // use HSQLDB to get XML that describes the semantics of the statement
    // this is much easier to parse than SQL and is checked against the catalog
    try {
      m_xmlSQL = m_HSQL.getXMLCompiledStatement(m_sql);
    } catch (HSQLParseException e) {
      // XXXLOG probably want a real log message here
      throw new PlanningErrorException(e.getMessage());
    }

    if (m_isUpsert) {
      assert (m_xmlSQL.name.equalsIgnoreCase("INSERT"));
      // for AdHoc cache distinguish purpose which is based on the XML
      m_xmlSQL.attributes.put(UPSERT_TAG, "true");
    }

    m_planSelector.outputCompiledStatement(m_xmlSQL);
  }
Esempio n. 4
0
  private CompiledPlan compileFromXML(VoltXMLElement xmlSQL, String[] paramValues) {
    // Get a parsed statement from the xml
    // The callers of compilePlan are ready to catch any exceptions thrown here.
    AbstractParsedStmt parsedStmt =
        AbstractParsedStmt.parse(m_sql, xmlSQL, paramValues, m_db, m_joinOrder);
    if (parsedStmt == null) {
      m_recentErrorMsg = "Failed to parse SQL statement: " + getOriginalSql();
      return null;
    }

    if (m_isUpsert) {
      // no insert/upsert with joins
      if (parsedStmt.m_tableList.size() != 1) {
        m_recentErrorMsg = "UPSERT is support only with one single table: " + getOriginalSql();
        return null;
      }

      Table tb = parsedStmt.m_tableList.get(0);
      Constraint pkey = null;
      for (Constraint ct : tb.getConstraints()) {
        if (ct.getType() == ConstraintType.PRIMARY_KEY.getValue()) {
          pkey = ct;
          break;
        }
      }

      if (pkey == null) {
        m_recentErrorMsg = "Unsupported UPSERT table without primary key: " + getOriginalSql();
        return null;
      }
    }

    m_planSelector.outputParsedStatement(parsedStmt);

    // Init Assembler. Each plan assembler requires a new instance of the PlanSelector
    // to keep track of the best plan
    PlanAssembler assembler =
        new PlanAssembler(m_cluster, m_db, m_partitioning, (PlanSelector) m_planSelector.clone());
    // find the plan with minimal cost
    CompiledPlan bestPlan = assembler.getBestCostPlan(parsedStmt);

    // This processing of bestPlan outside/after getBestCostPlan
    // allows getBestCostPlan to be called both here and
    // in PlanAssembler.getNextUnion on each branch of a union.

    // make sure we got a winner
    if (bestPlan == null) {
      if (m_debuggingStaticModeToRetryOnError) {
        assembler.getBestCostPlan(parsedStmt);
      }
      m_recentErrorMsg = assembler.getErrorMessage();
      if (m_recentErrorMsg == null) {
        m_recentErrorMsg = "Unable to plan for statement. Error unknown.";
      }
      return null;
    }

    if (bestPlan.isReadOnly()) {
      SendPlanNode sendNode = new SendPlanNode();
      // connect the nodes to build the graph
      sendNode.addAndLinkChild(bestPlan.rootPlanGraph);
      // this plan is final, generate schema and resolve all the column index references
      bestPlan.rootPlanGraph = sendNode;
    }

    // Execute the generateOutputSchema and resolveColumnIndexes once for the best plan
    bestPlan.rootPlanGraph.generateOutputSchema(m_db);
    bestPlan.rootPlanGraph.resolveColumnIndexes();

    if (parsedStmt instanceof ParsedSelectStmt) {
      List<SchemaColumn> columns = bestPlan.rootPlanGraph.getOutputSchema().getColumns();
      ((ParsedSelectStmt) parsedStmt).checkPlanColumnMatch(columns);
    }

    // Output the best plan debug info
    assembler.finalizeBestCostPlan();

    // reset all the plan node ids for a given plan
    // this makes the ids deterministic
    bestPlan.resetPlanNodeIds(1);

    // split up the plan everywhere we see send/recieve into multiple plan fragments
    List<AbstractPlanNode> receives =
        bestPlan.rootPlanGraph.findAllNodesOfClass(AbstractReceivePlanNode.class);
    if (receives.size() > 1) {
      // Have too many receive node for two fragment plan limit
      m_recentErrorMsg =
          "This join of multiple partitioned tables is too complex. "
              + "Consider simplifying its subqueries: "
              + getOriginalSql();
      return null;
    }

    /*/ enable for debug ...
    if (receives.size() > 1) {
        System.out.println(plan.rootPlanGraph.toExplainPlanString());
    }
    // ... enable for debug */
    if (receives.size() == 1) {
      AbstractReceivePlanNode recvNode = (AbstractReceivePlanNode) receives.get(0);
      fragmentize(bestPlan, recvNode);
    }

    return bestPlan;
  }