use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.
the class TestJoinOrder method testMoreThan5TablesJoin.
public void testMoreThan5TablesJoin() {
String sql;
sql = "select * FROM T1, T2, T3, T4, T5, T6, T7";
checkJoinOrder(sql, -1);
// Try the left outer join
sql = "select * FROM T1, T2, T3, T4, T5, T6 left outer join T7 on T6.F = T7.G";
checkJoinOrder(sql, -1);
sql = "select * FROM T1, T2, T3 LEFT JOIN T4 ON T3.C = T4.D LEFT JOIN T5 ON T3.C = T5.E, T6,T7";
checkJoinOrder(sql, -1);
// Try the right outer join
sql = "select * FROM T1, T2, T3, T4, T5, T6 right outer join T7 on T6.F = T7.G";
checkJoinOrder(sql, 7);
sql = "select * FROM T1, T2, T3, T4, T5 right outer join T6 on T6.F = T5.E, T7";
checkJoinOrder(sql, 6);
sql = "select * FROM T1, T2, T3, T4 right outer join T5 on T5.E = T4.D right outer join T6 on T6.F = T5.E, T7";
checkJoinOrder(sql, 5, 6);
// Sub-queries is an interesting question to test
AbstractPlanNode pn;
sql = "select * FROM T1, T2, (select T4.D from T3 right outer join T4 on T4.D = T3.C) TM1 LEFT OUTER JOIN T5 on T5.E = TM1.D, T6, T7";
pn = compile(sql);
validJoinOrder(pn.toExplainPlanString(), "T1", "T2", "T4", "T3", "T5", "T6", "T7");
/*
NEST LOOP INNER JOIN
NEST LOOP INNER JOIN
NEST LOOP LEFT JOIN
filter by (T5.E = TM1.D)
NEST LOOP INNER JOIN
NEST LOOP INNER JOIN
SEQUENTIAL SCAN of "T1"
SEQUENTIAL SCAN of "T2"
SEQUENTIAL SCAN of "TM1"
NEST LOOP LEFT JOIN
filter by (T4.D = T3.C)
SEQUENTIAL SCAN of "T4"
SEQUENTIAL SCAN of "T3"
SEQUENTIAL SCAN of "T5"
SEQUENTIAL SCAN of "T6"
SEQUENTIAL SCAN of "T7"
*/
pn = compileSPWithJoinOrder(sql, "T1,T2,TM1,T5,T6,T7");
validJoinOrder(pn.toExplainPlanString(), "T1", "T2", "T4", "T3", "T5", "T6", "T7");
//
// Join order not the input table order
//
// Do we have a case the join order as the input table order that is invalid ?!
}
use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.
the class testPlannerTester method testLoadJoinType.
public void testLoadJoinType() throws FileNotFoundException {
AbstractPlanNode pn = null;
pn = compile("select * from l, t where l.b=t.b limit ?;");
plannerTester.setUpForTest(m_currentDir + "tests/frontend/org/voltdb/planner/testplans-plannerTester-ddl.sql", "testplans-plannerTester-ddl");
System.out.println(pn.toExplainPlanString());
System.out.println(pn.toJSONString());
plannerTester.writePlanToFile(pn, m_homeDir, "prettyJson.txt", "");
ArrayList<String> getsql = new ArrayList<>();
AbstractPlanNode pn2 = plannerTester.loadPlanFromFile(m_homeDir + "prettyJson.txt", getsql);
System.out.println(pn2.toExplainPlanString());
ArrayList<AbstractPlanNode> list1 = pn.getPlanNodeList();
ArrayList<AbstractPlanNode> list2 = pn2.getPlanNodeList();
assertTrue(list1.size() == list2.size());
for (int i = 0; i < list1.size(); i++) {
Map<PlanNodeType, AbstractPlanNode> inlineNodes1 = list1.get(i).getInlinePlanNodes();
Map<PlanNodeType, AbstractPlanNode> inlineNodes2 = list2.get(i).getInlinePlanNodes();
if (inlineNodes1 != null) {
assertTrue(inlineNodes1.size() == inlineNodes2.size());
}
}
}
use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.
the class plannerTester method configCompileSave.
private static void configCompileSave(String config, boolean isSave) throws Exception {
if (!setUp(config)) {
return;
}
int size = m_stmts.size();
for (int i = 0; i < size; i++) {
String query = m_stmts.get(i);
String joinOrder = null;
if (query.startsWith("JOIN:")) {
String[] splitLine = query.split(":");
joinOrder = splitLine[1];
query = splitLine[2];
}
// This avoids cascading "file-not-found" errors.
try {
List<AbstractPlanNode> pnList = s_singleton.compileWithJoinOrderToFragments(query, joinOrder);
AbstractPlanNode pn = pnList.get(0);
if (pnList.size() == 2) {
// multi partition query plan
assert (pnList.get(1) instanceof SendPlanNode);
if (!pn.reattachFragment(pnList.get(1))) {
System.err.println("Receive plan node not found in reattachFragment.");
}
}
writePlanToFile(pn, m_workPath, config + ".plan" + i, m_stmts.get(i));
if (isSave) {
writePlanToFile(pn, m_baselinePath, config + ".plan" + i, m_stmts.get(i));
}
} catch (PlanningErrorException ex) {
System.err.printf("Planning error, line %d: %s\n", i, ex.getMessage());
}
}
if (isSave) {
System.out.println("Baseline files generated at: " + m_baselinePath);
}
}
use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.
the class PlanAssembler method checkLimitPushDownViability.
/**
* Check if we can push the limit node down.
*
* Return a mid-plan send node, if one exists and can host a
* distributed limit node.
* There is guaranteed to be at most a single receive/send pair.
* Abort the search if a node that a "limit" can't be pushed past
* is found before its receive node.
*
* Can only push past:
* * coordinatingAggregator: a distributed aggregator
* a copy of which has already been pushed down.
* Distributing a LIMIT to just above that aggregator is correct.
* (I've got some doubts that this is correct??? --paul)
*
* * order by: if the plan requires a sort, getNextSelectPlan()
* will have already added an ORDER BY.
* A distributed LIMIT will be added above a copy
* of that ORDER BY node.
*
* * projection: these have no effect on the application of limits.
*
* @param root
* @return If we can push the limit down, the send plan node is returned.
* Otherwise null -- when the plan is single-partition when
* its "coordinator" part contains a push-blocking node type.
*/
protected AbstractPlanNode checkLimitPushDownViability(AbstractPlanNode root) {
AbstractPlanNode receiveNode = root;
List<ParsedColInfo> orderBys = m_parsedSelect.orderByColumns();
boolean orderByCoversAllGroupBy = m_parsedSelect.groupByIsAnOrderByPermutation();
while (!(receiveNode instanceof ReceivePlanNode)) {
// TODO: We might want to optimize/push down "limit" for some cases
if (!(receiveNode instanceof OrderByPlanNode) && !(receiveNode instanceof ProjectionPlanNode) && !isValidAggregateNodeForLimitPushdown(receiveNode, orderBys, orderByCoversAllGroupBy)) {
return null;
}
if (receiveNode instanceof OrderByPlanNode) {
// limit can still push down if ordered by aggregate values.
if (!m_parsedSelect.hasPartitionColumnInGroupby() && isOrderByAggregationValue(m_parsedSelect.orderByColumns())) {
return null;
}
}
// Traverse...
if (receiveNode.getChildCount() == 0) {
return null;
}
// nothing that allows pushing past has multiple inputs
assert (receiveNode.getChildCount() == 1);
receiveNode = receiveNode.getChild(0);
}
return receiveNode.getChild(0);
}
use of org.voltdb.plannodes.AbstractPlanNode in project voltdb by VoltDB.
the class PlanAssembler method getNextInsertPlan.
/**
* Get the next (only) plan for a SQL insertion. Inserts are pretty simple
* and this will only generate a single plan.
*
* @return The next (only) plan for a given insert statement, then null.
*/
private CompiledPlan getNextInsertPlan() {
// do it the right way once, then return null after that
if (m_bestAndOnlyPlanWasGenerated) {
return null;
}
m_bestAndOnlyPlanWasGenerated = true;
// figure out which table we're inserting into
assert (m_parsedInsert.m_tableList.size() == 1);
Table targetTable = m_parsedInsert.m_tableList.get(0);
StmtSubqueryScan subquery = m_parsedInsert.getSubqueryScan();
CompiledPlan retval = null;
String isContentDeterministic = null;
if (subquery != null) {
isContentDeterministic = subquery.calculateContentDeterminismMessage();
if (subquery.getBestCostPlan() == null) {
// in getBestCostPlan, above.
throw new PlanningErrorException("INSERT INTO ... SELECT subquery could not be planned: " + m_recentErrorMsg);
}
boolean targetIsExportTable = tableListIncludesExportOnly(m_parsedInsert.m_tableList);
InsertSubPlanAssembler subPlanAssembler = new InsertSubPlanAssembler(m_catalogDb, m_parsedInsert, m_partitioning, targetIsExportTable);
AbstractPlanNode subplan = subPlanAssembler.nextPlan();
if (subplan == null) {
throw new PlanningErrorException(subPlanAssembler.m_recentErrorMsg);
}
assert (m_partitioning.isJoinValid());
// Use the subquery's plan as the basis for the insert plan.
retval = subquery.getBestCostPlan();
} else {
retval = new CompiledPlan();
}
retval.setReadOnly(false);
// for the INSERT ... SELECT ... case, by analyzing the subquery.
if (m_parsedInsert.m_isUpsert) {
boolean hasPrimaryKey = false;
for (Constraint constraint : targetTable.getConstraints()) {
if (constraint.getType() != ConstraintType.PRIMARY_KEY.getValue()) {
continue;
}
hasPrimaryKey = true;
boolean targetsPrimaryKey = false;
for (ColumnRef colRef : constraint.getIndex().getColumns()) {
int primary = colRef.getColumn().getIndex();
for (Column targetCol : m_parsedInsert.m_columns.keySet()) {
if (targetCol.getIndex() == primary) {
targetsPrimaryKey = true;
break;
}
}
if (!targetsPrimaryKey) {
throw new PlanningErrorException("UPSERT on table \"" + targetTable.getTypeName() + "\" must specify a value for primary key \"" + colRef.getColumn().getTypeName() + "\".");
}
}
}
if (!hasPrimaryKey) {
throw new PlanningErrorException("UPSERT is not allowed on table \"" + targetTable.getTypeName() + "\" that has no primary key.");
}
}
CatalogMap<Column> targetTableColumns = targetTable.getColumns();
for (Column col : targetTableColumns) {
boolean needsValue = (!m_parsedInsert.m_isUpsert) && (col.getNullable() == false) && (col.getDefaulttype() == 0);
if (needsValue && !m_parsedInsert.m_columns.containsKey(col)) {
// This check could be done during parsing?
throw new PlanningErrorException("Column " + col.getName() + " has no default and is not nullable.");
}
// hint that this statement can be executed SP.
if (col.equals(m_partitioning.getPartitionColForDML()) && subquery == null) {
// When AdHoc insert-into-select is supported, we'll need to be able to infer
// partitioning of the sub-select
AbstractExpression expr = m_parsedInsert.getExpressionForPartitioning(col);
String fullColumnName = targetTable.getTypeName() + "." + col.getTypeName();
m_partitioning.addPartitioningExpression(fullColumnName, expr, expr.getValueType());
}
}
NodeSchema matSchema = null;
if (subquery == null) {
matSchema = new NodeSchema();
}
int[] fieldMap = new int[m_parsedInsert.m_columns.size()];
int i = 0;
// - For VALUES(...) insert statements, build the materialize node's schema
for (Map.Entry<Column, AbstractExpression> e : m_parsedInsert.m_columns.entrySet()) {
Column col = e.getKey();
fieldMap[i] = col.getIndex();
if (matSchema != null) {
AbstractExpression valExpr = e.getValue();
valExpr.setInBytes(col.getInbytes());
// Patch over any mismatched expressions with an explicit cast.
// Most impossible-to-cast type combinations should have already been caught by the
// parser, but there are also runtime checks in the casting code
// -- such as for out of range values.
valExpr = castExprIfNeeded(valExpr, col);
matSchema.addColumn(AbstractParsedStmt.TEMP_TABLE_NAME, AbstractParsedStmt.TEMP_TABLE_NAME, col.getTypeName(), col.getTypeName(), valExpr);
}
i++;
}
// the root of the insert plan may be an InsertPlanNode, or
// it may be a scan plan node. We may do an inline InsertPlanNode
// as well.
InsertPlanNode insertNode = new InsertPlanNode();
insertNode.setTargetTableName(targetTable.getTypeName());
if (subquery != null) {
insertNode.setSourceIsPartitioned(!subquery.getIsReplicated());
}
// The field map tells the insert node
// where to put values produced by child into the row to be inserted.
insertNode.setFieldMap(fieldMap);
AbstractPlanNode root = insertNode;
if (matSchema != null) {
MaterializePlanNode matNode = new MaterializePlanNode(matSchema);
// connect the insert and the materialize nodes together
insertNode.addAndLinkChild(matNode);
retval.statementGuaranteesDeterminism(false, true, isContentDeterministic);
} else {
ScanPlanNodeWithInlineInsert planNode = (retval.rootPlanGraph instanceof ScanPlanNodeWithInlineInsert) ? ((ScanPlanNodeWithInlineInsert) retval.rootPlanGraph) : null;
// Inline upsert might be possible, but not now.
if (planNode != null && (!m_parsedInsert.m_isUpsert) && (!planNode.hasInlineAggregateNode())) {
planNode.addInlinePlanNode(insertNode);
root = planNode.getAbstractNode();
} else {
// Otherwise just make it out-of-line.
insertNode.addAndLinkChild(retval.rootPlanGraph);
}
}
if (m_partitioning.wasSpecifiedAsSingle() || m_partitioning.isInferredSingle()) {
insertNode.setMultiPartition(false);
retval.rootPlanGraph = root;
return retval;
}
insertNode.setMultiPartition(true);
// Add a compensating sum of modified tuple counts or a limit 1
// AND a send on top of a union-like receive node.
boolean isReplicated = targetTable.getIsreplicated();
retval.rootPlanGraph = addCoordinatorToDMLNode(root, isReplicated);
return retval;
}
Aggregations