m_bestAndOnlyPlanWasGenerated = true;
ParsedInsertStmt insertStmt = (ParsedInsertStmt)m_parsedStmt;
Table targetTable = insertStmt.m_tableList.get(0);
targetTable.getTypeName();
StmtSubqueryScan subquery = insertStmt.getSubqueries().get(0);
if (targetTable.getIsreplicated()) {
// must not be single-partition insert if targeting a replicated table
// 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()) {
// [assume that c1 is the partitioning column]
// INSERT INTO t1 (c1, c2, c3, ...)
// SELECT e1, e2, e3, ... FROM ...
//
// can be analyzed as if it was
//
// SELECT COUNT(*)
// FROM t1
// INNER JOIN
// (SELECT e1, e2, e3, ... FROM ...) AS insert_subquery
// ON t1.c1 = insert_subquery.e1;
//
// Build the corresponding data structures for analysis by StatementPartitioning.
if (subquery.getBestCostPlan().rootPlanGraph.hasAnyNodeOfType(PlanNodeType.SEND)) {
// 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;
}
List<StmtTableScan> tables = new ArrayList<>();
StmtTargetTableScan stmtTargetTableScan = new StmtTargetTableScan(targetTable, targetTable.getTypeName());
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;
Column partitioningCol = targetTable.getPartitioncolumn();
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;
}