}
@SuppressWarnings("nls")
private RelNode genUnionLogicalPlan(String unionalias, String leftalias, RelNode leftRel,
String rightalias, RelNode rightRel) throws SemanticException {
HiveUnionRel unionRel = null;
// 1. Get Row Resolvers, Column map for original left and right input of
// Union Rel
RowResolver leftRR = this.relToHiveRR.get(leftRel);
RowResolver rightRR = this.relToHiveRR.get(rightRel);
HashMap<String, ColumnInfo> leftmap = leftRR.getFieldMap(leftalias);
HashMap<String, ColumnInfo> rightmap = rightRR.getFieldMap(rightalias);
// 2. Validate that Union is feasible according to Hive (by using type
// info from RR)
if (leftmap.size() != rightmap.size()) {
throw new SemanticException("Schema of both sides of union should match.");
}
ASTNode tabref = qb.getAliases().isEmpty() ? null : qb.getParseInfo().getSrcForAlias(
qb.getAliases().get(0));
for (Map.Entry<String, ColumnInfo> lEntry : leftmap.entrySet()) {
String field = lEntry.getKey();
ColumnInfo lInfo = lEntry.getValue();
ColumnInfo rInfo = rightmap.get(field);
if (rInfo == null) {
throw new SemanticException(generateErrorMessage(tabref,
"Schema of both sides of union should match. " + rightalias
+ " does not have the field " + field));
}
if (lInfo == null) {
throw new SemanticException(generateErrorMessage(tabref,
"Schema of both sides of union should match. " + leftalias
+ " does not have the field " + field));
}
if (!lInfo.getInternalName().equals(rInfo.getInternalName())) {
throw new OptiqSemanticException(generateErrorMessage(tabref,
"Schema of both sides of union should match: field " + field + ":"
+ " appears on the left side of the UNION at column position: "
+ getPositionFromInternalName(lInfo.getInternalName())
+ ", and on the right side of the UNION at column position: "
+ getPositionFromInternalName(rInfo.getInternalName())
+ ". Column positions should match for a UNION"));
}
// try widening coversion, otherwise fail union
TypeInfo commonTypeInfo = FunctionRegistry.getCommonClassForUnionAll(lInfo.getType(),
rInfo.getType());
if (commonTypeInfo == null) {
throw new OptiqSemanticException(generateErrorMessage(tabref,
"Schema of both sides of union should match: Column " + field + " is of type "
+ lInfo.getType().getTypeName() + " on first table and type "
+ rInfo.getType().getTypeName() + " on second table"));
}
}
// 3. construct Union Output RR using original left & right Input
RowResolver unionoutRR = new RowResolver();
for (Map.Entry<String, ColumnInfo> lEntry : leftmap.entrySet()) {
String field = lEntry.getKey();
ColumnInfo lInfo = lEntry.getValue();
ColumnInfo rInfo = rightmap.get(field);
ColumnInfo unionColInfo = new ColumnInfo(lInfo);
unionColInfo.setTabAlias(unionalias);
unionColInfo.setType(FunctionRegistry.getCommonClassForUnionAll(lInfo.getType(),
rInfo.getType()));
unionoutRR.put(unionalias, field, unionColInfo);
}
// 4. Determine which columns requires cast on left/right input (Optiq
// requires exact types on both sides of union)
boolean leftNeedsTypeCast = false;
boolean rightNeedsTypeCast = false;
List<RexNode> leftProjs = new ArrayList<RexNode>();
List<RexNode> rightProjs = new ArrayList<RexNode>();
List<RelDataTypeField> leftRowDT = leftRel.getRowType().getFieldList();
List<RelDataTypeField> rightRowDT = rightRel.getRowType().getFieldList();
RelDataType leftFieldDT;
RelDataType rightFieldDT;
RelDataType unionFieldDT;
for (int i = 0; i < leftRowDT.size(); i++) {
leftFieldDT = leftRowDT.get(i).getType();
rightFieldDT = rightRowDT.get(i).getType();
if (!leftFieldDT.equals(rightFieldDT)) {
unionFieldDT = TypeConverter.convert(unionoutRR.getColumnInfos().get(i).getType(),
cluster.getTypeFactory());
if (!unionFieldDT.equals(leftFieldDT)) {
leftNeedsTypeCast = true;
}
leftProjs.add(cluster.getRexBuilder().ensureType(unionFieldDT,
cluster.getRexBuilder().makeInputRef(leftFieldDT, i), true));
if (!unionFieldDT.equals(rightFieldDT)) {
rightNeedsTypeCast = true;
}
rightProjs.add(cluster.getRexBuilder().ensureType(unionFieldDT,
cluster.getRexBuilder().makeInputRef(rightFieldDT, i), true));
} else {
leftProjs.add(cluster.getRexBuilder().ensureType(leftFieldDT,
cluster.getRexBuilder().makeInputRef(leftFieldDT, i), true));
rightProjs.add(cluster.getRexBuilder().ensureType(rightFieldDT,
cluster.getRexBuilder().makeInputRef(rightFieldDT, i), true));
}
}
// 5. Introduce Project Rel above original left/right inputs if cast is
// needed for type parity
RelNode unionLeftInput = leftRel;
RelNode unionRightInput = rightRel;
if (leftNeedsTypeCast) {
unionLeftInput = HiveProjectRel.create(leftRel, leftProjs, leftRel.getRowType()
.getFieldNames());
}
if (rightNeedsTypeCast) {
unionRightInput = HiveProjectRel.create(rightRel, rightProjs, rightRel.getRowType()
.getFieldNames());
}
// 6. Construct Union Rel
ImmutableList.Builder bldr = new ImmutableList.Builder<RelNode>();
bldr.add(unionLeftInput);
bldr.add(unionRightInput);
unionRel = new HiveUnionRel(cluster, TraitsUtil.getDefaultTraitSet(cluster),
bldr.build());
relToHiveRR.put(unionRel, unionoutRR);
relToHiveColNameOptiqPosMap.put(unionRel,
this.buildHiveToOptiqColumnMap(unionoutRR, unionRel));