Skip to content

Commit 9414b77

Browse files
olivrleetanclary
authored andcommitted
[CALCITE-6301] Extend ‘Must-filter’ columns to support a conditional bypass list
1 parent 27298b2 commit 9414b77

File tree

12 files changed

+674
-101
lines changed

12 files changed

+674
-101
lines changed

core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import org.apache.calcite.rel.type.RelDataTypeFactory;
2121
import org.apache.calcite.rel.type.RelDataTypeField;
2222
import org.apache.calcite.sql.SqlNode;
23-
import org.apache.calcite.util.ImmutableBitSet;
2423
import org.apache.calcite.util.Pair;
2524
import org.apache.calcite.util.Util;
2625

@@ -58,9 +57,13 @@ abstract class AbstractNamespace implements SqlValidatorNamespace {
5857
/** As {@link #rowType}, but not necessarily a struct. */
5958
protected @Nullable RelDataType type;
6059

61-
/** Ordinals of fields that must be filtered. Initially the empty set, but
62-
* should typically be re-assigned on validate. */
63-
protected ImmutableBitSet mustFilterFields = ImmutableBitSet.of();
60+
/**
61+
* Class that holds information about what fields need to be filtered, what bypass-fields
62+
* can defuse the errors if they are filtered on as an alternative, and a set used during
63+
* validation internally. Initialized as empty object, but should typically be re-assiged
64+
* on validate.
65+
*/
66+
protected FilterRequirement filterRequirement = new FilterRequirement();
6467

6568
protected final @Nullable SqlNode enclosingNode;
6669

@@ -164,9 +167,9 @@ abstract class AbstractNamespace implements SqlValidatorNamespace {
164167
return ImmutableList.of();
165168
}
166169

167-
@Override public ImmutableBitSet getMustFilterFields() {
168-
return requireNonNull(mustFilterFields,
169-
"mustFilterFields (maybe validation is not complete?)");
170+
@Override public FilterRequirement getFilterRequirement() {
171+
return requireNonNull(filterRequirement,
172+
"filterRequirement (maybe validation is not complete?)");
170173
}
171174

172175
@Override public SqlMonotonicity getMonotonicity(String columnName) {
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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.sql.validate;
18+
19+
import org.apache.calcite.util.ImmutableBitSet;
20+
21+
import com.google.common.collect.ImmutableSet;
22+
23+
import java.util.Set;
24+
25+
/**
26+
* Class that encapsulates filtering requirements when overloading SemanticTable. <br>
27+
*
28+
* <p>A few examples of the behavior:<br>
29+
*
30+
* <p>Table <code>t</code> has a must-filter field <code>f</code> and bypass-fields <code>b0</code>
31+
* and <code>b1</code>.<br>
32+
* SQL: <code>select f from t;</code><br> -> Errors because there's no filter on f. <br>
33+
* SQL: <code>select * from (select f from t);</code><br> -> Errors at the inner query because
34+
* there's no filter on f. <br>
35+
* SQL: <code>select f from t where f = 1;</code><br> -> Valid because there is a filter on f.<br>
36+
* SQL: <code>select * from (select f from t) where f = 1;</code><br> -> Valid because there is a
37+
* filter on f. <br>
38+
* SQL: <code>select f from t where b0 = 1;</code><br> -> Valid because there is a filter on
39+
* bypass-field b0.<br>
40+
*
41+
* <p>Some notes on remnantFilterFields.<br>
42+
* remnantFilterFields is used to identify whether the query should error
43+
* at the top level query. It is populated with the filter-field value when a filter-field is not
44+
* selected or filtered on, but a bypass-field for the table is selected.
45+
* The remnantFilterFields are no longer accessible by the enclosing query and hence can no
46+
* longer be defused by filtering on it; however, it can be defused if the bypass-field is
47+
* filtered on, hence we need to keep track of it.
48+
49+
* Example:<br>
50+
* Table <code>t</code> has a must-filter field <code>f</code> and bypass-fields <code>b0</code>
51+
* and <code>b1</code>.<br>
52+
* SQL: <code>select b0, b1 from t;</code><br>
53+
*
54+
* <p>This results in: <br>
55+
* filterFields:[]<br>
56+
* bypassFields:[b0, b1]<br>
57+
* remnantFilterFields: [f]<br>
58+
* -> Errors because it is a top level query and remnantFilterFields is not empty. <br>
59+
*
60+
* <p>SQL: <code>select * from (select b0, b1 from t) where b0 = 1;</code><br>
61+
* When unwrapping the inner query we get the same FilterRequirement as the previous example:<br>
62+
* filterFields:[]<br>
63+
* bypassFields:[b0, b1]<br>
64+
* remnantFilterFields: [f]<br>
65+
* When unwrapping the top level query, the filter on b0 defuses the remnantFilterField requirement
66+
* of [f] because it originated from the same table, resulting in the following: <br>
67+
* filterFields:[]<br>
68+
* bypassFields:[b0, b1]<br>
69+
* remnantFilterFields: []<br>
70+
* -> Valid because remnantFilterFields is empty now.
71+
*/
72+
final class FilterRequirement {
73+
74+
/** The ordinals (in the row type) of the "must-filter" fields,
75+
* fields that must be filtered in a query.*/
76+
private final ImmutableBitSet filterFields;
77+
/** The ordinals (in the row type) of the "bypass" fields,
78+
* fields that can defuse validation errors on filterFields if filtered on. */
79+
private final ImmutableBitSet bypassFields;
80+
/** Set of filterField SqlQualifieds that have not been defused
81+
* in the current query, but can still be defused by filtering on a bypass field in the
82+
* enclosing query.*/
83+
private final ImmutableSet<SqlQualified> remnantFilterFields;
84+
85+
/**
86+
* Creates a <code>FilterRequirement</code>.
87+
*
88+
* @param filterFields Ordinals of the "must-filter" fields.
89+
* @param bypassFields Ordinals of the "bypass" fields.
90+
* @param remnantFilterFields Filter fields that can no longer be filtered on,
91+
* but can only be defused if a bypass field is filtered on.
92+
*/
93+
FilterRequirement(ImmutableBitSet filterFields,
94+
ImmutableBitSet bypassFields, Set<SqlQualified> remnantFilterFields) {
95+
this.filterFields = ImmutableBitSet.of(filterFields);
96+
this.bypassFields = ImmutableBitSet.of(bypassFields);
97+
this.remnantFilterFields = ImmutableSet.copyOf(remnantFilterFields);
98+
}
99+
100+
/** Creates an empty FilterRequirement. */
101+
FilterRequirement() {
102+
this(ImmutableBitSet.of(), ImmutableBitSet.of(), ImmutableSet.of());
103+
}
104+
/** Returns filterFields. */
105+
public ImmutableBitSet getFilterFields() {
106+
return filterFields;
107+
}
108+
109+
/** Returns bypassFields. */
110+
public ImmutableBitSet getBypassFields() {
111+
return bypassFields;
112+
}
113+
114+
/** Returns remnantFilterFields. */
115+
public ImmutableSet<SqlQualified> getRemnantFilterFields() {
116+
return remnantFilterFields;
117+
}
118+
}

core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ private SqlValidatorNamespace resolveImpl(SqlIdentifier id) {
210210
}
211211
}
212212

213-
this.mustFilterFields = resolvedNamespace.getMustFilterFields();
213+
filterRequirement = resolvedNamespace.getFilterRequirement();
214214
RelDataType rowType = resolvedNamespace.getRowType();
215215

216216
if (extendList != null) {

core/src/main/java/org/apache/calcite/sql/validate/SemanticTable.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616
*/
1717
package org.apache.calcite.sql.validate;
1818

19+
import com.google.common.collect.ImmutableList;
20+
1921
import org.checkerframework.checker.nullness.qual.Nullable;
2022

23+
import java.util.List;
24+
2125
/**
2226
* Extension to {@link SqlValidatorTable} with extra, optional metadata.
2327
*
@@ -44,4 +48,12 @@ public interface SemanticTable {
4448
default boolean mustFilter(int column) {
4549
return getFilter(column) != null;
4650
}
51+
52+
/**
53+
* Returns a list of column ordinals (0-based) of fields that defuse
54+
* must-filter columns when filtered on.
55+
*/
56+
default List<Integer> bypassFieldList() {
57+
return ImmutableList.of();
58+
}
4759
}

0 commit comments

Comments
 (0)