public Boolean areColumnsUnique(ProjectRelBase rel, BitSet columns, boolean ignoreNulls) { // ProjectRel maps a set of rows to a different set; // Without knowledge of the mapping function(whether it // preserves uniqueness), it is only safe to derive uniqueness // info from the child of a project when the mapping is f(a) => a. // // Also need to map the input column set to the corresponding child // references List<RexNode> projExprs = rel.getProjects(); BitSet childColumns = new BitSet(); for (int bit : BitSets.toIter(columns)) { RexNode projExpr = projExprs.get(bit); if (projExpr instanceof RexInputRef) { childColumns.set(((RexInputRef) projExpr).getIndex()); } else if (projExpr instanceof RexCall && ignoreNulls) { // If the expression is a cast such that the types are the same // except for the nullability, then if we're ignoring nulls, // it doesn't matter whether the underlying column reference // is nullable. Check that the types are the same by making a // nullable copy of both types and then comparing them. RexCall call = (RexCall) projExpr; if (call.getOperator() != SqlStdOperatorTable.CAST) { continue; } RexNode castOperand = call.getOperands().get(0); if (!(castOperand instanceof RexInputRef)) { continue; } RelDataTypeFactory typeFactory = rel.getCluster().getTypeFactory(); RelDataType castType = typeFactory.createTypeWithNullability(projExpr.getType(), true); RelDataType origType = typeFactory.createTypeWithNullability(castOperand.getType(), true); if (castType.equals(origType)) { childColumns.set(((RexInputRef) castOperand).getIndex()); } } else { // If the expression will not influence uniqueness of the // projection, then skip it. continue; } } // If no columns can affect uniqueness, then return unknown if (childColumns.cardinality() == 0) { return null; } return RelMetadataQuery.areColumnsUnique(rel.getChild(), childColumns, ignoreNulls); }
// implement RelOptRule public void onMatch(RelOptRuleCall call) { final FilterRelBase filterRel = call.rel(0); final ProjectRelBase projRel = call.rel(1); // convert the filter to one that references the child of the project RexNode newCondition = RelOptUtil.pushFilterPastProject(filterRel.getCondition(), projRel); RelNode newFilterRel = filterFactory == null ? filterRel.copy(filterRel.getTraitSet(), projRel.getChild(), newCondition) : filterFactory.createFilter(projRel.getChild(), newCondition); RelNode newProjRel = projectFactory == null ? projRel.copy( projRel.getTraitSet(), newFilterRel, projRel.getProjects(), projRel.getRowType()) : projectFactory.createProject( newFilterRel, projRel.getProjects(), projRel.getRowType().getFieldNames()); call.transformTo(newProjRel); }