Skip to content

Commit 28e9b76

Browse files
alex-plekhanovzstan
authored andcommitted
IGNITE-27700 SQL Calcite: Fix merge filter to scan after trimmed fields - Fixes #12675.
Signed-off-by: Aleksey Plekhanov <plehanov.alex@gmail.com> (cherry picked from commit 77987ea)
1 parent 14361a6 commit 28e9b76

30 files changed

Lines changed: 242 additions & 190 deletions

modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteScalarFunction.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,41 @@
2626
* Implementation of {@link ScalarFunction} for Ignite user defined functions.
2727
*/
2828
public class IgniteScalarFunction extends IgniteReflectiveFunctionBase implements ScalarFunction {
29+
/** */
30+
private final boolean deterministic;
31+
2932
/**
3033
* Private constructor.
3134
*/
32-
private IgniteScalarFunction(Method method, CallImplementor implementor) {
35+
private IgniteScalarFunction(Method method, CallImplementor implementor, boolean deterministic) {
3336
super(method, implementor);
37+
38+
this.deterministic = deterministic;
3439
}
3540

3641
/**
3742
* Creates {@link ScalarFunction} from given method.
3843
*
3944
* @param method Method that is used to implement the function.
45+
* @param deterministic Is function deterministic.
4046
* @return Created {@link ScalarFunction}.
4147
*/
42-
public static ScalarFunction create(Method method) {
48+
public static ScalarFunction create(Method method, boolean deterministic) {
4349
CallImplementor implementor = RexImpTable.createImplementor(
4450
new ReflectiveCallNotNullImplementor(method), NullPolicy.NONE, false);
4551

46-
return new IgniteScalarFunction(method, implementor);
52+
return new IgniteScalarFunction(method, implementor, deterministic);
4753
}
4854

4955
/** {@inheritDoc} */
5056
@Override public RelDataType getReturnType(RelDataTypeFactory typeFactory) {
5157
return typeFactory.createJavaType(method.getReturnType());
5258
}
59+
60+
/**
61+
* @return Deterministic flag.
62+
*/
63+
public boolean isDeterministic() {
64+
return deterministic;
65+
}
5366
}

modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/RexExecutorImpl.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.lang.reflect.Modifier;
2020
import java.lang.reflect.Type;
2121
import java.util.List;
22-
2322
import com.google.common.collect.ImmutableList;
2423
import org.apache.calcite.DataContext;
2524
import org.apache.calcite.adapter.java.JavaTypeFactory;
@@ -35,14 +34,19 @@
3534
import org.apache.calcite.rel.type.RelDataType;
3635
import org.apache.calcite.rel.type.RelDataTypeFactory;
3736
import org.apache.calcite.rex.RexBuilder;
37+
import org.apache.calcite.rex.RexCall;
3838
import org.apache.calcite.rex.RexExecutable;
3939
import org.apache.calcite.rex.RexExecutor;
4040
import org.apache.calcite.rex.RexNode;
4141
import org.apache.calcite.rex.RexProgram;
4242
import org.apache.calcite.rex.RexProgramBuilder;
43+
import org.apache.calcite.rex.RexVisitor;
44+
import org.apache.calcite.rex.RexVisitorImpl;
4345
import org.apache.calcite.sql.validate.SqlConformance;
4446
import org.apache.calcite.sql.validate.SqlConformanceEnum;
47+
import org.apache.calcite.sql.validate.SqlUserDefinedFunction;
4548
import org.apache.calcite.util.BuiltInMethod;
49+
import org.apache.calcite.util.ControlFlowException;
4650
import org.apache.calcite.util.Util;
4751
import org.apache.ignite.internal.processors.query.calcite.type.IgniteCustomType;
4852

@@ -133,12 +137,34 @@ public static RexExecutable getExecutable(RexBuilder rexBuilder, List<RexNode> e
133137
* Do constant reduction using generated code.
134138
*/
135139
@Override public void reduce(RexBuilder rexBuilder, List<RexNode> constExps, List<RexNode> reducedValues) {
140+
RexVisitor<Void> nonDeterministicUdfFinder = new RexVisitorImpl<Void>(true) {
141+
@Override public Void visitCall(RexCall call) {
142+
if (call.getOperator() instanceof SqlUserDefinedFunction) {
143+
SqlUserDefinedFunction udfFunc = (SqlUserDefinedFunction)call.getOperator();
144+
145+
if (udfFunc.getFunction() instanceof IgniteScalarFunction
146+
&& !((IgniteScalarFunction)udfFunc.getFunction()).isDeterministic())
147+
throw Util.FoundOne.NULL; // Don't reduce non-deterministic UDF functions.
148+
}
149+
150+
return super.visitCall(call);
151+
}
152+
};
153+
136154
for (RexNode node : constExps) {
137155
// Do not simplify custom types, since we can't convert it to literal of this type.
138156
if (node.getType() instanceof IgniteCustomType) {
139157
reducedValues.addAll(constExps);
140158
return;
141159
}
160+
161+
try {
162+
node.accept(nonDeterministicUdfFinder);
163+
}
164+
catch (ControlFlowException foundNonDeterministic) {
165+
reducedValues.addAll(constExps);
166+
return;
167+
}
142168
}
143169
final String code = compile(rexBuilder, constExps,
144170
(list, index, storageType) -> {

modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerHelper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,10 @@ public static IgniteRel optimize(SqlNode sqlNode, IgnitePlanner planner, IgniteL
127127

128128
rel = planner.extractConjunctionOverDisjunctionCommonPart(rel);
129129

130-
rel = planner.trimUnusedFields(root.withRel(rel)).rel;
131-
132130
rel = planner.transform(PlannerPhase.HEP_FILTER_PUSH_DOWN, rel.getTraitSet(), rel);
133131

132+
rel = planner.trimUnusedFields(root.withRel(rel)).rel;
133+
134134
// The following pushed down project can erase top-level hints. We store them to reassign hints for join nodes.
135135
// Clear the inherit pathes to consider the hints as not propogated ones.
136136
List<RelHint> topHints = HintUtils.allRelHints(rel).stream().map(h -> h.inheritPath.isEmpty()

modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerPhase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ public enum PlannerPhase {
108108
@Override public RuleSet getRules(PlanningContext ctx) {
109109
return ctx.rules(
110110
RuleSets.ofList(
111+
CoreRules.FILTER_REDUCE_EXPRESSIONS,
111112
FilterScanMergeRule.TABLE_SCAN_SKIP_CORRELATED,
112113

113114
CoreRules.FILTER_MERGE,

modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/AbstractIndexScan.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.apache.calcite.rel.RelInput;
2828
import org.apache.calcite.rel.RelWriter;
2929
import org.apache.calcite.rel.metadata.RelMetadataQuery;
30+
import org.apache.calcite.rel.type.RelDataType;
3031
import org.apache.calcite.rex.RexBuilder;
3132
import org.apache.calcite.rex.RexNode;
3233
import org.apache.calcite.rex.RexUtil;
@@ -67,12 +68,13 @@ protected AbstractIndexScan(
6768
RelTraitSet traitSet,
6869
RelOptTable table,
6970
String idxName,
71+
@Nullable RelDataType rowType,
7072
@Nullable List<RexNode> proj,
7173
@Nullable RexNode cond,
7274
@Nullable List<SearchBounds> searchBounds,
7375
@Nullable ImmutableBitSet reqColumns
7476
) {
75-
super(cluster, traitSet, Collections.emptyList(), table, proj, cond, reqColumns);
77+
super(cluster, traitSet, Collections.emptyList(), table, rowType, proj, cond, reqColumns);
7678

7779
this.idxName = idxName;
7880
this.searchBounds = searchBounds;

modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteIndexScan.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.calcite.rel.RelCollation;
2525
import org.apache.calcite.rel.RelInput;
2626
import org.apache.calcite.rel.RelWriter;
27+
import org.apache.calcite.rel.type.RelDataType;
2728
import org.apache.calcite.rex.RexNode;
2829
import org.apache.calcite.util.ImmutableBitSet;
2930
import org.apache.ignite.internal.processors.query.calcite.prepare.bounds.SearchBounds;
@@ -64,6 +65,7 @@ public IgniteIndexScan(RelInput input) {
6465
* @param traits Traits of this relational expression
6566
* @param tbl Table definition.
6667
* @param idxName Index name.
68+
* @param rowType Row type.
6769
* @param proj Projects.
6870
* @param cond Filters.
6971
* @param requiredCols Participating columns.
@@ -74,13 +76,14 @@ public IgniteIndexScan(
7476
RelTraitSet traits,
7577
RelOptTable tbl,
7678
String idxName,
79+
@Nullable RelDataType rowType,
7780
@Nullable List<RexNode> proj,
7881
@Nullable RexNode cond,
7982
@Nullable List<SearchBounds> searchBounds,
8083
@Nullable ImmutableBitSet requiredCols,
8184
RelCollation collation
8285
) {
83-
this(-1L, cluster, traits, tbl, idxName, proj, cond, searchBounds, requiredCols, collation);
86+
this(-1L, cluster, traits, tbl, idxName, rowType, proj, cond, searchBounds, requiredCols, collation);
8487
}
8588

8689
/**
@@ -89,6 +92,7 @@ public IgniteIndexScan(
8992
* @param traits Traits of this relational expression
9093
* @param tbl Table definition.
9194
* @param idxName Index name.
95+
* @param rowType Row type.
9296
* @param proj Projects.
9397
* @param cond Filters.
9498
* @param requiredCols Participating colunms.
@@ -100,13 +104,14 @@ private IgniteIndexScan(
100104
RelTraitSet traits,
101105
RelOptTable tbl,
102106
String idxName,
107+
@Nullable RelDataType rowType,
103108
@Nullable List<RexNode> proj,
104109
@Nullable RexNode cond,
105110
@Nullable List<SearchBounds> searchBounds,
106111
@Nullable ImmutableBitSet requiredCols,
107112
RelCollation collation
108113
) {
109-
super(cluster, traits, tbl, idxName, proj, cond, searchBounds, requiredCols);
114+
super(cluster, traits, tbl, idxName, rowType, proj, cond, searchBounds, requiredCols);
110115

111116
this.sourceId = sourceId;
112117
this.collation = collation;
@@ -132,13 +137,13 @@ private IgniteIndexScan(
132137
/** {@inheritDoc} */
133138
@Override public IgniteRel clone(long sourceId) {
134139
return new IgniteIndexScan(sourceId, getCluster(), getTraitSet(), getTable(),
135-
idxName, projects, condition, searchBounds, requiredColumns, collation);
140+
idxName, rowType, projects, condition, searchBounds, requiredColumns, collation);
136141
}
137142

138143
/** {@inheritDoc} */
139144
@Override public IgniteRel clone(RelOptCluster cluster, List<IgniteRel> inputs) {
140145
return new IgniteIndexScan(sourceId, cluster, getTraitSet(), getTable(),
141-
idxName, projects, condition, searchBounds, requiredColumns, collation);
146+
idxName, rowType, projects, condition, searchBounds, requiredColumns, collation);
142147
}
143148

144149
/** {@inheritDoc} */

modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTableScan.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.calcite.plan.RelTraitSet;
2525
import org.apache.calcite.rel.RelInput;
2626
import org.apache.calcite.rel.RelWriter;
27+
import org.apache.calcite.rel.type.RelDataType;
2728
import org.apache.calcite.rex.RexNode;
2829
import org.apache.calcite.util.ImmutableBitSet;
2930
import org.jetbrains.annotations.Nullable;
@@ -63,14 +64,15 @@ public IgniteTableScan(
6364
RelTraitSet traits,
6465
RelOptTable tbl
6566
) {
66-
this(cluster, traits, tbl, null, null, null);
67+
this(cluster, traits, tbl, null, null, null, null);
6768
}
6869

6970
/**
7071
* Creates a TableScan.
7172
* @param cluster Cluster that this relational expression belongs to
7273
* @param traits Traits of this relational expression
7374
* @param tbl Table definition.
75+
* @param rowType Row type.
7476
* @param proj Projects.
7577
* @param cond Filters.
7678
* @param requiredColunms Participating colunms.
@@ -79,18 +81,20 @@ public IgniteTableScan(
7981
RelOptCluster cluster,
8082
RelTraitSet traits,
8183
RelOptTable tbl,
84+
@Nullable RelDataType rowType,
8285
@Nullable List<RexNode> proj,
8386
@Nullable RexNode cond,
8487
@Nullable ImmutableBitSet requiredColunms
8588
) {
86-
this(-1L, cluster, traits, tbl, proj, cond, requiredColunms);
89+
this(-1L, cluster, traits, tbl, rowType, proj, cond, requiredColunms);
8790
}
8891

8992
/**
9093
* Creates a TableScan.
9194
* @param cluster Cluster that this relational expression belongs to
9295
* @param traits Traits of this relational expression
9396
* @param tbl Table definition.
97+
* @param rowType Row type.
9498
* @param proj Projects.
9599
* @param cond Filters.
96100
* @param requiredColunms Participating colunms.
@@ -100,11 +104,12 @@ private IgniteTableScan(
100104
RelOptCluster cluster,
101105
RelTraitSet traits,
102106
RelOptTable tbl,
107+
@Nullable RelDataType rowType,
103108
@Nullable List<RexNode> proj,
104109
@Nullable RexNode cond,
105110
@Nullable ImmutableBitSet requiredColunms
106111
) {
107-
super(cluster, traits, ImmutableList.of(), tbl, proj, cond, requiredColunms);
112+
super(cluster, traits, ImmutableList.of(), tbl, rowType, proj, cond, requiredColunms);
108113
this.sourceId = sourceId;
109114
}
110115

@@ -126,11 +131,13 @@ private IgniteTableScan(
126131

127132
/** {@inheritDoc} */
128133
@Override public IgniteRel clone(long sourceId) {
129-
return new IgniteTableScan(sourceId, getCluster(), getTraitSet(), getTable(), projects, condition, requiredColumns);
134+
return new IgniteTableScan(sourceId, getCluster(), getTraitSet(), getTable(), rowType, projects, condition,
135+
requiredColumns);
130136
}
131137

132138
/** {@inheritDoc} */
133139
@Override public IgniteRel clone(RelOptCluster cluster, List<IgniteRel> inputs) {
134-
return new IgniteTableScan(sourceId, cluster, getTraitSet(), getTable(), projects, condition, requiredColumns);
140+
return new IgniteTableScan(sourceId, cluster, getTraitSet(), getTable(), rowType, projects, condition,
141+
requiredColumns);
135142
}
136143
}

modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/ProjectableFilterableTableScan.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,16 @@ protected ProjectableFilterableTableScan(
7575
RelTraitSet traitSet,
7676
List<RelHint> hints,
7777
RelOptTable table,
78+
@Nullable RelDataType rowType,
7879
@Nullable List<RexNode> proj,
7980
@Nullable RexNode cond,
8081
@Nullable ImmutableBitSet reqColumns
8182
) {
8283
super(cluster, traitSet, hints, table);
8384

85+
assert proj == null || rowType != null : "rowType should be provided if project != null";
86+
87+
this.rowType = rowType;
8488
projects = proj;
8589
condition = cond;
8690
requiredColumns = reqColumns;
@@ -90,6 +94,7 @@ protected ProjectableFilterableTableScan(
9094
protected ProjectableFilterableTableScan(RelInput input) {
9195
super(input);
9296
condition = input.getExpression("filters");
97+
rowType = input.get("rowType") == null ? null : input.getRowType("rowType");
9398
projects = input.get("projects") == null ? null : input.getExpressionList("projects");
9499
requiredColumns = input.get("requiredColumns") == null ? null : input.getBitSet("requiredColumns");
95100
}
@@ -129,6 +134,7 @@ protected RelWriter explainTerms0(RelWriter pw) {
129134
}
130135

131136
return pw
137+
.itemIf("rowType", rowType, projects != null) // Intentional project check here.
132138
.itemIf("projects", projects, projects != null)
133139
.itemIf("requiredColumns", requiredColumns, requiredColumns != null);
134140
}
@@ -151,10 +157,9 @@ protected RelWriter explainTerms0(RelWriter pw) {
151157

152158
/** {@inheritDoc} */
153159
@Override public RelDataType deriveRowType() {
154-
if (projects != null)
155-
return RexUtil.createStructType(Commons.typeFactory(getCluster()), projects);
156-
else
157-
return table.unwrap(IgniteTable.class).getRowType(Commons.typeFactory(getCluster()), requiredColumns);
160+
assert projects == null : "For merged projects rowType should be provided explicetely";
161+
162+
return table.unwrap(IgniteTable.class).getRowType(Commons.typeFactory(getCluster()), requiredColumns);
158163
}
159164

160165
/** */

modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/logical/IgniteLogicalIndexScan.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.calcite.plan.RelOptCluster;
2222
import org.apache.calcite.plan.RelOptTable;
2323
import org.apache.calcite.plan.RelTraitSet;
24+
import org.apache.calcite.rel.type.RelDataType;
2425
import org.apache.calcite.rex.RexNode;
2526
import org.apache.calcite.util.ImmutableBitSet;
2627
import org.apache.ignite.internal.processors.query.calcite.prepare.bounds.SearchBounds;
@@ -37,6 +38,7 @@ public static IgniteLogicalIndexScan create(
3738
RelTraitSet traits,
3839
RelOptTable table,
3940
String idxName,
41+
@Nullable RelDataType rowType,
4042
@Nullable List<RexNode> proj,
4143
@Nullable RexNode cond,
4244
@Nullable ImmutableBitSet requiredColumns
@@ -51,6 +53,7 @@ public static IgniteLogicalIndexScan create(
5153
traits,
5254
table,
5355
idxName,
56+
rowType,
5457
proj,
5558
cond,
5659
searchBounds,
@@ -63,6 +66,7 @@ public static IgniteLogicalIndexScan create(
6366
* @param traits Traits of this relational expression
6467
* @param tbl Table definition.
6568
* @param idxName Index name.
69+
* @param rowType Row type.
6670
* @param proj Projects.
6771
* @param cond Filters.
6872
* @param searchBounds Index search bounds.
@@ -73,11 +77,12 @@ private IgniteLogicalIndexScan(
7377
RelTraitSet traits,
7478
RelOptTable tbl,
7579
String idxName,
80+
@Nullable RelDataType rowType,
7681
@Nullable List<RexNode> proj,
7782
@Nullable RexNode cond,
7883
@Nullable List<SearchBounds> searchBounds,
7984
@Nullable ImmutableBitSet requiredCols
8085
) {
81-
super(cluster, traits, tbl, idxName, proj, cond, searchBounds, requiredCols);
86+
super(cluster, traits, tbl, idxName, rowType, proj, cond, searchBounds, requiredCols);
8287
}
8388
}

0 commit comments

Comments
 (0)