use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.
the class PlanAssembler method handleWindowedOperators.
/**
* Create nodes for windowed operations.
*
* @param root
* @return
*/
private AbstractPlanNode handleWindowedOperators(AbstractPlanNode root) {
// Get the windowed expression. We need to set its output
// schema from the display list.
WindowFunctionExpression winExpr = m_parsedSelect.getWindowFunctionExpressions().get(0);
assert (winExpr != null);
// This will set the output schema to contain the
// windowed schema column only. In generateOutputSchema
// we will add the input columns.
WindowFunctionPlanNode pnode = new WindowFunctionPlanNode();
pnode.setWindowFunctionExpression(winExpr);
// We always need an order by plan node, even if the sort
// is optimized away by an index. This may be turned
// into an inline order by in a MergeReceivePlanNode.
IndexUseForOrderBy scanNode = findScanNodeForWindowFunction(root);
AbstractPlanNode cnode = null;
int winfunc = (scanNode == null) ? SubPlanAssembler.NO_INDEX_USE : scanNode.getWindowFunctionUsesIndex();
// statement level order by ordering.
if ((SubPlanAssembler.STATEMENT_LEVEL_ORDER_BY_INDEX == winfunc) || (SubPlanAssembler.NO_INDEX_USE == winfunc)) {
// No index. Calculate the expression order here and stuff it into
// the order by node. Note that if we support more than one window
// function this would be the case when scanNode.getWindowFunctionUsesIndex()
// returns a window function number which is different from the number
// of winExpr.
List<AbstractExpression> partitionByExpressions = winExpr.getPartitionByExpressions();
// If the order by expression list contains a partition by expression then
// we won't have to sort by it twice. We sort by the partition by expressions
// first, and we don't care what order we sort by them. So, find the
// sort direction in the order by list and use that in the partition by
// list, and then mark that it was deleted in the order by
// list.
//
// We choose to make this dontsort rather than dosort because the
// Java default value for boolean is false, and we want to sort by
// default.
boolean[] dontsort = new boolean[winExpr.getOrderbySize()];
List<AbstractExpression> orderByExpressions = winExpr.getOrderByExpressions();
List<SortDirectionType> orderByDirections = winExpr.getOrderByDirections();
OrderByPlanNode onode = new OrderByPlanNode();
for (int idx = 0; idx < winExpr.getPartitionbySize(); ++idx) {
SortDirectionType pdir = SortDirectionType.ASC;
AbstractExpression partitionByExpression = partitionByExpressions.get(idx);
int sidx = winExpr.getSortIndexOfOrderByExpression(partitionByExpression);
if (0 <= sidx) {
pdir = orderByDirections.get(sidx);
dontsort[sidx] = true;
}
onode.addSort(partitionByExpression, pdir);
}
for (int idx = 0; idx < winExpr.getOrderbySize(); ++idx) {
if (!dontsort[idx]) {
AbstractExpression orderByExpr = orderByExpressions.get(idx);
SortDirectionType orderByDir = orderByDirections.get(idx);
onode.addSort(orderByExpr, orderByDir);
}
}
onode.addAndLinkChild(root);
cnode = onode;
} else {
assert (scanNode != null);
// inline order by node of a MergeReceive node.
assert (0 == scanNode.getWindowFunctionUsesIndex());
if (m_partitioning.requiresTwoFragments()) {
OrderByPlanNode onode = new OrderByPlanNode();
SortDirectionType dir = scanNode.getSortOrderFromIndexScan();
assert (dir != SortDirectionType.INVALID);
// This was created when the index was determined.
// We cached it in the scan node.
List<AbstractExpression> orderExprs = scanNode.getFinalExpressionOrderFromIndexScan();
assert (orderExprs != null);
for (AbstractExpression ae : orderExprs) {
onode.addSort(ae, dir);
}
// Link in the OrderByNode.
onode.addAndLinkChild(root);
cnode = onode;
} else {
// Don't create and link in the order by node.
cnode = root;
}
}
pnode.addAndLinkChild(cnode);
return pnode;
}
use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.
the class QueryPlanner method compileFromXML.
/**
* Find the best plan given the VoltXMLElement. By best here we mean the plan
* which is scored the best according to our plan metric scoring. The plan
* metric scoring takes into account join order and index use, but it does
* not take into account the output schema. Consequently, we don't compute the
* output schema for the plan nodes until after the best plan is discovered.
*
* The order here is:
* <ol>
* <li>
* Parse the VoltXMLElement to create an AbstractParsedStatement. This has
* a second effect of loading lists of join orders and access paths for planning.
* For us, and access path is a way of scanning something scannable. It's a generalization
* of the notion of scanning a table or an index.
* </li>
* <li>
* Create a PlanAssembler, and ask it for the best cost plan. This uses the
* side data created by the parser in the previous step.
* </li>
* <li>
* If the plan is read only, slap a SendPlanNode on the front. Presumably
* an insert, delete or upsert will have added the SendPlanNode into the plan node tree already.
* </li>
* <li>
* Compute the output schema. This computes the output schema for each
* node recursively, using a node specific method.
* </li>
* <li>
* Resolve the column indices. This makes sure that the indices of all
* TVEs in the output columns refer to the right input columns.
* </li>
* <li>
* Do some final cleaning up and verifying of the plan. For example,
* We renumber the nodes starting at 1.
* </li>
* </ol>
*
* @param xmlSQL
* @param paramValues
* @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 supported 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_db, m_partitioning, (PlanSelector) m_planSelector.clone());
// find the plan with minimal cost
CompiledPlan bestPlan = assembler.getBestCostPlan(parsedStmt);
// 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/receive 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;
}
use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.
the class QueryPlanner method replaceInsertPlanNodeWithUpsert.
public static AbstractPlanNode replaceInsertPlanNodeWithUpsert(AbstractPlanNode root) {
if (root == null) {
return null;
}
List<AbstractPlanNode> inserts = root.findAllNodesOfType(PlanNodeType.INSERT);
if (inserts.size() == 1) {
InsertPlanNode insertNode = (InsertPlanNode) inserts.get(0);
insertNode.setUpsert(true);
}
return root;
}
use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.
the class PlanAssembler method isValidAggregateNodeForLimitPushdown.
private static boolean isValidAggregateNodeForLimitPushdown(AbstractPlanNode aggregateNode, List<ParsedColInfo> orderBys, boolean orderByCoversAllGroupBy) {
if (aggregateNode instanceof AggregatePlanNode == false) {
return false;
}
if (aggregateNode.getParentCount() == 0) {
return false;
}
// Limitation: can only push past coordinating aggregation nodes
if (!((AggregatePlanNode) aggregateNode).m_isCoordinatingAggregator) {
return false;
}
AbstractPlanNode parent = aggregateNode.getParent(0);
AbstractPlanNode orderByNode = null;
if (parent instanceof OrderByPlanNode) {
orderByNode = parent;
} else if (parent instanceof ProjectionPlanNode && parent.getParentCount() > 0 && parent.getParent(0) instanceof OrderByPlanNode) {
// Xin really wants inline project with aggregation
orderByNode = parent.getParent(0);
}
if (orderByNode == null) {
// the limit should not be pushed down.
return false;
}
if ((!orderByCoversAllGroupBy) || isOrderByAggregationValue(orderBys)) {
return false;
}
return true;
}
use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.
the class TestColumnTrimmingPlans method testEng585Plan.
public void testEng585Plan() {
AbstractPlanNode pn = null;
pn = compile("select max(s.int2) as foo from s, t where s.s_pk = t.s_pk and t.t_pk1 = ?;");
// TODO: To actually detect ENG-585 regression, we'd have to program a check that the join is
// materializing narrow rows of fewer than 5 columns. Yeah.
System.out.println(pn.toJSONString());
}
Aggregations