forked from apache/calcite
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CALCITE-6301] Following "must-filter" columns, add "bypass" columns
In [CALCITE-6219], we introduced `interface SemanticTable`. A table that implements this interface can define "must-filter" fields; a query that does not filter those fields in its WHERE or HAVING clause will throw a validation error. In this commit, we allow "bypass" fields. If a query filters on any bypass field in its WHERE or HAVING clause, then the must-filter fields are ignored ("defused"). Add `class FilterRequirement`, which holds must-filter fields, bypass fields, and an internal concept called "remnant-filter" fields. Various tidy-ups (Julian Hyde): improve javadoc formatting, remove accessor methods to public final fields, and use the new `ImmutableBitSet.stream()` method. Close apache#3984
- Loading branch information
1 parent
a601040
commit 7ad5a39
Showing
12 changed files
with
706 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
135 changes: 135 additions & 0 deletions
135
core/src/main/java/org/apache/calcite/sql/validate/FilterRequirement.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to you under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.apache.calcite.sql.validate; | ||
|
||
import org.apache.calcite.util.ImmutableBitSet; | ||
|
||
import com.google.common.collect.ImmutableSet; | ||
|
||
import java.util.Set; | ||
|
||
/** | ||
* Filtering requirements of a query, describing "must-filter" fields and | ||
* "bypass" fields. | ||
* | ||
* <p>"Must-filter" fields must be filtered for a query to be considered valid; | ||
* and "bypass" fields can defuse the errors if they are filtered on as an | ||
* alternative. | ||
* | ||
* <p>Filter requirements originate in a {@link SemanticTable} in the model | ||
* and propagate to any query that uses that table. | ||
* | ||
* <p>For example, consider table {@code t}, | ||
* which has a must-filter field {@code f} | ||
* and bypass-fields {@code b0} and {@code b1}, | ||
* and the following queries: | ||
* | ||
* <ol> | ||
* <li>Query {@code select f from t} | ||
* is invalid because there is no filter on {@code f}. | ||
* | ||
* <li>Query {@code select * from (select f from t)} gives an error in the | ||
* subquery because there is no filter on {@code f}. | ||
* | ||
* <li>Query {@code select f from t where f = 1} is valid because there is a | ||
* filter on {@code f}. | ||
* | ||
* <li>Query {@code select * from (select f from t) where f = 1} is valid | ||
* because there is a filter on {@code f}. | ||
* | ||
* <li>Query {@code select f from t where b0 = 1} is valid because there is a | ||
* filter on the bypass-field {@code b0}. | ||
* </ol> | ||
* | ||
* <p>{@code FilterRequirement} is immutable, and has an instance {@link #EMPTY} | ||
* with no filters. | ||
* | ||
* <p><b>Notes on remnantFilterFields</b> | ||
* | ||
* <p>{@link #remnantFilterFields} identifies whether the query should error | ||
* at the top level query. It is populated with the filter-field value when a | ||
* filter-field is not selected or filtered on, but a bypass-field for the | ||
* table is selected. | ||
* | ||
* <p>A remnant-filter field is no longer accessible by the enclosing query, | ||
* and so the query can no longer be defused by filtering on it. We must keep | ||
* track of the remnant-filter field because the query can still be defused by | ||
* filtering on a bypass-field. | ||
* | ||
* <p>For example, consider table {@code t} with a must-filter field {@code f} | ||
* and bypass-fields {@code b0} and {@code b1}. | ||
* | ||
* <ol> | ||
* <li>Query {@code select b0, b1 from t} results in | ||
* {@code filterFields} = [], | ||
* {@code bypassFields} = [{@code b0}, {@code b1}], | ||
* {@code remnantFilterFields} = [{@code f}]. | ||
* The query is invalid because it is a top-level query and | ||
* {@link #remnantFilterFields} is not empty. | ||
* | ||
* <li>Query {@code select * from (select b0, b1 from t) where b0 = 1} is valid. | ||
* When unwrapping the subquery we get the same {@code FilterRequirement} | ||
* as the previous example: | ||
* {@code filterFields} = [], | ||
* {@code bypassFields} = [{@code b0}, {@code b1}], | ||
* {@code remnantFilterFields} = [{@code f}]. | ||
* But when unwrapping the top-level query, the filter on {@code b0} defuses | ||
* the {@code remnantFilterField} requirement of [{@code f}] because it | ||
* originated in the same table, resulting in the following: | ||
* {@code filterFields} = [], | ||
* {@code bypassFields} = [{@code b0}, {@code b1}], | ||
* {@code remnantFilterFields} = []. | ||
* The query is valid because {@link #remnantFilterFields} is now empty. | ||
* </ol> | ||
* | ||
* @see SqlValidatorNamespace#getFilterRequirement() | ||
*/ | ||
public class FilterRequirement { | ||
/** Empty filter requirement. */ | ||
public static final FilterRequirement EMPTY = | ||
new FilterRequirement(ImmutableBitSet.of(), ImmutableBitSet.of(), | ||
ImmutableSet.of()); | ||
|
||
/** Ordinals (in the row type) of the "must-filter" fields, | ||
* fields that must be filtered in a query. */ | ||
public final ImmutableBitSet filterFields; | ||
|
||
/** Ordinals (in the row type) of the "bypass" fields, | ||
* fields that can defuse validation errors on {@link #filterFields} | ||
* if filtered on. */ | ||
public final ImmutableBitSet bypassFields; | ||
|
||
/** Set of {@link SqlQualified} instances representing fields that have not | ||
* been defused in the current query, but can still be defused by filtering | ||
* on a bypass field in the enclosing query. */ | ||
public final ImmutableSet<SqlQualified> remnantFilterFields; | ||
|
||
/** | ||
* Creates a {@code FilterRequirement}. | ||
* | ||
* @param filterFields Ordinals of the "must-filter" fields | ||
* @param bypassFields Ordinals of the "bypass" fields | ||
* @param remnantFilterFields Filter fields that can no longer be filtered on, | ||
* but can only be defused if a bypass field is filtered on | ||
*/ | ||
FilterRequirement(Iterable<Integer> filterFields, | ||
Iterable<Integer> bypassFields, Set<SqlQualified> remnantFilterFields) { | ||
this.filterFields = ImmutableBitSet.of(filterFields); | ||
this.bypassFields = ImmutableBitSet.of(bypassFields); | ||
this.remnantFilterFields = ImmutableSet.copyOf(remnantFilterFields); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.