private static void replacePlanForUpsert(CompiledPlan plan) { plan.rootPlanGraph = replaceInsertPlanNodeWithUpsert(plan.rootPlanGraph); plan.subPlanGraph = replaceInsertPlanNodeWithUpsert(plan.subPlanGraph); if (plan.explainedPlan != null) { plan.explainedPlan = plan.explainedPlan.replace("INSERT", "UPSERT"); } }
/** * Get the best plan for the SQL statement given, assuming the given costModel. * * @return The best plan found for the SQL statement. * @throws PlanningErrorException on failure. */ public CompiledPlan plan() throws PlanningErrorException { // reset any error message m_recentErrorMsg = null; // what's going to happen next: // If a parameterized statement exists, try to make a plan with it // On success return the plan. // On failure, try the plan again without parameterization if (m_paramzInfo != null) { try { // compile the plan with new parameters CompiledPlan plan = compileFromXML(m_paramzInfo.parameterizedXmlSQL, m_paramzInfo.paramLiteralValues); if (plan != null) { if (m_isUpsert) { replacePlanForUpsert(plan); } if (plan.extractParamValues(m_paramzInfo)) { return plan; } } else { if (m_debuggingStaticModeToRetryOnError) { plan = compileFromXML(m_paramzInfo.parameterizedXmlSQL, m_paramzInfo.paramLiteralValues); } } // fall through to try replan without parameterization. } catch (Exception e) { // ignore any errors planning with parameters // fall through to re-planning without them m_hasExceptionWhenParameterized = true; // note, expect real planning errors ignored here to be thrown again below m_recentErrorMsg = null; m_partitioning.resetAnalysisState(); } } // if parameterization isn't requested or if it failed, plan here CompiledPlan plan = compileFromXML(m_xmlSQL, null); if (plan == null) { if (m_debuggingStaticModeToRetryOnError) { plan = compileFromXML(m_xmlSQL, null); } throw new PlanningErrorException(m_recentErrorMsg); } if (m_isUpsert) { replacePlanForUpsert(plan); } return plan; }
private static void fragmentize(CompiledPlan plan, AbstractReceivePlanNode recvNode) { assert (recvNode.getChildCount() == 1); AbstractPlanNode childNode = recvNode.getChild(0); assert (childNode instanceof SendPlanNode); SendPlanNode sendNode = (SendPlanNode) childNode; // disconnect the send and receive nodes sendNode.clearParents(); recvNode.clearChildren(); plan.subPlanGraph = sendNode; return; }
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; }