-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Aleksey Plekhanov <plehanov.alex@gmail.com>
- Loading branch information
1 parent
1185941
commit 9f70c2a
Showing
29 changed files
with
1,246 additions
and
99 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
86 changes: 86 additions & 0 deletions
86
...rc/main/java/org/apache/ignite/internal/processors/query/calcite/hint/HintDefinition.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,86 @@ | ||
/* | ||
* 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.ignite.internal.processors.query.calcite.hint; | ||
|
||
import org.apache.calcite.rel.hint.HintPredicate; | ||
import org.apache.calcite.rel.hint.HintPredicates; | ||
import org.apache.ignite.internal.processors.query.calcite.rel.logical.IgniteLogicalTableScan; | ||
|
||
/** | ||
* Holds supported SQL hints and their settings. | ||
*/ | ||
public enum HintDefinition { | ||
/** Sets the query engine like H2 or Calcite. Is preprocessed by regexp. */ | ||
QUERY_ENGINE, | ||
|
||
/** Disables planner rules. */ | ||
DISABLE_RULE, | ||
|
||
/** Forces expanding of distinct aggregates to join. */ | ||
EXPAND_DISTINCT_AGG { | ||
/** {@inheritDoc} */ | ||
@Override public HintPredicate predicate() { | ||
return HintPredicates.AGGREGATE; | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override public HintOptionsChecker optionsChecker() { | ||
return HintsConfig.OPTS_CHECK_EMPTY; | ||
} | ||
}, | ||
|
||
/** Disables indexes. */ | ||
NO_INDEX { | ||
/** {@inheritDoc} */ | ||
@Override public HintPredicate predicate() { | ||
return (hint, rel) -> rel instanceof IgniteLogicalTableScan; | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override public HintOptionsChecker optionsChecker() { | ||
return HintsConfig.OPTS_CHECK_NO_KV; | ||
} | ||
}, | ||
|
||
/** Forces index usage. */ | ||
FORCE_INDEX { | ||
/** {@inheritDoc} */ | ||
@Override public HintPredicate predicate() { | ||
return NO_INDEX.predicate(); | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override public HintOptionsChecker optionsChecker() { | ||
return NO_INDEX.optionsChecker(); | ||
} | ||
}; | ||
|
||
/** | ||
* @return Hint predicate which limits redundant hint copying and reduces mem/cpu consumption. | ||
*/ | ||
HintPredicate predicate() { | ||
return HintPredicates.SET_VAR; | ||
} | ||
|
||
/** | ||
* @return {@link HintOptionsChecker}. | ||
*/ | ||
HintOptionsChecker optionsChecker() { | ||
return HintsConfig.OPTS_CHECK_PLAIN; | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
...ain/java/org/apache/ignite/internal/processors/query/calcite/hint/HintOptionsChecker.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,30 @@ | ||
/* | ||
* 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.ignite.internal.processors.query.calcite.hint; | ||
|
||
import java.util.function.Function; | ||
import org.apache.calcite.rel.hint.RelHint; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
/** | ||
* Hint options validator. Returns error text if the rel hint can't accept passed options. | ||
* Otherwise, returns {@code null}. | ||
*/ | ||
interface HintOptionsChecker extends Function<RelHint, @Nullable String> { | ||
/* No-op. */ | ||
} |
175 changes: 175 additions & 0 deletions
175
...ite/src/main/java/org/apache/ignite/internal/processors/query/calcite/hint/HintUtils.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,175 @@ | ||
/* | ||
* 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.ignite.internal.processors.query.calcite.hint; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
import org.apache.calcite.plan.RelOptUtil; | ||
import org.apache.calcite.rel.AbstractRelNode; | ||
import org.apache.calcite.rel.RelNode; | ||
import org.apache.calcite.rel.RelWriter; | ||
import org.apache.calcite.rel.core.AggregateCall; | ||
import org.apache.calcite.rel.hint.HintStrategyTable; | ||
import org.apache.calcite.rel.hint.Hintable; | ||
import org.apache.calcite.rel.hint.RelHint; | ||
import org.apache.calcite.rel.logical.LogicalAggregate; | ||
import org.apache.calcite.rel.type.RelDataType; | ||
import org.apache.calcite.sql.SqlExplainLevel; | ||
import org.apache.ignite.IgniteLogger; | ||
import org.apache.ignite.internal.processors.query.calcite.prepare.BaseQueryContext; | ||
import org.apache.ignite.internal.processors.query.calcite.util.Commons; | ||
import org.apache.ignite.internal.util.typedef.F; | ||
|
||
import static org.apache.ignite.internal.processors.query.calcite.hint.HintDefinition.EXPAND_DISTINCT_AGG; | ||
|
||
/** | ||
* Base class for working with Calcite's SQL hints. | ||
*/ | ||
public final class HintUtils { | ||
/** */ | ||
private HintUtils() { | ||
// No-op. | ||
} | ||
|
||
/** | ||
* @return Combined list options of all {@code hints} filtered with {@code hintDef}. | ||
* @see #filterHints(RelNode, Collection, List) | ||
*/ | ||
public static Collection<String> options(RelNode rel, Collection<RelHint> hints, HintDefinition hintDef) { | ||
return F.flatCollections(filterHints(rel, hints, Collections.singletonList(hintDef)).stream() | ||
.map(h -> h.listOptions).collect(Collectors.toList())); | ||
} | ||
|
||
/** | ||
* @return Hints filtered with {@code hintDefs} and suitable for {@code rel}. | ||
* @see HintStrategyTable#apply(List, RelNode) | ||
* @see #filterHints(RelNode, Collection, List) | ||
*/ | ||
public static List<RelHint> hints(RelNode rel, HintDefinition... hintDefs) { | ||
return rel.getCluster().getHintStrategies() | ||
.apply(filterHints(rel, allRelHints(rel), Arrays.asList(hintDefs)), rel); | ||
} | ||
|
||
/** | ||
* @return Hints of {@code rel} if it is a {@code Hintable}. If is not or has no hints, empty collection. | ||
* @see Hintable#getHints() | ||
*/ | ||
public static List<RelHint> allRelHints(RelNode rel) { | ||
return rel instanceof Hintable ? ((Hintable)rel).getHints() : Collections.emptyList(); | ||
} | ||
|
||
/** | ||
* @return Distinct hints within {@code hints} filtered with {@code hintDefs}, {@link HintOptionsChecker} and | ||
* removed inherit pathes. | ||
* @see HintOptionsChecker | ||
* @see RelHint#inheritPath | ||
*/ | ||
private static List<RelHint> filterHints(RelNode rel, Collection<RelHint> hints, List<HintDefinition> hintDefs) { | ||
Set<String> requiredHintDefs = hintDefs.stream().map(Enum::name).collect(Collectors.toSet()); | ||
|
||
List<RelHint> res = hints.stream().filter(h -> requiredHintDefs.contains(h.hintName)) | ||
.map(h -> { | ||
RelHint.Builder rb = RelHint.builder(h.hintName); | ||
|
||
if (!h.listOptions.isEmpty()) | ||
rb.hintOptions(h.listOptions); | ||
else if (!h.kvOptions.isEmpty()) | ||
rb.hintOptions(h.kvOptions); | ||
|
||
return rb.build(); | ||
}).distinct().collect(Collectors.toList()); | ||
|
||
// Validate hint options. | ||
Iterator<RelHint> it = res.iterator(); | ||
|
||
while (it.hasNext()) { | ||
RelHint hint = it.next(); | ||
|
||
String optsErr = HintDefinition.valueOf(hint.hintName).optionsChecker().apply(hint); | ||
|
||
if (!F.isEmpty(optsErr)) { | ||
skippedHint(rel, hint, optsErr); | ||
|
||
it.remove(); | ||
} | ||
} | ||
|
||
return res; | ||
} | ||
|
||
/** | ||
* @return {@code True} if {@code rel} is hinted with {@link HintDefinition#EXPAND_DISTINCT_AGG}. | ||
* {@code False} otherwise. | ||
*/ | ||
public static boolean isExpandDistinctAggregate(LogicalAggregate rel) { | ||
return !hints(rel, EXPAND_DISTINCT_AGG).isEmpty() | ||
&& rel.getAggCallList().stream().anyMatch(AggregateCall::isDistinct); | ||
} | ||
|
||
/** | ||
* Logs skipped hint. | ||
*/ | ||
public static void skippedHint(RelNode relNode, RelHint hint, String reason) { | ||
IgniteLogger log = Commons.context(relNode).unwrap(BaseQueryContext.class).logger(); | ||
|
||
if (log.isDebugEnabled()) { | ||
String hintOptions = hint.listOptions.isEmpty() ? "" : "with options " | ||
+ hint.listOptions.stream().map(o -> '\'' + o + '\'').collect(Collectors.joining(",")) | ||
+ ' '; | ||
|
||
if (!relNode.getInputs().isEmpty()) | ||
relNode = new NoInputsRelNodeWrap(relNode); | ||
|
||
log.debug(String.format("Skipped hint '%s' %sfor relation operator '%s'. %s", hint.hintName, | ||
hintOptions, RelOptUtil.toString(relNode, SqlExplainLevel.EXPPLAN_ATTRIBUTES).trim(), reason)); | ||
} | ||
} | ||
|
||
/** */ | ||
private static final class NoInputsRelNodeWrap extends AbstractRelNode { | ||
/** Original rel. */ | ||
private final RelNode rel; | ||
|
||
/** Ctor. */ | ||
private NoInputsRelNodeWrap(RelNode relNode) { | ||
super(relNode.getCluster(), relNode.getTraitSet()); | ||
|
||
this.rel = relNode; | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override public List<RelNode> getInputs() { | ||
return Collections.emptyList(); | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override protected RelDataType deriveRowType() { | ||
return rel.getRowType(); | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override public void explain(RelWriter pw) { | ||
rel.explain(pw); | ||
} | ||
} | ||
} |
Oops, something went wrong.