Skip to content

Commit b8a1823

Browse files
committed
[CALCITE-5787] Add interface in RelNode for getInputFieldsUsed
1 parent abd7ca2 commit b8a1823

File tree

6 files changed

+264
-0
lines changed

6 files changed

+264
-0
lines changed

core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,44 @@ interface Handler extends MetadataHandler<Selectivity> {
7676
}
7777
}
7878

79+
/**
80+
* Metadata that identifies, per input, which fields (columns) of each
81+
* input are referenced by a relational expression.
82+
*
83+
* <p>For a relational expression with N inputs, this returns an
84+
* {@link ImmutableList} of length N. Each element is an
85+
* {@link ImmutableBitSet} with bits set for zero-based field ordinals of
86+
* that input which are referenced by the expression.
87+
*
88+
* <p>Returns empty {@link ImmutableList} if information cannot be determined.
89+
*/
90+
public interface InputFieldsUsed extends Metadata {
91+
MetadataDef<InputFieldsUsed> DEF =
92+
MetadataDef.of(InputFieldsUsed.class, InputFieldsUsed.Handler.class,
93+
BuiltInMethod.INPUT_FIELDS_USED.method);
94+
95+
/**
96+
* Returns, for each input of this relational expression, a bit set of the
97+
* referenced field ordinals.
98+
*
99+
* @return an {@link ImmutableList} of {@link ImmutableBitSet} of length N
100+
* where N is the number of inputs, or empty {@link ImmutableList}
101+
* if the information is not available
102+
*/
103+
ImmutableList<ImmutableBitSet> getInputFieldsUsed();
104+
105+
/** Handler API. */
106+
@FunctionalInterface
107+
interface Handler extends MetadataHandler<InputFieldsUsed> {
108+
ImmutableList<ImmutableBitSet> getInputFieldsUsed(RelNode r,
109+
RelMetadataQuery mq);
110+
111+
@Override default MetadataDef<InputFieldsUsed> getDef() {
112+
return DEF;
113+
}
114+
}
115+
}
116+
79117
/** Metadata about which combinations of columns are unique identifiers. */
80118
public interface UniqueKeys extends Metadata {
81119
MetadataDef<UniqueKeys> DEF =

core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ protected DefaultRelMetadataProvider() {
6262
RelMdSelectivity.SOURCE,
6363
RelMdExplainVisibility.SOURCE,
6464
RelMdPredicates.SOURCE,
65+
RelMdInputFieldsUsed.SOURCE,
6566
RelMdAllPredicates.SOURCE,
6667
RelMdCollation.SOURCE,
6768
RelMdFunctionalDependency.SOURCE));
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.calcite.rel.metadata;
18+
19+
import org.apache.calcite.plan.RelOptUtil;
20+
import org.apache.calcite.rel.RelNode;
21+
import org.apache.calcite.rel.core.Aggregate;
22+
import org.apache.calcite.rel.core.Calc;
23+
import org.apache.calcite.rel.core.Filter;
24+
import org.apache.calcite.rel.core.Join;
25+
import org.apache.calcite.rel.core.JoinRelType;
26+
import org.apache.calcite.rel.core.Project;
27+
import org.apache.calcite.rel.core.SetOp;
28+
import org.apache.calcite.rel.core.TableScan;
29+
import org.apache.calcite.rex.RexNode;
30+
import org.apache.calcite.rex.RexProgram;
31+
import org.apache.calcite.util.ImmutableBitSet;
32+
33+
import com.google.common.collect.ImmutableList;
34+
35+
import java.util.List;
36+
import java.util.Set;
37+
38+
/**
39+
* Metadata provider to determine which input fields are used by a RelNode.
40+
*/
41+
public class RelMdInputFieldsUsed
42+
implements MetadataHandler<BuiltInMetadata.InputFieldsUsed> {
43+
public static final RelMetadataProvider SOURCE =
44+
ReflectiveRelMetadataProvider.reflectiveSource(
45+
new RelMdInputFieldsUsed(), BuiltInMetadata.InputFieldsUsed.Handler.class);
46+
47+
@Override public MetadataDef<BuiltInMetadata.InputFieldsUsed> getDef() {
48+
return BuiltInMetadata.InputFieldsUsed.DEF;
49+
}
50+
51+
public ImmutableList<ImmutableBitSet> getInputFieldsUsed(RelNode rel,
52+
RelMetadataQuery mq) {
53+
return ImmutableList.of();
54+
}
55+
56+
public ImmutableList<ImmutableBitSet> getInputFieldsUsed(TableScan scan,
57+
RelMetadataQuery mq) {
58+
final BuiltInMetadata.InputFieldsUsed.Handler handler =
59+
scan.getTable().unwrap(BuiltInMetadata.InputFieldsUsed.Handler.class);
60+
if (handler != null) {
61+
return handler.getInputFieldsUsed(scan, mq);
62+
}
63+
final int fieldCount = scan.getRowType().getFieldCount();
64+
return ImmutableList.of(ImmutableBitSet.range(fieldCount));
65+
}
66+
67+
public ImmutableList<ImmutableBitSet> getInputFieldsUsed(Project project,
68+
RelMetadataQuery mq) {
69+
final ImmutableBitSet bits = RelOptUtil.InputFinder.bits(project.getProjects(), null);
70+
return ImmutableList.of(bits);
71+
}
72+
73+
public ImmutableList<ImmutableBitSet> getInputFieldsUsed(Filter filter,
74+
RelMetadataQuery mq) {
75+
return mq.getInputFieldsUsed(filter.getInput());
76+
}
77+
78+
public ImmutableList<ImmutableBitSet> getInputFieldsUsed(Calc calc,
79+
RelMetadataQuery mq) {
80+
final RexProgram program = calc.getProgram();
81+
final List<RexNode> expandedProjects = program.expandList(program.getProjectList());
82+
final RexNode cond = program.getCondition() == null
83+
? null
84+
: program.expandLocalRef(program.getCondition());
85+
final ImmutableBitSet bits = RelOptUtil.InputFinder.bits(expandedProjects, cond);
86+
return ImmutableList.of(bits);
87+
}
88+
89+
public ImmutableList<ImmutableBitSet> getInputFieldsUsed(Join join,
90+
RelMetadataQuery mq) {
91+
List<ImmutableBitSet> leftInputFieldsUsed = mq.getInputFieldsUsed(join.getLeft());
92+
List<ImmutableBitSet> rightInputFieldsUsed = mq.getInputFieldsUsed(join.getRight());
93+
assert leftInputFieldsUsed.size() == 1 && rightInputFieldsUsed.size() == 1;
94+
95+
ImmutableBitSet rightUsedBits = rightInputFieldsUsed.get(0);
96+
if (join.getJoinType() == JoinRelType.SEMI
97+
|| join.getJoinType() == JoinRelType.ANTI) {
98+
rightUsedBits = ImmutableBitSet.of();
99+
}
100+
101+
return ImmutableList.of(leftInputFieldsUsed.get(0), rightUsedBits);
102+
}
103+
104+
public ImmutableList<ImmutableBitSet> getInputFieldsUsed(SetOp setOp,
105+
RelMetadataQuery mq) {
106+
final ImmutableList.Builder<ImmutableBitSet> builder = ImmutableList.builder();
107+
for (RelNode input : setOp.getInputs()) {
108+
ImmutableList<ImmutableBitSet> inputFieldsBits = mq.getInputFieldsUsed(input);
109+
assert inputFieldsBits.size() == 1;
110+
builder.add(inputFieldsBits.get(0));
111+
}
112+
return builder.build();
113+
}
114+
115+
public ImmutableList<ImmutableBitSet> getInputFieldsUsed(Aggregate agg,
116+
RelMetadataQuery mq) {
117+
Set<Integer> fields = RelOptUtil.getAllFields(agg);
118+
return ImmutableList.of(ImmutableBitSet.of(fields));
119+
}
120+
}

core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public class RelMetadataQuery extends RelMetadataQueryBase {
110110
private BuiltInMetadata.UniqueKeys.Handler uniqueKeysHandler;
111111
private BuiltInMetadata.LowerBoundCost.Handler lowerBoundCostHandler;
112112
private BuiltInMetadata.FunctionalDependency.Handler functionalDependencyHandler;
113+
private BuiltInMetadata.InputFieldsUsed.Handler inputFieldsUsedHandler;
113114

114115
/**
115116
* Creates the instance with {@link JaninoRelMetadataProvider} instance
@@ -158,6 +159,7 @@ public RelMetadataQuery(MetadataHandlerProvider provider) {
158159
this.lowerBoundCostHandler = provider.handler(BuiltInMetadata.LowerBoundCost.Handler.class);
159160
this.functionalDependencyHandler =
160161
provider.handler(BuiltInMetadata.FunctionalDependency.Handler.class);
162+
this.inputFieldsUsedHandler = provider.handler(BuiltInMetadata.InputFieldsUsed.Handler.class);
161163
}
162164

163165
/** Creates and initializes the instance that will serve as a prototype for
@@ -193,6 +195,7 @@ private RelMetadataQuery(@SuppressWarnings("unused") boolean dummy) {
193195
this.lowerBoundCostHandler = initialHandler(BuiltInMetadata.LowerBoundCost.Handler.class);
194196
this.functionalDependencyHandler =
195197
initialHandler(BuiltInMetadata.FunctionalDependency.Handler.class);
198+
this.inputFieldsUsedHandler = initialHandler(BuiltInMetadata.InputFieldsUsed.Handler.class);
196199
}
197200

198201
private RelMetadataQuery(
@@ -225,6 +228,7 @@ private RelMetadataQuery(
225228
this.uniqueKeysHandler = prototype.uniqueKeysHandler;
226229
this.lowerBoundCostHandler = prototype.lowerBoundCostHandler;
227230
this.functionalDependencyHandler = prototype.functionalDependencyHandler;
231+
this.inputFieldsUsedHandler = prototype.inputFieldsUsedHandler;
228232
}
229233

230234
//~ Methods ----------------------------------------------------------------
@@ -1058,4 +1062,17 @@ public ArrowSet getFDs(RelNode rel) {
10581062
}
10591063
}
10601064
}
1065+
1066+
/**
1067+
* Returns the input fields are used by a RelNode.
1068+
*/
1069+
public ImmutableList<ImmutableBitSet> getInputFieldsUsed(RelNode rel) {
1070+
for (;;) {
1071+
try {
1072+
return inputFieldsUsedHandler.getInputFieldsUsed(rel, this);
1073+
} catch (MetadataHandlerProvider.NoHandler e) {
1074+
inputFieldsUsedHandler = revise(BuiltInMetadata.InputFieldsUsed.Handler.class);
1075+
}
1076+
}
1077+
}
10611078
}

core/src/main/java/org/apache/calcite/util/BuiltInMethod.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import org.apache.calcite.rel.metadata.BuiltInMetadata.ExplainVisibility;
6565
import org.apache.calcite.rel.metadata.BuiltInMetadata.ExpressionLineage;
6666
import org.apache.calcite.rel.metadata.BuiltInMetadata.FunctionalDependency;
67+
import org.apache.calcite.rel.metadata.BuiltInMetadata.InputFieldsUsed;
6768
import org.apache.calcite.rel.metadata.BuiltInMetadata.LowerBoundCost;
6869
import org.apache.calcite.rel.metadata.BuiltInMetadata.MaxRowCount;
6970
import org.apache.calcite.rel.metadata.BuiltInMetadata.Measure;
@@ -896,6 +897,7 @@ public enum BuiltInMethod {
896897
STR_TO_MAP(SqlFunctions.class, "strToMap", String.class, String.class, String.class),
897898
SUBSTRING_INDEX(SqlFunctions.class, "substringIndex", String.class, String.class, int.class),
898899
SELECTIVITY(Selectivity.class, "getSelectivity", RexNode.class),
900+
INPUT_FIELDS_USED(InputFieldsUsed.class, "getInputFieldsUsed"),
899901
UNIQUE_KEYS(UniqueKeys.class, "getUniqueKeys", boolean.class),
900902
AVERAGE_ROW_SIZE(Size.class, "averageRowSize"),
901903
AVERAGE_COLUMN_SIZES(Size.class, "averageColumnSizes"),

core/src/test/java/org/apache/calcite/test/RelMetadataTest.java

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.apache.calcite.rel.SingleRel;
4444
import org.apache.calcite.rel.core.Aggregate;
4545
import org.apache.calcite.rel.core.AggregateCall;
46+
import org.apache.calcite.rel.core.Calc;
4647
import org.apache.calcite.rel.core.Correlate;
4748
import org.apache.calcite.rel.core.Exchange;
4849
import org.apache.calcite.rel.core.Filter;
@@ -52,6 +53,7 @@
5253
import org.apache.calcite.rel.core.Minus;
5354
import org.apache.calcite.rel.core.Project;
5455
import org.apache.calcite.rel.core.Sample;
56+
import org.apache.calcite.rel.core.SetOp;
5557
import org.apache.calcite.rel.core.Sort;
5658
import org.apache.calcite.rel.core.TableModify;
5759
import org.apache.calcite.rel.core.TableScan;
@@ -931,6 +933,90 @@ final RelMetadataFixture sql(String sql) {
931933
assertThat(fd2, sameInstance(fd1));
932934
}
933935

936+
// ----------------------------------------------------------------------
937+
// Tests for InputFieldsUsed metadata in RelMdInputFieldsUsed
938+
// ----------------------------------------------------------------------
939+
940+
@Test void testInputFieldsUsedSemiJoin() {
941+
final RelBuilder relBuilder = RelBuilderTest.createBuilder();
942+
relBuilder.scan("EMP");
943+
relBuilder.scan("DEPT");
944+
// Build semi-join on DEPTNO
945+
relBuilder.semiJoin(
946+
relBuilder.equals(relBuilder.field(2, 0, "DEPTNO"),
947+
relBuilder.field(2, 1, "DEPTNO")));
948+
final Join join = (Join) relBuilder.build();
949+
final RelMetadataQuery mq = join.getCluster().getMetadataQuery();
950+
final List<ImmutableBitSet> inputFields = mq.getInputFieldsUsed(join);
951+
952+
// For SEMI join expect left input fields to be all columns of left input
953+
// and right input fields to be empty (semi-join does not require right output).
954+
final int leftCount = join.getLeft().getRowType().getFieldCount();
955+
assertThat(inputFields, hasSize(2));
956+
assertThat(inputFields.get(0), equalTo(ImmutableBitSet.range(leftCount)));
957+
assertThat(inputFields.get(1).isEmpty(), is(true));
958+
}
959+
960+
@Test void testInputFieldsUsedUnionSetOp() {
961+
final RelBuilder builder = RelBuilderTest.createBuilder();
962+
builder.scan("DEPT").project(builder.field(1)); // name
963+
builder.scan("EMP").project(builder.field(2)); // job
964+
builder.union(true);
965+
final SetOp setOp = (SetOp) builder.build();
966+
final RelMetadataQuery mq = setOp.getCluster().getMetadataQuery();
967+
final List<ImmutableBitSet> inputFields = mq.getInputFieldsUsed(setOp);
968+
assertThat(
969+
inputFields, equalTo(
970+
ImmutableList.of(ImmutableBitSet.of(1), ImmutableBitSet.of(2))));
971+
}
972+
973+
@Test void testInputFieldsUsedProject() {
974+
final RelBuilder builder = RelBuilderTest.createBuilder();
975+
final RelNode project = builder
976+
.scan("EMP")
977+
.project(builder.field(0), builder.field(2))
978+
.build();
979+
final RelMetadataQuery mq = project.getCluster().getMetadataQuery();
980+
final java.util.List<ImmutableBitSet> inputFields = mq.getInputFieldsUsed(project);
981+
982+
assertThat(inputFields, hasSize(1));
983+
assertThat(inputFields.get(0), equalTo(ImmutableBitSet.of(0, 2)));
984+
}
985+
986+
@Test void testInputFieldsUsedFilter() {
987+
final RelBuilder builder = RelBuilderTest.createBuilder();
988+
final RelNode filter = builder
989+
.scan("EMP")
990+
.filter(builder.equals(builder.field(2), builder.literal(10)))
991+
.build();
992+
final RelMetadataQuery mq = filter.getCluster().getMetadataQuery();
993+
final List<ImmutableBitSet> inputFields = mq.getInputFieldsUsed(filter);
994+
995+
final int fieldCount = filter.getInput(0).getRowType().getFieldCount();
996+
assertThat(inputFields, hasSize(1));
997+
assertThat(inputFields.get(0), equalTo(ImmutableBitSet.range(fieldCount)));
998+
}
999+
1000+
@Test void testInputFieldsUsedCalc() {
1001+
final RelBuilder builder = RelBuilderTest.createBuilder();
1002+
final RelNode proj = builder
1003+
.scan("EMP")
1004+
.project(builder.field(0), builder.field(2))
1005+
.build();
1006+
final HepProgram program = new HepProgramBuilder()
1007+
.addRuleInstance(CoreRules.PROJECT_TO_CALC)
1008+
.build();
1009+
final HepPlanner planner = new HepPlanner(program);
1010+
planner.setRoot(proj);
1011+
final RelNode calc = planner.findBestExp();
1012+
assertThat(calc, instanceOf(Calc.class));
1013+
1014+
final RelMetadataQuery mq = calc.getCluster().getMetadataQuery();
1015+
final List<ImmutableBitSet> inputFields = mq.getInputFieldsUsed(calc);
1016+
assertThat(inputFields, hasSize(1));
1017+
assertThat(inputFields.get(0), equalTo(ImmutableBitSet.of(0, 2)));
1018+
}
1019+
9341020
// ----------------------------------------------------------------------
9351021
// Tests for getColumnOrigins
9361022
// ----------------------------------------------------------------------

0 commit comments

Comments
 (0)