use of org.voltdb.plannodes.SchemaColumn in project voltdb by VoltDB.
the class TupleValueExpression method setColumnIndexUsingSchema.
/**
* Given an input schema, resolve this TVE expression.
*/
public int setColumnIndexUsingSchema(NodeSchema inputSchema) {
int index = inputSchema.getIndexOfTve(this);
if (index < 0) {
//* enable to debug*/ System.out.println("DEBUG: setColumnIndex candidates: " + inputSchema);
return index;
}
setColumnIndex(index);
if (getValueType() == null) {
// In case of sub-queries the TVE may not have its
// value type and size resolved yet. Try to resolve it now
SchemaColumn inputColumn = inputSchema.getColumns().get(index);
setTypeSizeAndInBytes(inputColumn);
}
return index;
}
use of org.voltdb.plannodes.SchemaColumn in project voltdb by VoltDB.
the class InsertSubPlanAssembler method nextPlan.
@Override
AbstractPlanNode nextPlan() {
if (m_bestAndOnlyPlanWasGenerated) {
return null;
}
// We may generate a few different plans for the subquery, but by the time
// we get here, we'll generate only one plan for the INSERT statement itself.
// Mostly this method exists to check that we can find a valid partitioning
// for the statement.
m_bestAndOnlyPlanWasGenerated = true;
ParsedInsertStmt insertStmt = (ParsedInsertStmt) m_parsedStmt;
Table targetTable = insertStmt.m_tableList.get(0);
targetTable.getTypeName();
StmtSubqueryScan subquery = insertStmt.getSubqueryScan();
boolean subqueryIsMultiFragment = subquery.getBestCostPlan().rootPlanGraph.hasAnyNodeOfType(PlanNodeType.SEND);
if (targetTable.getIsreplicated()) {
// setUpForNewPlans already validates this
assert (!m_partitioning.wasSpecifiedAsSingle() && !m_partitioning.isInferredSingle());
// Cannot access any partitioned tables in subquery for replicated table
if (!subquery.getIsReplicated()) {
throw new PlanningErrorException("Subquery in " + getSqlType() + " INTO ... SELECT statement may not access " + "partitioned data for insertion into replicated table " + targetTable.getTypeName() + ".");
}
} else if (!m_partitioning.wasSpecifiedAsSingle()) {
if (subqueryIsMultiFragment) {
// What is the appropriate level of detail for this message?
m_recentErrorMsg = getSqlType() + " INTO ... SELECT statement subquery is too complex. " + "Please either simplify the subquery or use a SELECT followed by an INSERT.";
return null;
}
Column partitioningCol = targetTable.getPartitioncolumn();
if (partitioningCol == null) {
assert (m_targetIsExportTable);
m_recentErrorMsg = "The target table for an INSERT INTO ... SELECT statement is an " + "stream with no partitioning column defined. " + "This is not currently supported. Please define a " + "partitioning column for this stream to use it with INSERT INTO ... SELECT.";
return null;
}
List<StmtTableScan> tables = new ArrayList<>();
StmtTargetTableScan stmtTargetTableScan = new StmtTargetTableScan(targetTable);
tables.add(stmtTargetTableScan);
tables.add(subquery);
// Create value equivalence between the partitioning column of the target table
// and the corresponding expression produced by the subquery.
HashMap<AbstractExpression, Set<AbstractExpression>> valueEquivalence = new HashMap<>();
int i = 0;
boolean setEquivalenceForPartitioningCol = false;
for (Column col : insertStmt.m_columns.keySet()) {
if (partitioningCol.compareTo(col) == 0) {
List<SchemaColumn> partitioningColumns = stmtTargetTableScan.getPartitioningColumns();
assert (partitioningColumns.size() == 1);
AbstractExpression targetPartitionColExpr = partitioningColumns.get(0).getExpression();
TupleValueExpression selectedExpr = subquery.getOutputExpression(i);
assert (!valueEquivalence.containsKey(targetPartitionColExpr));
assert (!valueEquivalence.containsKey(selectedExpr));
Set<AbstractExpression> equivSet = new HashSet<>();
equivSet.add(targetPartitionColExpr);
equivSet.add(selectedExpr);
valueEquivalence.put(targetPartitionColExpr, equivSet);
valueEquivalence.put(selectedExpr, equivSet);
setEquivalenceForPartitioningCol = true;
}
++i;
}
if (!setEquivalenceForPartitioningCol) {
// partitioning column of target table is not being set from value produced by the subquery.
m_recentErrorMsg = "Partitioning column must be assigned a value " + "produced by the subquery in an " + getSqlType() + " INTO ... SELECT statement.";
return null;
}
m_partitioning.analyzeForMultiPartitionAccess(tables, valueEquivalence);
if (!m_partitioning.isJoinValid()) {
m_recentErrorMsg = "Partitioning could not be determined for " + getSqlType() + " INTO ... SELECT statement. " + "Please ensure that statement does not attempt to copy row data from one partition to another, " + "which is unsupported.";
return null;
}
}
return subquery.getBestCostPlan().rootPlanGraph;
}
use of org.voltdb.plannodes.SchemaColumn in project voltdb by VoltDB.
the class StatementPartitioning method analyzeForMultiPartitionAccess.
/**
* Given the query's list of tables and its collection(s) of equality-filtered columns and their equivalents,
* determine whether all joins involving partitioned tables can be executed locally on a single partition.
* This is only the case when they include equality comparisons between partition key columns.
* VoltDB will reject joins of multiple partitioned tables unless all their partition keys are
* constrained to be equal to each other.
* Example: select * from T1, T2 where T1.ID = T2.ID
* Additionally, in this case, there may be a constant equality filter on any of the columns,
* which we want to extract as our SP partitioning parameter.
*
* @param tableAliasList The tables.
* @param valueEquivalence Their column equality filters
* @return the number of independently partitioned tables
* -- partitioned tables that aren't joined or filtered by the same value.
* The caller can raise an alarm if there is more than one.
*/
public void analyzeForMultiPartitionAccess(Collection<StmtTableScan> scans, HashMap<AbstractExpression, Set<AbstractExpression>> valueEquivalence) {
//* enable to debug */ System.out.println("DEBUG: analyze4MPAccess w/ scans:" + scans.size() + " filters:" + valueEquivalence.size());
TupleValueExpression tokenPartitionKey = null;
Set<Set<AbstractExpression>> eqSets = new HashSet<Set<AbstractExpression>>();
int unfilteredPartitionKeyCount = 0;
// reset this flag to forget the last result of the multiple partition access path.
// AdHoc with parameters will call this function at least two times
// By default this flag should be true.
setJoinValid(true);
setJoinInvalidReason(null);
boolean subqueryHasReceiveNode = false;
boolean hasPartitionedTableJoin = false;
// Iterate over the tables to collect partition columns.
for (StmtTableScan tableScan : scans) {
// Replicated tables don't need filter coverage.
if (tableScan.getIsReplicated()) {
continue;
}
// The partition column can be null in an obscure edge case.
// The table is declared non-replicated yet specifies no partitioning column.
// This can occur legitimately when views based on partitioned tables neglect to group by the partition column.
// The interpretation of this edge case is that the table has "randomly distributed data".
// In such a case, the table is valid for use by MP queries only and can only be joined with replicated tables
// because it has no recognized partitioning join key.
List<SchemaColumn> columnsNeedingCoverage = tableScan.getPartitioningColumns();
if (tableScan instanceof StmtSubqueryScan) {
StmtSubqueryScan subScan = (StmtSubqueryScan) tableScan;
subScan.promoteSinglePartitionInfo(valueEquivalence, eqSets);
CompiledPlan subqueryPlan = subScan.getBestCostPlan();
if ((!subScan.canRunInOneFragment()) || ((subqueryPlan != null) && subqueryPlan.rootPlanGraph.hasAnyNodeOfClass(AbstractReceivePlanNode.class))) {
if (subqueryHasReceiveNode) {
// Has found another subquery with receive node on the same level
// Not going to support this kind of subquery join with 2 fragment plan.
setJoinValid(false);
setJoinInvalidReason("This multipartition query is not plannable. " + "It has a subquery which cannot be single partition.");
// Still needs to count the independent partition tables
break;
}
subqueryHasReceiveNode = true;
if (subScan.isTableAggregate()) {
// Any process based on this subquery should require 1 fragment only.
continue;
}
} else {
// this subquery partition table without receive node
hasPartitionedTableJoin = true;
}
} else {
// This table is a partition table
hasPartitionedTableJoin = true;
}
boolean unfiltered = true;
for (AbstractExpression candidateColumn : valueEquivalence.keySet()) {
if (!(candidateColumn instanceof TupleValueExpression)) {
continue;
}
TupleValueExpression candidatePartitionKey = (TupleValueExpression) candidateColumn;
if (!canCoverPartitioningColumn(candidatePartitionKey, columnsNeedingCoverage)) {
continue;
}
unfiltered = false;
if (tokenPartitionKey == null) {
tokenPartitionKey = candidatePartitionKey;
}
eqSets.add(valueEquivalence.get(candidatePartitionKey));
}
if (unfiltered) {
++unfilteredPartitionKeyCount;
}
}
// end for each table StmtTableScan in the collection
m_countOfIndependentlyPartitionedTables = eqSets.size() + unfilteredPartitionKeyCount;
//* enable to debug */ System.out.println("DEBUG: analyze4MPAccess found: " + m_countOfIndependentlyPartitionedTables + " = " + eqSets.size() + " + " + unfilteredPartitionKeyCount);
if (m_countOfIndependentlyPartitionedTables > 1) {
setJoinValid(false);
setJoinInvalidReason("This query is not plannable. " + "The planner cannot guarantee that all rows would be in a single partition.");
}
// on outer level. Not going to support this kind of join.
if (subqueryHasReceiveNode && hasPartitionedTableJoin) {
setJoinValid(false);
setJoinInvalidReason("This query is not plannable. It has a subquery which needs cross-partition access.");
}
if ((unfilteredPartitionKeyCount == 0) && (eqSets.size() == 1)) {
for (Set<AbstractExpression> partitioningValues : eqSets) {
for (AbstractExpression constExpr : partitioningValues) {
if (constExpr instanceof TupleValueExpression) {
continue;
}
VoltType valueType = tokenPartitionKey.getValueType();
addPartitioningExpression(tokenPartitionKey.getTableName() + '.' + tokenPartitionKey.getColumnName(), constExpr, valueType);
// Only need one constant value.
break;
}
}
}
}
use of org.voltdb.plannodes.SchemaColumn in project voltdb by VoltDB.
the class StatementPartitioning method canCoverPartitioningColumn.
private static boolean canCoverPartitioningColumn(TupleValueExpression candidatePartitionKey, List<SchemaColumn> columnsNeedingCoverage) {
if (columnsNeedingCoverage == null)
return false;
for (SchemaColumn col : columnsNeedingCoverage) {
String partitionedTableAlias = col.getTableAlias();
String columnNeedingCoverage = col.getColumnAlias();
assert (candidatePartitionKey.getTableAlias() != null);
if (!candidatePartitionKey.getTableAlias().equals(partitionedTableAlias)) {
continue;
}
String candidateColumnName = candidatePartitionKey.getColumnName();
if (!candidateColumnName.equals(columnNeedingCoverage)) {
continue;
}
// Maybe need more checkings
return true;
}
return false;
}
use of org.voltdb.plannodes.SchemaColumn in project voltdb by VoltDB.
the class TestSelfJoins method testSelfJoin.
public void testSelfJoin() {
AbstractPlanNode pn = compile("select * FROM R1 A JOIN R1 B ON A.C = B.C WHERE B.A > 0 AND A.C < 3");
pn = pn.getChild(0).getChild(0);
assertTrue(pn instanceof NestLoopPlanNode);
assertEquals(4, pn.getOutputSchema().getColumns().size());
assertEquals(2, pn.getChildCount());
AbstractPlanNode c = pn.getChild(0);
assertTrue(c instanceof SeqScanPlanNode);
SeqScanPlanNode ss = (SeqScanPlanNode) c;
assertEquals("R1", ss.getTargetTableName());
assertEquals("A", ss.getTargetTableAlias());
assertEquals(ExpressionType.COMPARE_LESSTHAN, ss.getPredicate().getExpressionType());
c = pn.getChild(1);
assertTrue(c instanceof SeqScanPlanNode);
ss = (SeqScanPlanNode) c;
assertEquals("R1", ss.getTargetTableName());
assertEquals("B", ss.getTargetTableAlias());
assertEquals(ExpressionType.COMPARE_GREATERTHAN, ss.getPredicate().getExpressionType());
pn = compile("select * FROM R1 JOIN R1 B ON R1.C = B.C");
pn = pn.getChild(0).getChild(0);
assertTrue(pn instanceof NestLoopPlanNode);
assertEquals(4, pn.getOutputSchema().getColumns().size());
assertEquals(2, pn.getChildCount());
c = pn.getChild(0);
assertTrue(c instanceof SeqScanPlanNode);
ss = (SeqScanPlanNode) c;
assertEquals("R1", ss.getTargetTableName());
assertEquals("R1", ss.getTargetTableAlias());
c = pn.getChild(1);
assertTrue(c instanceof SeqScanPlanNode);
ss = (SeqScanPlanNode) c;
assertEquals("R1", ss.getTargetTableName());
assertEquals("B", ss.getTargetTableAlias());
pn = compile("select A.A, A.C, B.A, B.C FROM R1 A JOIN R1 B ON A.C = B.C");
pn = pn.getChild(0).getChild(0);
assertTrue(pn instanceof NestLoopPlanNode);
assertEquals(4, pn.getOutputSchema().getColumns().size());
pn = compile("select A,B.C FROM R1 A JOIN R2 B USING(A)");
pn = pn.getChild(0);
assertTrue(pn instanceof ProjectionPlanNode);
NodeSchema ns = pn.getOutputSchema();
for (SchemaColumn sc : ns.getColumns()) {
AbstractExpression e = sc.getExpression();
assertTrue(e instanceof TupleValueExpression);
TupleValueExpression tve = (TupleValueExpression) e;
assertNotSame(-1, tve.getColumnIndex());
}
}
Aggregations