use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rel.core.JoinRelType in project hive by apache.
the class ASTConverter method convertSource.
private QueryBlockInfo convertSource(RelNode r) throws CalciteSemanticException {
Schema s = null;
ASTNode ast = null;
if (r instanceof TableScan) {
TableScan f = (TableScan) r;
s = new Schema(f);
ast = ASTBuilder.table(f);
planMapper.link(ast, f);
} else if (r instanceof HiveJdbcConverter) {
HiveJdbcConverter f = (HiveJdbcConverter) r;
s = new Schema(f);
ast = ASTBuilder.table(f);
} else if (r instanceof DruidQuery) {
DruidQuery f = (DruidQuery) r;
s = new Schema(f);
ast = ASTBuilder.table(f);
} else if (r instanceof Join) {
Join join = (Join) r;
QueryBlockInfo left = convertSource(join.getLeft());
QueryBlockInfo right = convertSource(join.getRight());
s = new Schema(left.schema, right.schema);
ASTNode cond = join.getCondition().accept(new RexVisitor(s, false, r.getCluster().getRexBuilder()));
boolean semiJoin = join.isSemiJoin() || join.getJoinType() == JoinRelType.ANTI;
if (join.getRight() instanceof Join && !semiJoin) {
// should not be done for semijoin since it will change the semantics
// Invert join inputs; this is done because otherwise the SemanticAnalyzer
// methods to merge joins will not kick in
JoinRelType type;
if (join.getJoinType() == JoinRelType.LEFT) {
type = JoinRelType.RIGHT;
} else if (join.getJoinType() == JoinRelType.RIGHT) {
type = JoinRelType.LEFT;
} else {
type = join.getJoinType();
}
ast = ASTBuilder.join(right.ast, left.ast, type, cond);
addPkFkInfoToAST(ast, join, true);
} else {
ast = ASTBuilder.join(left.ast, right.ast, join.getJoinType(), cond);
addPkFkInfoToAST(ast, join, false);
}
if (semiJoin) {
s = left.schema;
}
} else if (r instanceof Union) {
Union u = ((Union) r);
ASTNode left = new ASTConverter(((Union) r).getInput(0), this.derivedTableCount, planMapper).convert();
for (int ind = 1; ind < u.getInputs().size(); ind++) {
left = getUnionAllAST(left, new ASTConverter(((Union) r).getInput(ind), this.derivedTableCount, planMapper).convert());
String sqAlias = nextAlias();
ast = ASTBuilder.subQuery(left, sqAlias);
s = new Schema((Union) r, sqAlias);
}
} else {
ASTConverter src = new ASTConverter(r, this.derivedTableCount, planMapper);
ASTNode srcAST = src.convert();
String sqAlias = nextAlias();
s = src.getRowSchema(sqAlias);
ast = ASTBuilder.subQuery(srcAST, sqAlias);
}
return new QueryBlockInfo(s, ast);
}
use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rel.core.JoinRelType in project hive by apache.
the class HiveJoinConstraintsRule method onMatch.
@Override
public void onMatch(RelOptRuleCall call) {
final Project project = call.rel(0);
final RexBuilder rexBuilder = project.getCluster().getRexBuilder();
List<RexNode> topProjExprs = project.getProjects();
Join join = call.rel(1);
final JoinRelType joinType = join.getJoinType();
final RelNode leftInput = join.getLeft();
final RelNode rightInput = join.getRight();
final RexNode cond = join.getCondition();
// TODO:https://issues.apache.org/jira/browse/HIVE-23920
if (joinType == JoinRelType.ANTI) {
return;
}
// 1) If it is an inner, check whether project only uses columns from one side.
// That side will need to be the FK side.
// If it is a left outer, left will be the FK side.
// If it is a right outer, right will be the FK side.
final RelNode fkInput;
final RelNode nonFkInput;
final ImmutableBitSet topRefs = RelOptUtil.InputFinder.bits(topProjExprs, null);
final ImmutableBitSet leftBits = ImmutableBitSet.range(leftInput.getRowType().getFieldCount());
final ImmutableBitSet rightBits = ImmutableBitSet.range(leftInput.getRowType().getFieldCount(), join.getRowType().getFieldCount());
// These boolean values represent corresponding left, right input which is potential FK
boolean leftInputPotentialFK = topRefs.intersects(leftBits);
boolean rightInputPotentialFK = topRefs.intersects(rightBits);
if (leftInputPotentialFK && rightInputPotentialFK && (joinType == JoinRelType.INNER || joinType == JoinRelType.SEMI)) {
// Both inputs are referenced. Before making a decision, try to swap
// references in join condition if it is an inner join, i.e. if a join
// condition column is referenced above the join, then we can just
// reference the column from the other side.
// For example, given two relations R(a1,a2), S(b1) :
// SELECT a2, b1 FROM R, S ON R.a1=R.b1 =>
// SELECT a2, a1 FROM R, S ON R.a1=R.b1
int joinFieldCount = join.getRowType().getFieldCount();
Mapping mappingLR = Mappings.create(MappingType.PARTIAL_FUNCTION, joinFieldCount, joinFieldCount);
Mapping mappingRL = Mappings.create(MappingType.PARTIAL_FUNCTION, joinFieldCount, joinFieldCount);
for (RexNode conj : RelOptUtil.conjunctions(cond)) {
if (!conj.isA(SqlKind.EQUALS)) {
continue;
}
RexCall eq = (RexCall) conj;
RexNode op1 = eq.getOperands().get(0);
RexNode op2 = eq.getOperands().get(1);
if (op1 instanceof RexInputRef && op2 instanceof RexInputRef) {
// Check references
int ref1 = ((RexInputRef) op1).getIndex();
int ref2 = ((RexInputRef) op2).getIndex();
int leftRef = -1;
int rightRef = -1;
if (leftBits.get(ref1) && rightBits.get(ref2)) {
leftRef = ref1;
rightRef = ref2;
} else if (rightBits.get(ref1) && leftBits.get(ref2)) {
leftRef = ref2;
rightRef = ref1;
}
if (leftRef != -1 && rightRef != -1) {
// as it is useless
if (mappingLR.getTargetOpt(leftRef) == -1) {
mappingLR.set(leftRef, rightRef);
}
if (mappingRL.getTargetOpt(rightRef) == -1) {
mappingRL.set(rightRef, leftRef);
}
}
}
}
if (mappingLR.size() != 0) {
// First insert missing elements into the mapping as identity mappings
for (int i = 0; i < joinFieldCount; i++) {
if (mappingLR.getTargetOpt(i) == -1) {
mappingLR.set(i, i);
}
if (mappingRL.getTargetOpt(i) == -1) {
mappingRL.set(i, i);
}
}
// Then, we start by trying to reference only left side in top projections
List<RexNode> swappedTopProjExprs = topProjExprs.stream().map(projExpr -> projExpr.accept(new RexPermuteInputsShuttle(mappingRL, call.rel(1)))).collect(Collectors.toList());
rightInputPotentialFK = RelOptUtil.InputFinder.bits(swappedTopProjExprs, null).intersects(rightBits);
if (!rightInputPotentialFK) {
topProjExprs = swappedTopProjExprs;
} else {
// If it did not work, we try to reference only right side in top projections
swappedTopProjExprs = topProjExprs.stream().map(projExpr -> projExpr.accept(new RexPermuteInputsShuttle(mappingLR, call.rel(1)))).collect(Collectors.toList());
leftInputPotentialFK = RelOptUtil.InputFinder.bits(swappedTopProjExprs, null).intersects(leftBits);
if (!leftInputPotentialFK) {
topProjExprs = swappedTopProjExprs;
}
}
}
} else if (!leftInputPotentialFK && !rightInputPotentialFK) {
// TODO: There are no references in the project operator above.
// In this case, we should probably do two passes, one for
// left as FK and one for right as FK, although it may be expensive.
// Currently we only assume left as FK
leftInputPotentialFK = true;
}
final Mode mode;
switch(joinType) {
case SEMI:
case INNER:
// case ANTI: //TODO:https://issues.apache.org/jira/browse/HIVE-23920
if (leftInputPotentialFK && rightInputPotentialFK) {
// and there is nothing to transform
return;
}
fkInput = leftInputPotentialFK ? leftInput : rightInput;
nonFkInput = leftInputPotentialFK ? rightInput : leftInput;
mode = Mode.REMOVE;
break;
case LEFT:
fkInput = leftInput;
nonFkInput = rightInput;
mode = leftInputPotentialFK && !rightInputPotentialFK ? Mode.REMOVE : Mode.TRANSFORM;
break;
case RIGHT:
fkInput = rightInput;
nonFkInput = leftInput;
mode = !leftInputPotentialFK && rightInputPotentialFK ? Mode.REMOVE : Mode.TRANSFORM;
break;
default:
// Other type, bail out
return;
}
// 2) Check whether this join can be rewritten or removed
RewritablePKFKJoinInfo r = HiveRelOptUtil.isRewritablePKFKJoin(join, fkInput, nonFkInput, call.getMetadataQuery());
// 3) If it is the only condition, we can trigger the rewriting
if (r.rewritable) {
rewrite(mode, fkInput, nonFkInput, join, topProjExprs, call, project, r.nullableNodes);
} else {
// Possibly this could be enhanced to take other join type into consideration.
if (joinType != JoinRelType.INNER) {
return;
}
// first swap fk and non-fk input and see if we can rewrite them
RewritablePKFKJoinInfo fkRemoval = HiveRelOptUtil.isRewritablePKFKJoin(join, nonFkInput, fkInput, call.getMetadataQuery());
if (fkRemoval.rewritable) {
// we have established that nonFkInput is FK, and fkInput is PK
// and there is no row filtering on FK side
// check that FK side join column is distinct (i.e. have a group by)
ImmutableBitSet fkSideBitSet;
if (nonFkInput == leftInput) {
fkSideBitSet = leftBits;
} else {
fkSideBitSet = rightBits;
}
ImmutableBitSet.Builder fkJoinColBuilder = ImmutableBitSet.builder();
for (RexNode conj : RelOptUtil.conjunctions(cond)) {
if (!conj.isA(SqlKind.EQUALS)) {
return;
}
RexCall eq = (RexCall) conj;
RexNode op1 = eq.getOperands().get(0);
RexNode op2 = eq.getOperands().get(1);
if (op1 instanceof RexInputRef && op2 instanceof RexInputRef) {
// Check references
int ref1 = ((RexInputRef) op1).getIndex();
int ref2 = ((RexInputRef) op2).getIndex();
int leftRef = -1;
int rightRef = -1;
if (fkSideBitSet.get(ref1)) {
// check that join columns are not nullable
if (op1.getType().isNullable()) {
return;
}
fkJoinColBuilder.set(fkSideBitSet.indexOf(ref1));
} else {
if (op2.getType().isNullable()) {
return;
}
fkJoinColBuilder.set(fkSideBitSet.indexOf(ref2));
}
}
}
if (!call.getMetadataQuery().areColumnsUnique(nonFkInput, fkJoinColBuilder.build())) {
return;
}
// all conditions are met, therefore we can perform rewrite to remove fk side
rewrite(mode, fkInput, nonFkInput, join, topProjExprs, call, project, fkRemoval.nullableNodes);
}
}
}
use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rel.core.JoinRelType in project hive by apache.
the class HiveFilterJoinRule method perform.
/**
* Perform is duplicated from parent class to be able to call the modified
* classify filters. The modified classify method can push filter conditions
* that refer only to the SJ right input to the corresponding input (the
* fix is in the number of fields for the join, which is inferred from the
* join in the original method rather than the concatenation of the join
* inputs).
* TODO: Remove this method once {@link RelOptUtil#classifyFilters} is fixed.
*/
protected void perform(RelOptRuleCall call, Filter filter, Join join) {
final List<RexNode> joinFilters = RelOptUtil.conjunctions(join.getCondition());
final List<RexNode> origJoinFilters = ImmutableList.copyOf(joinFilters);
// again on the new cartesian product joinRel.
if (filter == null && joinFilters.isEmpty()) {
return;
}
final List<RexNode> aboveFilters = filter != null ? getConjunctions(filter) : new ArrayList<>();
final ImmutableList<RexNode> origAboveFilters = ImmutableList.copyOf(aboveFilters);
// Simplify Outer Joins
JoinRelType joinType = join.getJoinType();
if (!origAboveFilters.isEmpty() && join.getJoinType() != JoinRelType.INNER) {
joinType = RelOptUtil.simplifyJoin(join, origAboveFilters, joinType);
}
final List<RexNode> leftFilters = new ArrayList<>();
final List<RexNode> rightFilters = new ArrayList<>();
// TODO - add logic to derive additional filters. E.g., from
// (t1.a = 1 AND t2.a = 2) OR (t1.b = 3 AND t2.b = 4), you can
// derive table filters:
// (t1.a = 1 OR t1.b = 3)
// (t2.a = 2 OR t2.b = 4)
// Try to push down above filters. These are typically where clause
// filters. They can be pushed down if they are not on the NULL
// generating side.
boolean filterPushed = classifyFilters(join, aboveFilters, joinType == JoinRelType.INNER || joinType == JoinRelType.SEMI, joinType == JoinRelType.INNER || joinType == JoinRelType.LEFT || joinType == JoinRelType.SEMI || joinType == JoinRelType.ANTI, joinType == JoinRelType.INNER || joinType == JoinRelType.RIGHT, joinFilters, leftFilters, rightFilters);
// Move join filters up if needed
validateJoinFilters(aboveFilters, joinFilters, join, joinType);
// If no filter got pushed after validate, reset filterPushed flag
if (leftFilters.isEmpty() && rightFilters.isEmpty() && joinFilters.size() == origJoinFilters.size()) {
if (Sets.newHashSet(joinFilters).equals(Sets.newHashSet(origJoinFilters))) {
filterPushed = false;
}
}
// into for the same reason.
if (joinType != JoinRelType.ANTI && classifyFilters(join, joinFilters, false, joinType == JoinRelType.INNER || joinType == JoinRelType.RIGHT || joinType == JoinRelType.SEMI, joinType == JoinRelType.INNER || joinType == JoinRelType.LEFT || joinType == JoinRelType.SEMI, joinFilters, leftFilters, rightFilters)) {
filterPushed = true;
}
// then this rule is a no-op
if ((!filterPushed && joinType == join.getJoinType()) || (joinFilters.isEmpty() && leftFilters.isEmpty() && rightFilters.isEmpty())) {
return;
}
// create Filters on top of the children if any filters were
// pushed to them
final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
final RelBuilder relBuilder = call.builder();
final RelNode leftRel = relBuilder.push(join.getLeft()).filter(leftFilters).build();
final RelNode rightRel = relBuilder.push(join.getRight()).filter(rightFilters).build();
// create the new join node referencing the new children and
// containing its new join filters (if there are any)
final ImmutableList<RelDataType> fieldTypes = ImmutableList.<RelDataType>builder().addAll(RelOptUtil.getFieldTypeList(leftRel.getRowType())).addAll(RelOptUtil.getFieldTypeList(rightRel.getRowType())).build();
final RexNode joinFilter = RexUtil.composeConjunction(rexBuilder, RexUtil.fixUp(rexBuilder, joinFilters, fieldTypes));
// then this rule is a no-op
if (joinFilter.isAlwaysTrue() && leftFilters.isEmpty() && rightFilters.isEmpty() && joinType == join.getJoinType()) {
return;
}
RelNode newJoinRel = join.copy(join.getTraitSet(), joinFilter, leftRel, rightRel, joinType, join.isSemiJoinDone());
call.getPlanner().onCopy(join, newJoinRel);
if (!leftFilters.isEmpty()) {
call.getPlanner().onCopy(filter, leftRel);
}
if (!rightFilters.isEmpty()) {
call.getPlanner().onCopy(filter, rightRel);
}
relBuilder.push(newJoinRel);
// Create a project on top of the join if some of the columns have become
// NOT NULL due to the join-type getting stricter.
relBuilder.convert(join.getRowType(), false);
// create a FilterRel on top of the join if needed
relBuilder.filter(RexUtil.fixUp(rexBuilder, aboveFilters, RelOptUtil.getFieldTypeList(relBuilder.peek().getRowType())));
call.transformTo(relBuilder.build());
}
use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rel.core.JoinRelType in project hive by apache.
the class JoinVisitor method genJoin.
private JoinOperator genJoin(RelNode join, ExprNodeDesc[][] joinExpressions, List<List<ExprNodeDesc>> filterExpressions, List<Operator<?>> children, String[] baseSrc, String tabAlias) throws SemanticException {
// 1. Extract join type
JoinCondDesc[] joinCondns;
boolean semiJoin;
boolean noOuterJoin;
if (join instanceof HiveMultiJoin) {
HiveMultiJoin hmj = (HiveMultiJoin) join;
joinCondns = new JoinCondDesc[hmj.getJoinInputs().size()];
for (int i = 0; i < hmj.getJoinInputs().size(); i++) {
joinCondns[i] = new JoinCondDesc(new JoinCond(hmj.getJoinInputs().get(i).left, hmj.getJoinInputs().get(i).right, transformJoinType(hmj.getJoinTypes().get(i))));
}
semiJoin = false;
noOuterJoin = !hmj.isOuterJoin();
} else {
joinCondns = new JoinCondDesc[1];
JoinRelType joinRelType = JoinRelType.INNER;
if (join instanceof Join) {
joinRelType = ((Join) join).getJoinType();
}
JoinType joinType;
switch(joinRelType) {
case SEMI:
joinType = JoinType.LEFTSEMI;
semiJoin = true;
break;
case ANTI:
joinType = JoinType.ANTI;
semiJoin = true;
break;
default:
assert join instanceof Join;
joinType = transformJoinType(((Join) join).getJoinType());
semiJoin = false;
}
joinCondns[0] = new JoinCondDesc(new JoinCond(0, 1, joinType));
noOuterJoin = joinType != JoinType.FULLOUTER && joinType != JoinType.LEFTOUTER && joinType != JoinType.RIGHTOUTER;
}
// 2. We create the join aux structures
ArrayList<ColumnInfo> outputColumns = new ArrayList<ColumnInfo>();
ArrayList<String> outputColumnNames = new ArrayList<String>(join.getRowType().getFieldNames());
Operator<?>[] childOps = new Operator[children.size()];
Map<String, Byte> reversedExprs = new HashMap<String, Byte>();
Map<Byte, List<ExprNodeDesc>> exprMap = new HashMap<Byte, List<ExprNodeDesc>>();
Map<Byte, List<ExprNodeDesc>> filters = new HashMap<Byte, List<ExprNodeDesc>>();
Map<String, ExprNodeDesc> colExprMap = new HashMap<String, ExprNodeDesc>();
HashMap<Integer, Set<String>> posToAliasMap = new HashMap<Integer, Set<String>>();
int outputPos = 0;
for (int pos = 0; pos < children.size(); pos++) {
// 2.1. Backtracking from RS
ReduceSinkOperator inputRS = (ReduceSinkOperator) children.get(pos);
if (inputRS.getNumParent() != 1) {
throw new SemanticException("RS should have single parent");
}
Operator<?> parent = inputRS.getParentOperators().get(0);
ReduceSinkDesc rsDesc = inputRS.getConf();
int[] index = inputRS.getValueIndex();
Byte tag = (byte) rsDesc.getTag();
// 2.1.1. If semijoin...
if (semiJoin && pos != 0) {
exprMap.put(tag, new ArrayList<ExprNodeDesc>());
childOps[pos] = inputRS;
continue;
}
posToAliasMap.put(pos, new HashSet<String>(inputRS.getSchema().getTableNames()));
List<String> keyColNames = rsDesc.getOutputKeyColumnNames();
List<String> valColNames = rsDesc.getOutputValueColumnNames();
Map<String, ExprNodeDesc> descriptors = buildBacktrackFromReduceSinkForJoin(outputPos, outputColumnNames, keyColNames, valColNames, index, parent, baseSrc[pos]);
List<ColumnInfo> parentColumns = parent.getSchema().getSignature();
for (int i = 0; i < index.length; i++) {
ColumnInfo info = new ColumnInfo(parentColumns.get(i));
info.setInternalName(outputColumnNames.get(outputPos));
info.setTabAlias(tabAlias);
outputColumns.add(info);
reversedExprs.put(outputColumnNames.get(outputPos), tag);
outputPos++;
}
exprMap.put(tag, new ArrayList<ExprNodeDesc>(descriptors.values()));
colExprMap.putAll(descriptors);
childOps[pos] = inputRS;
}
// 3. We populate the filters and filterMap structure needed in the join descriptor
List<List<ExprNodeDesc>> filtersPerInput = Lists.newArrayList();
int[][] filterMap = new int[children.size()][];
for (int i = 0; i < children.size(); i++) {
filtersPerInput.add(new ArrayList<ExprNodeDesc>());
}
// 3. We populate the filters structure
for (int i = 0; i < filterExpressions.size(); i++) {
int leftPos = joinCondns[i].getLeft();
int rightPos = joinCondns[i].getRight();
for (ExprNodeDesc expr : filterExpressions.get(i)) {
// We need to update the exprNode, as currently
// they refer to columns in the output of the join;
// they should refer to the columns output by the RS
int inputPos = updateExprNode(expr, reversedExprs, colExprMap);
if (inputPos == -1) {
inputPos = leftPos;
}
filtersPerInput.get(inputPos).add(expr);
if (joinCondns[i].getType() == JoinDesc.FULL_OUTER_JOIN || joinCondns[i].getType() == JoinDesc.LEFT_OUTER_JOIN || joinCondns[i].getType() == JoinDesc.RIGHT_OUTER_JOIN) {
if (inputPos == leftPos) {
updateFilterMap(filterMap, leftPos, rightPos);
} else {
updateFilterMap(filterMap, rightPos, leftPos);
}
}
}
}
for (int pos = 0; pos < children.size(); pos++) {
ReduceSinkOperator inputRS = (ReduceSinkOperator) children.get(pos);
ReduceSinkDesc rsDesc = inputRS.getConf();
Byte tag = (byte) rsDesc.getTag();
filters.put(tag, filtersPerInput.get(pos));
}
// 4. We create the join operator with its descriptor
JoinDesc desc = new JoinDesc(exprMap, outputColumnNames, noOuterJoin, joinCondns, filters, joinExpressions, null);
desc.setReversedExprs(reversedExprs);
desc.setFilterMap(filterMap);
JoinOperator joinOp = (JoinOperator) OperatorFactory.getAndMakeChild(childOps[0].getCompilationOpContext(), desc, new RowSchema(outputColumns), childOps);
joinOp.setColumnExprMap(colExprMap);
joinOp.setPosToAliasMap(posToAliasMap);
joinOp.getConf().setBaseSrc(baseSrc);
if (LOG.isDebugEnabled()) {
LOG.debug("Generated " + joinOp + " with row schema: [" + joinOp.getSchema() + "]");
}
return joinOp;
}
use of org.apache.beam.vendor.calcite.v1_28_0.org.apache.calcite.rel.core.JoinRelType in project flink by apache.
the class FlinkRelDecorrelator method aggregateCorrelatorOutput.
/**
* Pulls a {@link Project} above a {@link Correlate} from its RHS input.
* Enforces nullability for join output.
*
* @param correlate Correlate
* @param project the original project as the RHS input of the join
* @param isCount Positions which are calls to the <code>COUNT</code>
* aggregation function
* @return the subtree with the new LogicalProject at the root
*/
private RelNode aggregateCorrelatorOutput(Correlate correlate, LogicalProject project, Set<Integer> isCount) {
final RelNode left = correlate.getLeft();
final JoinRelType joinType = correlate.getJoinType().toJoinType();
// now create the new project
final List<Pair<RexNode, String>> newProjects = Lists.newArrayList();
// Project everything from the LHS and then those from the original
// project
final List<RelDataTypeField> leftInputFields = left.getRowType().getFieldList();
for (int i = 0; i < leftInputFields.size(); i++) {
newProjects.add(RexInputRef.of2(i, leftInputFields));
}
// Marked where the projected expr is coming from so that the types will
// become nullable for the original projections which are now coming out
// of the nullable side of the OJ.
boolean projectPulledAboveLeftCorrelator = joinType.generatesNullsOnRight();
for (Pair<RexNode, String> pair : project.getNamedProjects()) {
RexNode newProjExpr = removeCorrelationExpr(pair.left, projectPulledAboveLeftCorrelator, isCount);
newProjects.add(Pair.of(newProjExpr, pair.right));
}
return RelOptUtil.createProject(correlate, newProjects, false);
}
Aggregations