/** * Miscellaneous post parse activity . * * @param sql * @param db * @param joinOrder */ @Override void postParse(String sql, String joinOrder) { for (AbstractParsedStmt selectStmt : m_children) { selectStmt.postParse(sql, joinOrder); } // these just shouldn't happen right? assert (noTableSelectionList.size() == 0); this.sql = sql; this.joinOrder = joinOrder; }
@Override void parse(VoltXMLElement stmtNode) { String type = stmtNode.attributes.get("uniontype"); // Set operation type m_unionType = UnionType.valueOf(type); assert (stmtNode.children.size() == m_children.size()); int i = 0; for (VoltXMLElement selectSQL : stmtNode.children) { AbstractParsedStmt nextSelectStmt = m_children.get(i++); nextSelectStmt.parse(selectSQL); } }
/** * Parse tables and parameters * * @param root * @param db */ @Override void parseTablesAndParams(VoltXMLElement stmtNode) { assert (stmtNode.children.size() > 1); tableList.clear(); for (VoltXMLElement childSQL : stmtNode.children) { if (childSQL.name.equalsIgnoreCase(SELECT_NODE_NAME)) { AbstractParsedStmt childStmt = new ParsedSelectStmt(this.m_paramValues, this.m_db); childStmt.parseTablesAndParams(childSQL); m_children.add(childStmt); // So far T UNION T (as well as T JOIN T) are not handled properly // by the fragmentizer. Need to give an error if any table is mentioned // in the UNION TREE more than once. if (childStmt.scanColumns != null) { for (Table table : childStmt.tableList) { String tableName = table.getTypeName(); if (m_uniqueTables.contains(tableName)) { // The table is not 'unique' across the union throw new PlanningErrorException( "Table " + tableName + " appears more than once in the union statement"); } else { m_uniqueTables.add(tableName); } } } else { throw new PlanningErrorException("Scan columns are NULL the UNION statement"); } // Add statement's tables to the consolidated list tableList.addAll(childStmt.tableList); } else if (childSQL.name.equalsIgnoreCase(UNION_NODE_NAME)) { ParsedUnionStmt childStmt = new ParsedUnionStmt(this.m_paramValues, this.m_db); // Pass already accumulated unique tables to the child union childStmt.m_uniqueTables = m_uniqueTables; childStmt.parseTablesAndParams(childSQL); m_children.add(childStmt); // Add statement's tables to the consolidated list tableList.addAll(childStmt.tableList); // Child's unique tables now contains the consolidated list m_uniqueTables = childStmt.m_uniqueTables; } else { throw new PlanningErrorException("Unexpected Element in UNION statement: " + childSQL.name); } } }
/** * @param sql * @param xmlSQL * @param db */ public static AbstractParsedStmt parse( String sql, VoltXMLElement stmtTypeElement, String[] paramValues, Database db, String joinOrder) { AbstractParsedStmt retval = null; if (stmtTypeElement == null) { System.err.println("Unexpected error parsing hsql parsed stmt xml"); throw new RuntimeException("Unexpected error parsing hsql parsed stmt xml"); } // create non-abstract instances if (stmtTypeElement.name.equalsIgnoreCase(INSERT_NODE_NAME)) { retval = new ParsedInsertStmt(paramValues, db); } else if (stmtTypeElement.name.equalsIgnoreCase(UPDATE_NODE_NAME)) { retval = new ParsedUpdateStmt(paramValues, db); } else if (stmtTypeElement.name.equalsIgnoreCase(DELETE_NODE_NAME)) { retval = new ParsedDeleteStmt(paramValues, db); } else if (stmtTypeElement.name.equalsIgnoreCase(SELECT_NODE_NAME)) { retval = new ParsedSelectStmt(paramValues, db); } else if (stmtTypeElement.name.equalsIgnoreCase(UNION_NODE_NAME)) { retval = new ParsedUnionStmt(paramValues, db); } else { throw new RuntimeException("Unexpected Element: " + stmtTypeElement.name); } // parse tables and parameters retval.parseTablesAndParams(stmtTypeElement); // parse specifics retval.parse(stmtTypeElement); // post parse action retval.postParse(sql, joinOrder); return retval; }
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; }