This is an automated email from the ASF dual-hosted git repository.
gvvinblade pushed a commit to branch ignite-12248
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/ignite-12248 by this push:
new 4fdf5dc IGNITE-13476: Calcite improvements. Implement ProjectionNode.
This closes #8302
4fdf5dc is described below
commit 4fdf5dcc1972b2525898312b88e0aa72df28aaef
Author: zstan <[email protected]>
AuthorDate: Fri Oct 2 16:16:57 2020 +0300
IGNITE-13476: Calcite improvements. Implement ProjectionNode. This closes
#8302
---
modules/calcite/pom.xml | 7 +-
.../processors/query/calcite/exec/IndexScan.java | 28 +++-
.../query/calcite/exec/LogicalRelImplementor.java | 31 ++++-
.../processors/query/calcite/exec/TableScan.java | 37 +++--
.../query/calcite/metadata/IgniteMdPredicates.java | 4 +-
.../processors/query/calcite/prepare/Cloner.java | 6 +-
.../query/calcite/prepare/PlannerPhase.java | 15 +-
.../query/calcite/rel/FilterableTableScan.java | 72 ----------
.../query/calcite/rel/IgniteIndexScan.java | 52 +++++--
.../query/calcite/rel/IgniteProject.java | 48 +++----
.../query/calcite/rel/IgniteTableScan.java | 28 +++-
.../rel/ProjectableFilterableTableScan.java | 136 ++++++++++++++++++
.../query/calcite/rule/ExposeIndexRule.java | 6 +-
...rIntoScanRule.java => FilterScanMergeRule.java} | 66 +++++++--
.../query/calcite/rule/ProjectScanMergeRule.java | 153 +++++++++++++++++++++
.../rule/logical/LogicalProjectMergeRule.java | 47 +++++++
.../rule/logical/LogicalProjectRemoveRule.java | 49 +++++++
.../query/calcite/schema/IgniteIndex.java | 11 +-
.../query/calcite/schema/IgniteTable.java | 26 +++-
.../query/calcite/schema/IgniteTableImpl.java | 25 ++--
.../query/calcite/schema/TableDescriptor.java | 25 +++-
.../query/calcite/schema/TableDescriptorImpl.java | 64 +++++----
.../processors/query/calcite/trait/TraitUtils.java | 25 +++-
.../processors/query/calcite/util/RexUtils.java | 37 ++++-
.../query/calcite/CalciteQueryProcessorTest.java | 3 +-
.../processors/query/calcite/PlannerTest.java | 132 +++++++++++++-----
.../processors/query/calcite/QueryChecker.java | 105 +++++++++++++-
.../processors/query/calcite/QueryCheckerTest.java | 43 ++++++
.../query/calcite/rules/OrToUnionRuleTest.java | 2 +-
.../calcite/rules/ProjectScanMergeRuleTest.java | 116 ++++++++++++++++
.../ignite/testsuites/IgniteCalciteTestSuite.java | 7 +-
31 files changed, 1159 insertions(+), 247 deletions(-)
diff --git a/modules/calcite/pom.xml b/modules/calcite/pom.xml
index 47ce428..c3452e5 100644
--- a/modules/calcite/pom.xml
+++ b/modules/calcite/pom.xml
@@ -74,12 +74,6 @@
</dependency>
<dependency>
- <groupId>org.apache.calcite</groupId>
- <artifactId>calcite-linq4j</artifactId>
- <version>${calcite.version}</version>
- </dependency>
-
- <dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
@@ -174,6 +168,7 @@
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
+
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-clients</artifactId>
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
index f6f595a..dcc178e 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
@@ -21,9 +21,11 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.cluster.ClusterTopologyException;
@@ -54,6 +56,7 @@ import org.apache.ignite.spi.indexing.IndexingQueryFilterImpl;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
/**
* Scan on index.
@@ -101,6 +104,12 @@ public class IndexScan<Row> implements Iterable<Row>,
AutoCloseable {
/** */
private volatile List<GridDhtLocalPartition> reserved;
+ /** */
+ private final Function<Row, Row> rowTransformer;
+
+ /** */
+ private final ImmutableBitSet requiredColunms;
+
/**
* @param ectx Execution context.
* @param desc Table descriptor.
@@ -115,7 +124,9 @@ public class IndexScan<Row> implements Iterable<Row>,
AutoCloseable {
GridIndex<H2Row> idx,
Predicate<Row> filters,
Supplier<Row> lowerBound,
- Supplier<Row> upperBound
+ Supplier<Row> upperBound,
+ Function<Row, Row> rowTransformer,
+ @Nullable ImmutableBitSet requiredColunms
) {
this.ectx = ectx;
this.desc = desc;
@@ -123,7 +134,7 @@ public class IndexScan<Row> implements Iterable<Row>,
AutoCloseable {
kctx = cctx.kernalContext();
coCtx = cctx.cacheObjectContext();
- RelDataType rowType = desc.selectRowType(this.ectx.getTypeFactory());
+ RelDataType rowType = desc.rowType(this.ectx.getTypeFactory(),
requiredColunms);
factory = this.ectx.rowHandler().factory(this.ectx.getTypeFactory(),
rowType);
this.idx = idx;
@@ -133,6 +144,8 @@ public class IndexScan<Row> implements Iterable<Row>,
AutoCloseable {
this.upperBound = upperBound;
partsArr = ectx.localPartitions();
mvccSnapshot = ectx.mvccSnapshot();
+ this.rowTransformer = rowTransformer;
+ this.requiredColunms = requiredColunms;
}
/** {@inheritDoc} */
@@ -314,10 +327,15 @@ public class IndexScan<Row> implements Iterable<Row>,
AutoCloseable {
while (next == null && cursor.next()) {
H2Row h2Row = cursor.get();
- Row r = desc.toRow(ectx, (CacheDataRow)h2Row, factory);
+ Row r = desc.toRow(ectx, (CacheDataRow)h2Row, factory,
requiredColunms);
+
+ if (filters != null && !filters.test(r))
+ continue;
+
+ if (rowTransformer != null)
+ r = rowTransformer.apply(r);
- if (filters == null || filters.test(r))
- next = r;
+ next = r;
}
}
}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java
index 5c01ed7..11bfd8a 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java
@@ -73,6 +73,7 @@ import
org.apache.ignite.internal.processors.query.calcite.schema.TableDescripto
import org.apache.ignite.internal.processors.query.calcite.trait.Destination;
import
org.apache.ignite.internal.processors.query.calcite.trait.DistributionFunction;
import
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribution;
+import
org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.internal.util.typedef.F;
@@ -216,16 +217,24 @@ public class LogicalRelImplementor<Row> implements
IgniteRelVisitor<Node<Row>> {
/** {@inheritDoc} */
@Override public Node<Row> visit(IgniteIndexScan rel) {
RexNode condition = rel.condition();
- Predicate<Row> filters = condition == null ? null :
expressionFactory.predicate(condition, rel.getRowType());
+ List<RexNode> projects = rel.projects();
- List<RexNode> lowerCond = rel.lowerIndexCondition();
- Supplier<Row> lower = lowerCond == null ? null :
expressionFactory.rowSource(lowerCond);
+ IgniteTable tbl = rel.getTable().unwrap(IgniteTable.class);
+ IgniteTypeFactory typeFactory = ctx.getTypeFactory();
+ ImmutableBitSet requiredColunms = rel.requiredColunms();
+ List<RexNode> lowerCond = rel.lowerIndexCondition();
List<RexNode> upperCond = rel.upperIndexCondition();
+
+ RelDataType cols = tbl.getRowType(typeFactory, requiredColunms);
+
+ Predicate<Row> filters = condition == null ? null :
expressionFactory.predicate(condition, cols);
+ Supplier<Row> lower = lowerCond == null ? null :
expressionFactory.rowSource(lowerCond);
Supplier<Row> upper = upperCond == null ? null :
expressionFactory.rowSource(upperCond);
+ Function<Row, Row> prj = projects == null ? null :
expressionFactory.project(projects, cols);
- IgniteIndex idx =
rel.getTable().unwrap(IgniteTable.class).getIndex(rel.indexName());
- Iterable<Row> rowsIter = idx.scan(ctx, filters, lower, upper);
+ IgniteIndex idx = tbl.getIndex(rel.indexName());
+ Iterable<Row> rowsIter = idx.scan(ctx, filters, lower, upper, prj,
requiredColunms);
return new ScanNode<>(ctx, rowsIter);
}
@@ -233,10 +242,18 @@ public class LogicalRelImplementor<Row> implements
IgniteRelVisitor<Node<Row>> {
/** {@inheritDoc} */
@Override public Node<Row> visit(IgniteTableScan rel) {
RexNode condition = rel.condition();
- Predicate<Row> filters = condition == null ? null :
expressionFactory.predicate(condition, rel.getRowType());
+ List<RexNode> projects = rel.projects();
+ ImmutableBitSet requiredColunms = rel.requiredColunms();
IgniteTable tbl = rel.getTable().unwrap(IgniteTable.class);
- Iterable<Row> rowsIter = tbl.scan(ctx, filters);
+ IgniteTypeFactory typeFactory = ctx.getTypeFactory();
+
+ RelDataType cols = tbl.getRowType(typeFactory, requiredColunms);
+
+ Predicate<Row> filters = condition == null ? null :
expressionFactory.predicate(condition, cols);
+ Function<Row, Row> prj = projects == null ? null :
expressionFactory.project(projects, cols);
+
+ Iterable<Row> rowsIter = tbl.scan(ctx, filters, prj, requiredColunms);
return new ScanNode<>(ctx, rowsIter);
}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/TableScan.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/TableScan.java
index c9d126e..b4690ef 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/TableScan.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/TableScan.java
@@ -24,8 +24,10 @@ import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Queue;
+import java.util.function.Function;
import java.util.function.Predicate;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cluster.ClusterTopologyException;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
@@ -41,6 +43,8 @@ import
org.apache.ignite.internal.processors.query.calcite.exec.RowHandler.RowFa
import
org.apache.ignite.internal.processors.query.calcite.schema.TableDescriptor;
import org.apache.ignite.internal.util.lang.GridCursor;
import org.apache.ignite.internal.util.lang.GridIteratorAdapter;
+import org.apache.ignite.internal.util.typedef.F;
+import org.jetbrains.annotations.Nullable;
/** */
public class TableScan<Row> implements Iterable<Row>, AutoCloseable {
@@ -72,13 +76,27 @@ public class TableScan<Row> implements Iterable<Row>,
AutoCloseable {
private volatile List<GridDhtLocalPartition> reserved;
/** */
- public TableScan(ExecutionContext<Row> ectx, TableDescriptor desc,
Predicate<Row> filters) {
+ private final Function<Row, Row> rowTransformer;
+
+ /** Participating colunms. */
+ private final ImmutableBitSet requiredColunms;
+
+ /** */
+ public TableScan(
+ ExecutionContext<Row> ectx,
+ TableDescriptor desc,
+ Predicate<Row> filters,
+ Function<Row, Row> rowTransformer,
+ @Nullable ImmutableBitSet requiredColunms
+ ) {
this.ectx = ectx;
cctx = desc.cacheContext();
this.desc = desc;
this.filters = filters;
+ this.rowTransformer = rowTransformer;
+ this.requiredColunms = requiredColunms;
- RelDataType rowType = desc.selectRowType(this.ectx.getTypeFactory());
+ RelDataType rowType = desc.rowType(this.ectx.getTypeFactory(),
requiredColunms);
factory = this.ectx.rowHandler().factory(this.ectx.getTypeFactory(),
rowType);
topVer = ectx.planningContext().topologyVersion();
@@ -170,11 +188,10 @@ public class TableScan<Row> implements Iterable<Row>,
AutoCloseable {
/** */
private synchronized void release() {
- if (reserved == null)
+ if (F.isEmpty(reserved))
return;
- for (GridDhtLocalPartition part : reserved)
- part.release();
+ reserved.forEach(GridDhtLocalPartition::release);
reserved = null;
}
@@ -192,6 +209,7 @@ public class TableScan<Row> implements Iterable<Row>,
AutoCloseable {
/** */
private Row next;
+ /** */
private IteratorImpl() {
assert reserved != null;
@@ -246,15 +264,18 @@ public class TableScan<Row> implements Iterable<Row>,
AutoCloseable {
if (!desc.match(row))
continue;
- Row r = desc.toRow(ectx, row, factory);
+ Row r = desc.toRow(ectx, row, factory, requiredColunms);
+
if (filters != null && !filters.test(r))
continue;
+ if (rowTransformer != null)
+ r = rowTransformer.apply(r);
+
next = r;
break;
- } else {
+ } else
cur = null;
- }
}
}
}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/IgniteMdPredicates.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/IgniteMdPredicates.java
index ffd92ca..cc39748 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/IgniteMdPredicates.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/IgniteMdPredicates.java
@@ -29,7 +29,7 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexShuttle;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.util.BuiltInMethod;
-import
org.apache.ignite.internal.processors.query.calcite.rel.FilterableTableScan;
+import
org.apache.ignite.internal.processors.query.calcite.rel.ProjectableFilterableTableScan;
/** */
@SuppressWarnings("unused") // actually all methods are used by runtime
generated classes
@@ -41,7 +41,7 @@ public class IgniteMdPredicates extends RelMdPredicates {
/**
* See {@link
RelMdPredicates#getPredicates(org.apache.calcite.rel.RelNode,
org.apache.calcite.rel.metadata.RelMetadataQuery)}
*/
- public RelOptPredicateList getPredicates(FilterableTableScan rel,
RelMetadataQuery mq) {
+ public RelOptPredicateList getPredicates(ProjectableFilterableTableScan
rel, RelMetadataQuery mq) {
if (rel.condition() == null)
return RelOptPredicateList.EMPTY;
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/Cloner.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/Cloner.java
index aeee7f5..44e6a08 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/Cloner.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/Cloner.java
@@ -138,12 +138,14 @@ class Cloner implements IgniteRelVisitor<IgniteRel> {
/** {@inheritDoc} */
@Override public IgniteRel visit(IgniteIndexScan rel) {
- return new IgniteIndexScan(cluster, rel.getTraitSet(), rel.getTable(),
rel.indexName(), rel.condition());
+ return new IgniteIndexScan(cluster, rel.getTraitSet(), rel.getTable(),
rel.indexName(), rel.projects(),
+ rel.condition(), rel.requiredColunms());
}
/** {@inheritDoc} */
@Override public IgniteRel visit(IgniteTableScan rel) {
- return new IgniteTableScan(cluster, rel.getTraitSet(), rel.getTable(),
rel.condition());
+ return new IgniteTableScan(cluster, rel.getTraitSet(), rel.getTable(),
rel.projects(), rel.condition(),
+ rel.requiredColunms());
}
/** {@inheritDoc} */
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerPhase.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerPhase.java
index ef2e20b..b8d04d2 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerPhase.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerPhase.java
@@ -18,7 +18,6 @@
package org.apache.ignite.internal.processors.query.calcite.prepare;
import org.apache.calcite.rel.rules.ProjectFilterTransposeRule;
-import org.apache.calcite.rel.rules.ProjectMergeRule;
import org.apache.calcite.rel.rules.SortRemoveRule;
import org.apache.calcite.rel.rules.SubQueryRemoveRule;
import org.apache.calcite.rel.rules.UnionMergeRule;
@@ -29,9 +28,10 @@ import
org.apache.ignite.internal.processors.query.calcite.rule.AggregateConvert
import
org.apache.ignite.internal.processors.query.calcite.rule.CorrelatedNestedLoopJoinConverterRule;
import
org.apache.ignite.internal.processors.query.calcite.rule.ExposeIndexRule;
import
org.apache.ignite.internal.processors.query.calcite.rule.FilterConverterRule;
+import
org.apache.ignite.internal.processors.query.calcite.rule.FilterScanMergeRule;
import
org.apache.ignite.internal.processors.query.calcite.rule.NestedLoopJoinConverterRule;
import
org.apache.ignite.internal.processors.query.calcite.rule.ProjectConverterRule;
-import
org.apache.ignite.internal.processors.query.calcite.rule.PushFilterIntoScanRule;
+import
org.apache.ignite.internal.processors.query.calcite.rule.ProjectScanMergeRule;
import
org.apache.ignite.internal.processors.query.calcite.rule.SortConverterRule;
import
org.apache.ignite.internal.processors.query.calcite.rule.TableModifyConverterRule;
import
org.apache.ignite.internal.processors.query.calcite.rule.UnionConverterRule;
@@ -40,6 +40,8 @@ import
org.apache.ignite.internal.processors.query.calcite.rule.logical.FilterJo
import
org.apache.ignite.internal.processors.query.calcite.rule.logical.LogicalFilterMergeRule;
import
org.apache.ignite.internal.processors.query.calcite.rule.logical.LogicalFilterProjectTransposeRule;
import
org.apache.ignite.internal.processors.query.calcite.rule.logical.LogicalOrToUnionRule;
+import
org.apache.ignite.internal.processors.query.calcite.rule.logical.LogicalProjectMergeRule;
+import
org.apache.ignite.internal.processors.query.calcite.rule.logical.LogicalProjectRemoveRule;
import static
org.apache.ignite.internal.processors.query.calcite.prepare.IgnitePrograms.cbo;
import static
org.apache.ignite.internal.processors.query.calcite.prepare.IgnitePrograms.hep;
@@ -77,13 +79,16 @@ public enum PlannerPhase {
FilterJoinRule.PUSH_JOIN_CONDITION,
FilterJoinRule.FILTER_ON_JOIN,
ProjectConverterRule.INSTANCE,
- ProjectMergeRule.INSTANCE,
+ LogicalProjectMergeRule.INSTANCE,
+ LogicalProjectRemoveRule.INSTANCE,
+ ProjectScanMergeRule.TABLE_SCAN,
+ ProjectScanMergeRule.INDEX_SCAN,
FilterConverterRule.INSTANCE,
LogicalFilterMergeRule.INSTANCE,
LogicalFilterProjectTransposeRule.INSTANCE,
+ FilterScanMergeRule.TABLE_SCAN,
+ FilterScanMergeRule.INDEX_SCAN,
TableModifyConverterRule.INSTANCE,
- PushFilterIntoScanRule.FILTER_INTO_INDEX_SCAN,
- PushFilterIntoScanRule.FILTER_INTO_TABLE_SCAN,
ProjectFilterTransposeRule.INSTANCE,
LogicalOrToUnionRule.INSTANCE,
UnionMergeRule.INSTANCE,
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/FilterableTableScan.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/FilterableTableScan.java
deleted file mode 100644
index 87a933a..0000000
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/FilterableTableScan.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package org.apache.ignite.internal.processors.query.calcite.rel;
-
-import java.util.List;
-import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptCost;
-import org.apache.calcite.plan.RelOptPlanner;
-import org.apache.calcite.plan.RelOptTable;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelInput;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.RelWriter;
-import org.apache.calcite.rel.core.TableScan;
-import org.apache.calcite.rel.hint.RelHint;
-import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.rex.RexNode;
-import org.jetbrains.annotations.Nullable;
-
-public class FilterableTableScan extends TableScan {
- /** */
- protected final RexNode cond;
-
- /** */
- public FilterableTableScan(RelOptCluster cluster, RelTraitSet traitSet,
- List<RelHint> hints, RelOptTable table, @Nullable RexNode cond) {
- super(cluster, traitSet, hints, table);
- this.cond = cond;
- }
-
- /** */
- public FilterableTableScan(RelInput input) {
- super(input);
- cond = input.getExpression("filters");
- }
-
- /** */
- public RexNode condition() {
- return cond;
- }
-
- /** {@inheritDoc} */
- @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
- assert inputs.isEmpty();
-
- return this;
- }
-
- /** {@inheritDoc} */
- @Override public RelWriter explainTerms(RelWriter pw) {
- return explainTerms0(super.explainTerms(pw));
- }
-
- /** */
- protected RelWriter explainTerms0(RelWriter pw) {
- return pw.itemIf("filters", cond, cond != null);
- }
-
- /** {@inheritDoc} */
- @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
RelMetadataQuery mq) {
- double tableRows = table.getRowCount();
- return planner.getCostFactory().makeCost(tableRows, 0, 0);
- }
-
- /** {@inheritDoc} */
- @Override public double estimateRowCount(RelMetadataQuery mq) {
- double rows = table.getRowCount();
-
- if (cond != null)
- rows *= mq.getSelectivity(this, cond);
-
- return rows;
- }
-}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteIndexScan.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteIndexScan.java
index aa31932..daaf28b 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteIndexScan.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteIndexScan.java
@@ -49,6 +49,7 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils;
import org.apache.ignite.internal.util.typedef.F;
@@ -67,7 +68,7 @@ import static
org.apache.ignite.internal.processors.query.calcite.util.RexUtils.
/**
* Relational operator that returns the contents of a table.
*/
-public class IgniteIndexScan extends FilterableTableScan implements IgniteRel {
+public class IgniteIndexScan extends ProjectableFilterableTableScan implements
IgniteRel {
/** Supported index operations. */
public static final Set<SqlKind> TREE_INDEX_COMPARISON =
EnumSet.of(
@@ -109,16 +110,36 @@ public class IgniteIndexScan extends FilterableTableScan
implements IgniteRel {
* @param traits Traits of this relational expression
* @param tbl Table definition.
* @param idxName Index name.
- * @param cond Filters for scan.
+ */
+ public IgniteIndexScan(
+ RelOptCluster cluster,
+ RelTraitSet traits,
+ RelOptTable tbl,
+ String idxName) {
+ this(cluster, traits, tbl, idxName, null, null, null);
+ }
+
+ /**
+ * Creates a TableScan.
+ * @param cluster Cluster that this relational expression belongs to
+ * @param traits Traits of this relational expression
+ * @param tbl Table definition.
+ * @param idxName Index name.
+ * @param proj Projects.
+ * @param cond Filters.
+ * @param requiredColunms Participating colunms.
*/
public IgniteIndexScan(
RelOptCluster cluster,
RelTraitSet traits,
RelOptTable tbl,
String idxName,
- @Nullable RexNode cond
+ @Nullable List<RexNode> proj,
+ @Nullable RexNode cond,
+ @Nullable ImmutableBitSet requiredColunms
) {
- super(cluster, traits, ImmutableList.of(), tbl, cond);
+ super(cluster, traits, ImmutableList.of(), tbl, proj, cond,
+ requiredColunms);
this.idxName = idxName;
RelCollation coll = TraitUtils.collation(traits);
@@ -135,7 +156,7 @@ public class IgniteIndexScan extends FilterableTableScan
implements IgniteRel {
if (!boundsArePossible())
return;
- assert cond != null;
+ assert condition() != null;
Map<Integer, List<RexCall>> fieldsToPredicates =
mapPredicatesToFields();
@@ -222,7 +243,7 @@ public class IgniteIndexScan extends FilterableTableScan
implements IgniteRel {
/** */
private Map<Integer, List<RexCall>> mapPredicatesToFields() {
- List<RexNode> predicatesConjunction = RelOptUtil.conjunctions(cond);
+ List<RexNode> predicatesConjunction =
RelOptUtil.conjunctions(condition());
Map<Integer, List<RexCall>> fieldsToPredicates = new
HashMap<>(predicatesConjunction.size());
@@ -252,10 +273,10 @@ public class IgniteIndexScan extends FilterableTableScan
implements IgniteRel {
/** */
private boolean boundsArePossible() {
- if (cond == null)
+ if (condition() == null)
return false;
- RexCall dnf = (RexCall)RexUtil.toDnf(getCluster().getRexBuilder(),
cond);
+ RexCall dnf = (RexCall)RexUtil.toDnf(getCluster().getRexBuilder(),
condition());
if (dnf.isA(OR) && dnf.getOperands().size() > 1) // OR conditions are
not supported yet.
return false;
@@ -354,9 +375,11 @@ public class IgniteIndexScan extends FilterableTableScan
implements IgniteRel {
/** {@inheritDoc} */
@Override protected RelWriter explainTerms0(RelWriter pw) {
- return pw.item("index", idxName )
- .item("collation", collation)
- .itemIf("filters", cond, cond != null)
+ pw = pw
+ .item("index", idxName)
+ .item("collation", collation);
+ pw = super.explainTerms0(pw);
+ return pw
.itemIf("lower", lowerIdxCond, !F.isEmpty(lowerIdxCond))
.itemIf("upper", upperIdxCond, !F.isEmpty(upperIdxCond));
}
@@ -377,6 +400,9 @@ public class IgniteIndexScan extends FilterableTableScan
implements IgniteRel {
@Override public RelOptCost computeSelfCost(RelOptPlanner planner,
RelMetadataQuery mq) {
double tableRows = table.getRowCount() * idxSelectivity;
+ if (projects() != null)
+ tableRows += tableRows * projects().size();
+
tableRows = RelMdUtil.addEpsilon(tableRows);
return planner.getCostFactory().makeCost(tableRows, 0, 0);
@@ -386,8 +412,8 @@ public class IgniteIndexScan extends FilterableTableScan
implements IgniteRel {
@Override public double estimateRowCount(RelMetadataQuery mq) {
double rows = table.getRowCount() * idxSelectivity;
- if (cond != null)
- rows *= mq.getSelectivity(this, cond);
+ if (condition() != null)
+ rows *= mq.getSelectivity(this, condition());
return rows;
}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteProject.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteProject.java
index 5864bcf..1fd1e56 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteProject.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteProject.java
@@ -21,10 +21,11 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-
import com.google.common.collect.ImmutableList;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelCollations;
@@ -32,6 +33,8 @@ import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.metadata.RelMdUtil;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexNode;
@@ -109,6 +112,8 @@ public class IgniteProject extends Project implements
TraitsAwareIgniteRel {
ImmutableIntList keys = distribution.getKeys();
List<Integer> srcKeys = new ArrayList<>(keys.size());
+ distribution = distribution.apply(mapping);
+
for (int key : keys) {
int src = mapping.getSourceOpt(key);
@@ -170,47 +175,28 @@ public class IgniteProject extends Project implements
TraitsAwareIgniteRel {
/** {@inheritDoc} */
@Override public List<Pair<RelTraitSet, List<RelTraitSet>>>
deriveDistribution(RelTraitSet nodeTraits, List<RelTraitSet> inputTraits) {
- // All distribution types except hash distribution are propagated as
is.
- // In case of hash distribution we need to project distribution keys.
- // In case one of distribution keys is erased by projection result
distribution
- // becomes random since we cannot determine where data is without
erased key.
-
RelTraitSet in = inputTraits.get(0);
- IgniteDistribution distribution = TraitUtils.distribution(in);
-
- if (distribution.getType() == HASH_DISTRIBUTED) {
- Mappings.TargetMapping mapping = Project.getPartialMapping(
- input.getRowType().getFieldCount(), getProjects());
- return
ImmutableList.of(Pair.of(nodeTraits.replace(distribution.apply(mapping)),
ImmutableList.of(in)));
- }
+ IgniteDistribution distribution = TraitUtils.projectDistribution(
+ TraitUtils.distribution(in), getProjects(),
getInput().getRowType());
return ImmutableList.of(Pair.of(nodeTraits.replace(distribution),
ImmutableList.of(in)));
}
/** {@inheritDoc} */
@Override public List<Pair<RelTraitSet, List<RelTraitSet>>>
deriveCollation(RelTraitSet nodeTraits, List<RelTraitSet> inputTraits) {
- // The code below projects input collation.
-
RelTraitSet in = inputTraits.get(0);
- RelCollation collation = TraitUtils.collation(in);
- if (collation.getFieldCollations().isEmpty())
- return
ImmutableList.of(Pair.of(nodeTraits.replace(RelCollations.EMPTY),
ImmutableList.of(in)));
+ RelCollation collation = TraitUtils.projectCollation(
+ TraitUtils.collation(in), getProjects(), getInput().getRowType());
- Map<Integer, Integer> targets = new HashMap<>();
- for (Ord<RexNode> project : Ord.zip(getProjects())) {
- if (project.e instanceof RexInputRef)
- targets.putIfAbsent(((RexInputRef)project.e).getIndex(),
project.i);
- }
-
- List<RelFieldCollation> outFieldCollations = new ArrayList<>();
- for (RelFieldCollation inFieldCollation :
collation.getFieldCollations()) {
- Integer newIndex = targets.get(inFieldCollation.getFieldIndex());
- if (newIndex != null)
-
outFieldCollations.add(inFieldCollation.withFieldIndex(newIndex));
- }
+ return ImmutableList.of(Pair.of(nodeTraits.replace(collation),
ImmutableList.of(in)));
+ }
- return
ImmutableList.of(Pair.of(nodeTraits.replace(RelCollations.of(outFieldCollations)),
ImmutableList.of(in)));
+ /** {@inheritDoc} */
+ @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
RelMetadataQuery mq) {
+ double rowCount = mq.getRowCount(getInput()) * exps.size();
+ rowCount = RelMdUtil.addEpsilon(rowCount); // to differ from rel nodes
with integrated projection
+ return planner.getCostFactory().makeCost(rowCount, 0, 0);
}
}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTableScan.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTableScan.java
index a25623e..14734f2 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTableScan.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTableScan.java
@@ -17,12 +17,14 @@
package org.apache.ignite.internal.processors.query.calcite.rel;
+import java.util.List;
import com.google.common.collect.ImmutableList;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.ImmutableBitSet;
import org.jetbrains.annotations.Nullable;
import static
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.changeTraits;
@@ -30,7 +32,7 @@ import static
org.apache.ignite.internal.processors.query.calcite.trait.TraitUti
/**
* Relational operator that returns the contents of a table.
*/
-public class IgniteTableScan extends FilterableTableScan implements IgniteRel {
+public class IgniteTableScan extends ProjectableFilterableTableScan implements
IgniteRel {
/**
* Constructor used for deserialization.
*
@@ -49,9 +51,29 @@ public class IgniteTableScan extends FilterableTableScan
implements IgniteRel {
public IgniteTableScan(
RelOptCluster cluster,
RelTraitSet traits,
+ RelOptTable tbl
+ ) {
+ super(cluster, traits, ImmutableList.of(), tbl);
+ }
+
+ /**
+ * Creates a TableScan.
+ * @param cluster Cluster that this relational expression belongs to
+ * @param traits Traits of this relational expression
+ * @param tbl Table definition.
+ * @param proj Projects.
+ * @param cond Filters.
+ * @param requiredColunms Participating colunms.
+ */
+ public IgniteTableScan(
+ RelOptCluster cluster,
+ RelTraitSet traits,
RelOptTable tbl,
- @Nullable RexNode cond) {
- super(cluster, traits, ImmutableList.of(), tbl, cond);
+ @Nullable List<RexNode> proj,
+ @Nullable RexNode cond,
+ @Nullable ImmutableBitSet requiredColunms
+ ) {
+ super(cluster, traits, ImmutableList.of(), tbl, proj, cond,
requiredColunms);
}
/** {@inheritDoc} */
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/ProjectableFilterableTableScan.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/ProjectableFilterableTableScan.java
new file mode 100644
index 0000000..97e068d
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/ProjectableFilterableTableScan.java
@@ -0,0 +1,136 @@
+package org.apache.ignite.internal.processors.query.calcite.rel;
+
+import java.util.List;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelInput;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelWriter;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.hint.RelHint;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
+import org.apache.ignite.internal.processors.query.calcite.util.Commons;
+import org.jetbrains.annotations.Nullable;
+
+/** Scan with projects and filters. */
+public class ProjectableFilterableTableScan extends TableScan {
+ /** Filters. */
+ private final RexNode condition;
+
+ /** Projects. */
+ private final List<RexNode> projects;
+
+ /** Participating colunms. */
+ private final ImmutableBitSet requiredColunms;
+
+ /** */
+ public ProjectableFilterableTableScan(
+ RelOptCluster cluster,
+ RelTraitSet traitSet,
+ List<RelHint> hints,
+ RelOptTable tbl
+ ) {
+ this(cluster, traitSet, hints, tbl, null, null, null);
+ }
+
+ /** */
+ public ProjectableFilterableTableScan(
+ RelOptCluster cluster,
+ RelTraitSet traitSet,
+ List<RelHint> hints,
+ RelOptTable table,
+ @Nullable List<RexNode> proj,
+ @Nullable RexNode cond,
+ @Nullable ImmutableBitSet reqColunms
+ ) {
+ super(cluster, traitSet, hints, table);
+
+ projects = proj;
+ condition = cond;
+ requiredColunms = reqColunms;
+ }
+
+ /** */
+ public ProjectableFilterableTableScan(RelInput input) {
+ super(input);
+ condition = input.getExpression("filters");
+ projects = input.get("projects") == null ? null :
input.getExpressionList("projects");
+ requiredColunms = input.get("requiredColunms") == null ? null :
input.getBitSet("requiredColunms");
+ }
+
+ /** @return Projections. */
+ public List<RexNode> projects() {
+ return projects;
+ }
+
+ /** @return Rex condition. */
+ public RexNode condition() {
+ return condition;
+ }
+
+ /** @return Participating colunms. */
+ public ImmutableBitSet requiredColunms() {
+ return requiredColunms;
+ }
+
+ /** {@inheritDoc} */
+ @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+ assert inputs.isEmpty();
+
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public RelWriter explainTerms(RelWriter pw) {
+ return explainTerms0(super.explainTerms(pw));
+ }
+
+ /** */
+ protected RelWriter explainTerms0(RelWriter pw) {
+ return pw
+ .itemIf("filters", condition, condition != null)
+ .itemIf("projects", projects, projects != null)
+ .itemIf("requiredColunms", requiredColunms, requiredColunms !=
null);
+ }
+
+ /** {@inheritDoc} */
+ @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
RelMetadataQuery mq) {
+ double tableRows = table.getRowCount();
+
+ if (projects != null)
+ tableRows += tableRows * projects.size();
+
+ return planner.getCostFactory().makeCost(tableRows, 0, 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override public double estimateRowCount(RelMetadataQuery mq) {
+ double rows = table.getRowCount();
+
+ if (condition != null)
+ rows *= mq.getSelectivity(this, condition);
+
+ return rows;
+ }
+
+ /** {@inheritDoc} */
+ @Override public RelDataType deriveRowType() {
+ if (projects != null)
+ return
RexUtil.createStructType(Commons.context(this).typeFactory(), projects);
+ else
+ return
table.unwrap(IgniteTable.class).getRowType(getCluster().getTypeFactory(),
requiredColunms);
+ }
+
+ /** */
+ public boolean simple() {
+ return condition == null && projects == null && requiredColunms ==
null;
+ }
+}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ExposeIndexRule.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ExposeIndexRule.java
index 46c9407..1dccef2 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ExposeIndexRule.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ExposeIndexRule.java
@@ -45,8 +45,8 @@ public class ExposeIndexRule extends RelOptRule {
/** */
private static boolean preMatch(IgniteTableScan scan) {
- return scan.getTable().unwrap(IgniteTable.class).indexes().size() > 1
// has indexes to expose
- && scan.condition() == null;
// was not modified by PushFilterIntoScanRule
+ return scan.simple() // was not modified by ProjectScanMergeRule or
FilterScanMergeRule
+ && scan.getTable().unwrap(IgniteTable.class).indexes().size() > 1;
// has indexes to expose
}
/** {@inheritDoc} */
@@ -63,7 +63,7 @@ public class ExposeIndexRule extends RelOptRule {
assert indexes.size() > 1;
- Map<RelNode, RelNode> equivMap = new HashMap<>();
+ Map<RelNode, RelNode> equivMap = new HashMap<>(indexes.size() - 1);
for (int i = 1; i < indexes.size(); i++)
equivMap.put(indexes.get(i), scan);
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/PushFilterIntoScanRule.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/FilterScanMergeRule.java
similarity index 62%
rename from
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/PushFilterIntoScanRule.java
rename to
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/FilterScanMergeRule.java
index ca30dbe..e5f071a 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/PushFilterIntoScanRule.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/FilterScanMergeRule.java
@@ -17,7 +17,9 @@
package org.apache.ignite.internal.processors.query.calcite.rule;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptRuleCall;
@@ -25,15 +27,22 @@ import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.RelFactories;
import org.apache.calcite.rel.logical.LogicalFilter;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexLocalRef;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexShuttle;
import org.apache.calcite.rex.RexSimplify;
import org.apache.calcite.rex.RexUtil;
-import
org.apache.ignite.internal.processors.query.calcite.rel.FilterableTableScan;
+import org.apache.calcite.util.ControlFlowException;
+import org.apache.calcite.util.mapping.MappingType;
+import org.apache.calcite.util.mapping.Mappings;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteIndexScan;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTableScan;
+import
org.apache.ignite.internal.processors.query.calcite.rel.ProjectableFilterableTableScan;
+import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
+import
org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
+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.util.RexUtils.builder;
@@ -42,22 +51,23 @@ import static
org.apache.ignite.internal.processors.query.calcite.util.RexUtils.
/**
* Rule that pushes filter into the scan. This might be useful for index range
scans.
*/
-public abstract class PushFilterIntoScanRule<T extends FilterableTableScan>
extends RelOptRule {
+public abstract class FilterScanMergeRule<T extends
ProjectableFilterableTableScan> extends RelOptRule {
/** Instance. */
- public static final PushFilterIntoScanRule<IgniteIndexScan>
FILTER_INTO_INDEX_SCAN =
- new PushFilterIntoScanRule<IgniteIndexScan>(LogicalFilter.class,
IgniteIndexScan.class, "PushFilterIntoIndexScanRule") {
+ public static final FilterScanMergeRule<IgniteIndexScan> INDEX_SCAN =
+ new FilterScanMergeRule<IgniteIndexScan>(LogicalFilter.class,
IgniteIndexScan.class, "FilterIndexScanMergeRule") {
/** {@inheritDoc} */
@Override protected IgniteIndexScan createNode(RelOptCluster
cluster, IgniteIndexScan scan, RexNode cond) {
- return new IgniteIndexScan(cluster, scan.getTraitSet(),
scan.getTable(), scan.indexName(), cond);
+ return new IgniteIndexScan(cluster, scan.getTraitSet(),
scan.getTable(), scan.indexName(),
+ scan.projects(), cond, scan.requiredColunms());
}
};
/** Instance. */
- public static final PushFilterIntoScanRule<IgniteTableScan>
FILTER_INTO_TABLE_SCAN =
- new PushFilterIntoScanRule<IgniteTableScan>(LogicalFilter.class,
IgniteTableScan.class, "PushFilterIntoTableScanRule") {
+ public static final FilterScanMergeRule<IgniteTableScan> TABLE_SCAN =
+ new FilterScanMergeRule<IgniteTableScan>(LogicalFilter.class,
IgniteTableScan.class, "FilterTableScanMergeRule") {
/** {@inheritDoc} */
@Override protected IgniteTableScan createNode(RelOptCluster
cluster, IgniteTableScan scan, RexNode cond) {
- return new IgniteTableScan(cluster, scan.getTraitSet(),
scan.getTable(), cond);
+ return new IgniteTableScan(cluster, scan.getTraitSet(),
scan.getTable(), scan.projects(), cond, scan.requiredColunms());
}
};
@@ -67,7 +77,7 @@ public abstract class PushFilterIntoScanRule<T extends
FilterableTableScan> exte
* @param clazz Class of relational expression to match.
* @param desc Description, or null to guess description
*/
- private PushFilterIntoScanRule(Class<? extends RelNode> clazz, Class<T>
tableClass, String desc) {
+ private FilterScanMergeRule(Class<? extends RelNode> clazz, Class<T>
tableClass, String desc) {
super(operand(clazz,
operand(tableClass, none())),
RelFactories.LOGICAL_BUILDER,
@@ -84,6 +94,32 @@ public abstract class PushFilterIntoScanRule<T extends
FilterableTableScan> exte
RexNode cond = filter.getCondition();
+ if (scan.projects() != null) {
+ IgniteTypeFactory typeFactory =
Commons.context(scan).typeFactory();
+
+ IgniteTable tbl = scan.getTable().unwrap(IgniteTable.class);
+
+ RelDataType cols = tbl.getRowType(typeFactory,
scan.requiredColunms());
+
+ Mappings.TargetMapping permutation = permutation(scan.projects(),
cols.getFieldCount());
+
+ try {
+ cond = new RexShuttle() {
+ @Override public RexNode visitLocalRef(RexLocalRef ref) {
+ int targetRef =
permutation.getTargetOpt(ref.getIndex());
+
+ if (targetRef == -1)
+ throw new ControlFlowException();
+
+ return new RexLocalRef(targetRef, ref.getType());
+ }
+ }.apply(cond);
+ }
+ catch (ControlFlowException e) {
+ return;
+ }
+ }
+
RexSimplify simplifier = simplifier(cluster);
// Let's remove from the condition common with the scan filter parts.
@@ -117,4 +153,16 @@ public abstract class PushFilterIntoScanRule<T extends
FilterableTableScan> exte
return new RexLocalRef(inputRef.getIndex(), inputRef.getType());
}
}
+
+ /** */
+ private static Mappings.TargetMapping permutation(List<RexNode> nodes, int
totalSize) {
+ final Mappings.TargetMapping mapping =
+ Mappings.create(MappingType.PARTIAL_FUNCTION, nodes.size(),
totalSize);
+
+ for (Ord<RexNode> node : Ord.zip(nodes)) {
+ if (node.e instanceof RexLocalRef)
+ mapping.set(node.i, ((RexLocalRef) node.e).getIndex());
+ }
+ return mapping;
+ }
}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ProjectScanMergeRule.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ProjectScanMergeRule.java
new file mode 100644
index 0000000..d7eebd9
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ProjectScanMergeRule.java
@@ -0,0 +1,153 @@
+/*
+ * 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.rule;
+
+import java.util.List;
+import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.RelFactories;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLocalRef;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
+import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.mapping.MappingType;
+import org.apache.calcite.util.mapping.Mappings;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteIndexScan;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTableScan;
+import
org.apache.ignite.internal.processors.query.calcite.rel.ProjectableFilterableTableScan;
+import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
+import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils;
+import
org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
+import org.apache.ignite.internal.processors.query.calcite.util.Commons;
+
+import static
org.apache.ignite.internal.processors.query.calcite.util.RexUtils.isIdentity;
+
+/** */
+public abstract class ProjectScanMergeRule<T extends
ProjectableFilterableTableScan> extends RelOptRule {
+ /** Instance. */
+ public static final ProjectScanMergeRule<IgniteIndexScan> INDEX_SCAN =
+ new ProjectScanMergeRule<IgniteIndexScan>(LogicalProject.class,
IgniteIndexScan.class, "ProjectIndexScanMergeRule") {
+ /** {@inheritDoc} */
+ @Override protected IgniteIndexScan createNode(RelOptCluster
cluster, IgniteIndexScan scan,
+ RelTraitSet traits, List<RexNode> projections, RexNode cond,
ImmutableBitSet requiredColunms) {
+ return new IgniteIndexScan(cluster, traits, scan.getTable(),
scan.indexName(), projections,
+ cond, requiredColunms);
+ }
+ };
+
+ /** Instance. */
+ public static final ProjectScanMergeRule<IgniteTableScan> TABLE_SCAN =
+ new ProjectScanMergeRule<IgniteTableScan>(LogicalProject.class,
IgniteTableScan.class, "ProjectTableScanMergeRule") {
+ /** {@inheritDoc} */
+ @Override protected IgniteTableScan createNode(RelOptCluster
cluster, IgniteTableScan scan,
+ RelTraitSet traits, List<RexNode> projections, RexNode cond,
ImmutableBitSet requiredColunms) {
+ return new IgniteTableScan(cluster, traits, scan.getTable(),
projections, cond, requiredColunms);
+ }
+ };
+
+ /** */
+ protected abstract T createNode(RelOptCluster cluster, T scan, RelTraitSet
traits, List<RexNode> projections,
+ RexNode cond, ImmutableBitSet
requiredColunms);
+
+ /**
+ * Constructor.
+ *
+ * @param projectionClazz Projection class of relational expression to
match.
+ * @param tableClass Ignite scan class.
+ * @param desc Description, or null to guess description
+ */
+ private ProjectScanMergeRule(Class<? extends RelNode> projectionClazz,
Class<T> tableClass, String desc) {
+ super(operand(projectionClazz,
+ operand(tableClass, none())),
+ RelFactories.LOGICAL_BUILDER, desc);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean matches(RelOptRuleCall call) {
+ T rel = call.rel(1);
+ return rel.projects() == null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onMatch(RelOptRuleCall call) {
+ LogicalProject relProject = call.rel(0);
+ T scan = call.rel(1);
+
+ RelOptCluster cluster = scan.getCluster();
+ List<RexNode> projects = relProject.getProjects();
+ RexNode cond = scan.condition();
+
+ // projection changes input collation and distribution.
+ RelTraitSet traits = scan.getTraitSet();
+
+ traits = traits.replace(TraitUtils.projectCollation(
+ TraitUtils.collation(traits), projects, scan.getRowType()));
+
+ traits = traits.replace(TraitUtils.projectDistribution(
+ TraitUtils.distribution(traits), projects, scan.getRowType()));
+
+ IgniteTable tbl = scan.getTable().unwrap(IgniteTable.class);
+ IgniteTypeFactory typeFactory = Commons.context(scan).typeFactory();
+ ImmutableBitSet.Builder builder = ImmutableBitSet.builder();
+
+ new RexShuttle() {
+ @Override public RexNode visitInputRef(RexInputRef ref) {
+ builder.set(ref.getIndex());
+ return ref;
+ }
+ }.apply(projects);
+
+ new RexShuttle() {
+ @Override public RexNode visitLocalRef(RexLocalRef inputRef) {
+ builder.set(inputRef.getIndex());
+ return inputRef;
+ }
+ }.apply(cond);
+
+ ImmutableBitSet requiredColunms = builder.build();
+
+ Mappings.TargetMapping targetMapping =
Mappings.create(MappingType.PARTIAL_FUNCTION,
+ tbl.getRowType(typeFactory).getFieldCount(),
requiredColunms.cardinality());
+
+ for (Ord<Integer> ord : Ord.zip(requiredColunms))
+ targetMapping.set(ord.e, ord.i);
+
+ projects = new RexShuttle() {
+ @Override public RexNode visitInputRef(RexInputRef ref) {
+ return new
RexLocalRef(targetMapping.getTarget(ref.getIndex()), ref.getType());
+ }
+ }.apply(projects);
+
+ if (isIdentity(projects, tbl.getRowType(typeFactory, requiredColunms),
true))
+ projects = null;
+
+ cond = new RexShuttle() {
+ @Override public RexNode visitLocalRef(RexLocalRef ref) {
+ return new
RexLocalRef(targetMapping.getTarget(ref.getIndex()), ref.getType());
+ }
+ }.apply(cond);
+
+ call.transformTo(createNode(cluster, scan, traits, projects, cond,
requiredColunms));
+ }
+}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectMergeRule.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectMergeRule.java
new file mode 100644
index 0000000..be69085
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectMergeRule.java
@@ -0,0 +1,47 @@
+/*
+ * 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.rule.logical;
+
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.rel.core.RelFactories;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.rules.ProjectMergeRule;
+import org.apache.calcite.rel.rules.TransformationRule;
+
+/**
+ * LogicalProjectMergeRule merges a {@link LogicalProject} into another {@link
LogicalProject},
+ * provided the projects aren't projecting identical sets of input references.
+ */
+public class LogicalProjectMergeRule extends RelOptRule implements
TransformationRule {
+ /** */
+ public static final RelOptRule INSTANCE = new LogicalProjectMergeRule();
+
+ /** */
+ private LogicalProjectMergeRule() {
+ super(
+ operand(LogicalProject.class,
+ operand(LogicalProject.class, any())),
+ RelFactories.LOGICAL_BUILDER, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onMatch(RelOptRuleCall call) {
+ ProjectMergeRule.INSTANCE.onMatch(call);
+ }
+}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectRemoveRule.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectRemoveRule.java
new file mode 100644
index 0000000..aacb848
--- /dev/null
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectRemoveRule.java
@@ -0,0 +1,49 @@
+/*
+ * 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.rule.logical;
+
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.rel.core.RelFactories;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.rules.ProjectRemoveRule;
+import org.apache.calcite.rel.rules.SubstitutionRule;
+
+/**
+ * Rule that, given a {@link LogicalProject} node that
+* merely returns its input, converts the node into its child.
+*/
+public class LogicalProjectRemoveRule extends RelOptRule implements
SubstitutionRule {
+ /** */
+ public static final RelOptRule INSTANCE = new LogicalProjectRemoveRule();
+
+ /** Constructor. */
+ public LogicalProjectRemoveRule() {
+ super(operandJ(LogicalProject.class, null,
ProjectRemoveRule::isTrivial, any()), RelFactories.LOGICAL_BUILDER, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onMatch(RelOptRuleCall call) {
+ ProjectRemoveRule.INSTANCE.onMatch(call);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean autoPruneOld() {
+ return true;
+ }
+}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java
index 7723337..4f3ba69 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java
@@ -16,13 +16,16 @@
*/
package org.apache.ignite.internal.processors.query.calcite.schema;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.internal.processors.query.GridIndex;
import
org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
import org.apache.ignite.internal.processors.query.calcite.exec.IndexScan;
import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
+import org.jetbrains.annotations.Nullable;
/**
* Ignite scannable index.
@@ -73,7 +76,11 @@ public class IgniteIndex {
ExecutionContext<Row> execCtx,
Predicate<Row> filters,
Supplier<Row> lowerIdxConditions,
- Supplier<Row> upperIdxConditions) {
- return new IndexScan<>(execCtx, table().descriptor(), idx, filters,
lowerIdxConditions, upperIdxConditions);
+ Supplier<Row> upperIdxConditions,
+ Function<Row, Row> rowTransformer,
+ @Nullable ImmutableBitSet requiredColunms
+ ) {
+ return new IndexScan<>(
+ execCtx, table().descriptor(), idx, filters, lowerIdxConditions,
upperIdxConditions, rowTransformer, requiredColunms);
}
}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteTable.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteTable.java
index def8848..04ab200 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteTable.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteTable.java
@@ -17,17 +17,22 @@
package org.apache.ignite.internal.processors.query.calcite.schema;
import java.util.Map;
+import java.util.function.Function;
import java.util.function.Predicate;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.schema.TranslatableTable;
+import org.apache.calcite.util.ImmutableBitSet;
import
org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
import
org.apache.ignite.internal.processors.query.calcite.metadata.NodesMapping;
import
org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteIndexScan;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTableScan;
import
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribution;
+import org.jetbrains.annotations.Nullable;
/**
* Ignite table.
@@ -39,6 +44,19 @@ public interface IgniteTable extends TranslatableTable {
TableDescriptor descriptor();
/** {@inheritDoc} */
+ default @Override RelDataType getRowType(RelDataTypeFactory typeFactory) {
+ return getRowType(typeFactory, null);
+ }
+
+ /**
+ * Returns new type according {@code usedClumns} param.
+ *
+ * @param typeFactory Factory.
+ * @param usedColumns Used columns enumeration.
+ */
+ RelDataType getRowType(RelDataTypeFactory typeFactory, ImmutableBitSet
usedColumns);
+
+ /** {@inheritDoc} */
@Override default TableScan toRel(RelOptTable.ToRelContext context,
RelOptTable relOptTable) {
return toRel(context.getCluster(), relOptTable);
}
@@ -67,9 +85,15 @@ public interface IgniteTable extends TranslatableTable {
*
* @param execCtx Execution context.
* @param filter
+ * @param rowTransformer Row transformer.
+ * @param usedColumns Used columns enumeration.
* @return Rows iterator.
*/
- public <Row> Iterable<Row> scan(ExecutionContext<Row> execCtx,
Predicate<Row> filter);
+ public <Row> Iterable<Row> scan(
+ ExecutionContext<Row> execCtx,
+ Predicate<Row> filter,
+ Function<Row, Row> rowTransformer,
+ @Nullable ImmutableBitSet usedColumns);
/**
* Returns nodes mapping.
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteTableImpl.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteTableImpl.java
index d52d38a..cbfc554 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteTableImpl.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteTableImpl.java
@@ -18,10 +18,12 @@
package org.apache.ignite.internal.processors.query.calcite.schema;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
import java.util.function.Predicate;
import com.google.common.collect.ImmutableList;
import org.apache.calcite.plan.RelOptCluster;
@@ -50,10 +52,12 @@ import
org.apache.ignite.internal.processors.query.calcite.rel.IgniteIndexScan;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTableScan;
import
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribution;
import
org.apache.ignite.internal.processors.query.calcite.trait.RewindabilityTrait;
+import
org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
@@ -82,8 +86,8 @@ public class IgniteTableImpl extends AbstractTable implements
IgniteTable {
}
/** {@inheritDoc} */
- @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) {
- return desc.apply(typeFactory);
+ @Override public RelDataType getRowType(RelDataTypeFactory typeFactory,
ImmutableBitSet usedColumns) {
+ return desc.rowType((IgniteTypeFactory)typeFactory, usedColumns);
}
/** {@inheritDoc} */
@@ -103,7 +107,7 @@ public class IgniteTableImpl extends AbstractTable
implements IgniteTable {
.replace(distribution())
.replace(RewindabilityTrait.REWINDABLE);
- return new IgniteTableScan(cluster, traitSet, relOptTbl, null);
+ return new IgniteTableScan(cluster, traitSet, relOptTbl);
}
/** {@inheritDoc} */
@@ -113,12 +117,17 @@ public class IgniteTableImpl extends AbstractTable
implements IgniteTable {
.replace(RewindabilityTrait.REWINDABLE)
.replace(getIndex(idxName).collation());
- return new IgniteIndexScan(cluster, traitSet, relOptTbl, idxName,
null);
+ return new IgniteIndexScan(cluster, traitSet, relOptTbl, idxName);
}
/** {@inheritDoc} */
- @Override public <Row> Iterable<Row> scan(ExecutionContext<Row> execCtx,
Predicate<Row> filter) {
- return new TableScan<>(execCtx, desc, filter);
+ @Override public <Row> Iterable<Row> scan(
+ ExecutionContext<Row> execCtx,
+ Predicate<Row> filter,
+ Function<Row, Row> rowTransformer,
+ @Nullable ImmutableBitSet usedColumns
+ ) {
+ return new TableScan<>(execCtx, desc, filter, rowTransformer,
usedColumns);
}
/** {@inheritDoc} */
@@ -141,16 +150,14 @@ public class IgniteTableImpl extends AbstractTable
implements IgniteTable {
}
}
-
/** {@inheritDoc} */
@Override public IgniteDistribution distribution() {
return desc.distribution();
}
/** {@inheritDoc} */
- @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
@Override public Map<String, IgniteIndex> indexes() {
- return indexes;
+ return Collections.unmodifiableMap(indexes);
}
/** {@inheritDoc} */
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptor.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptor.java
index eef32fc..00a3958 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptor.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptor.java
@@ -18,12 +18,13 @@
package org.apache.ignite.internal.processors.query.calcite.schema;
import java.util.Map;
-
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.rel.core.TableModify;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelProtoDataType;
import org.apache.calcite.sql2rel.InitializerExpressionFactory;
+import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
@@ -49,6 +50,11 @@ public interface TableDescriptor extends RelProtoDataType,
InitializerExpression
*/
IgniteDistribution distribution();
+ /** {@inheritDoc} */
+ @Override default RelDataType apply(RelDataTypeFactory factory) {
+ return rowType((IgniteTypeFactory)factory, null);
+ }
+
/**
* Returns row type excluding effectively virtual fields.
*
@@ -56,7 +62,7 @@ public interface TableDescriptor extends RelProtoDataType,
InitializerExpression
* @return Row type for INSERT operation.
*/
default RelDataType insertRowType(IgniteTypeFactory factory) {
- return apply(factory);
+ return rowType(factory, null);
}
/**
@@ -66,10 +72,19 @@ public interface TableDescriptor extends RelProtoDataType,
InitializerExpression
* @return Row type for SELECT operation.
*/
default RelDataType selectRowType(IgniteTypeFactory factory) {
- return apply(factory);
+ return rowType(factory, null);
}
/**
+ * Returns row type.
+ *
+ * @param factory Type factory.
+ * @param usedColumns Participating columns numeration.
+ * @return Row type.
+ */
+ RelDataType rowType(IgniteTypeFactory factory, ImmutableBitSet
usedColumns);
+
+ /**
* Checks whether is possible to update a column with a given index.
*
* @param tbl Parent table.
@@ -91,10 +106,12 @@ public interface TableDescriptor extends RelProtoDataType,
InitializerExpression
*
* @param ectx Execution context.
* @param row Cache row.
+ * @param requiredColunms Participating colunms.
* @return Relational node row.
* @throws IgniteCheckedException If failed.
*/
- <Row> Row toRow(ExecutionContext<Row> ectx, CacheDataRow row,
RowHandler.RowFactory<Row> factory) throws IgniteCheckedException;
+ <Row> Row toRow(ExecutionContext<Row> ectx, CacheDataRow row,
RowHandler.RowFactory<Row> factory,
+ @Nullable ImmutableBitSet requiredColunms) throws
IgniteCheckedException;
/**
* Converts a relational node row to cache key-value tuple;
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java
index 9110fa4..7a7f41c 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/TableDescriptorImpl.java
@@ -25,7 +25,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.rel.core.TableModify;
import org.apache.calcite.rel.type.RelDataType;
@@ -35,6 +34,7 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.schema.ColumnStrategy;
import org.apache.calcite.sql2rel.InitializerContext;
import org.apache.calcite.sql2rel.NullInitializerExpressionFactory;
+import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.binary.BinaryObject;
import org.apache.ignite.binary.BinaryObjectBuilder;
@@ -53,6 +53,7 @@ import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
+import org.jetbrains.annotations.Nullable;
/**
*
@@ -88,7 +89,7 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
private final int affField;
/** */
- private final BitSet virtualFlags;
+ private final ImmutableBitSet insertFields;
/** */
public TableDescriptorImpl(GridCacheContext<?,?> cctx,
GridQueryTypeDescriptor typeDesc, Object affinityIdentity) {
@@ -101,7 +102,7 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
List<ColumnDescriptor> descriptors = new ArrayList<>(fields.size() +
2);
// A _key/_val fields is virtual in case there is an alias or a
property(es) mapped to _key/_val object fields.
- BitSet virtualFlags = new BitSet();
+ BitSet virtualFields = new BitSet();
descriptors.add(
new KeyValDescriptor(QueryUtils.KEY_FIELD_NAME,
typeDesc.keyClass(), true, QueryUtils.KEY_COL));
@@ -124,7 +125,7 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
keyField = descriptors.size();
- virtualFlags.set(0);
+ virtualFields.set(0);
descriptors.add(new KeyValDescriptor(typeDesc.keyFieldAlias(),
typeDesc.keyClass(), true, fldIdx++));
}
@@ -133,12 +134,12 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
descriptors.add(new
KeyValDescriptor(typeDesc.valueFieldAlias(), typeDesc.valueClass(), false,
fldIdx++));
- virtualFlags.set(1);
+ virtualFields.set(1);
}
else {
GridQueryProperty prop = typeDesc.property(field);
- virtualFlags.set(prop.key() ? 0 : 1);
+ virtualFields.set(prop.key() ? 0 : 1);
descriptors.add(new FieldDescriptor(prop, fldIdx++));
}
@@ -151,19 +152,23 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
this.keyField = keyField;
this.valField = valField;
this.affField = affField;
- this.virtualFlags = virtualFlags;
this.descriptors = descriptors.toArray(DUMMY);
this.descriptorsMap = descriptorsMap;
- }
- /** {@inheritDoc} */
- @Override public RelDataType apply(RelDataTypeFactory factory) {
- return rowType((IgniteTypeFactory) factory, false);
+ ImmutableBitSet.Builder b = ImmutableBitSet.builder();
+ for (int i = 0; i < this.descriptors.length; i++) {
+ if (virtualFields.get(i))
+ continue;
+
+ b.set(i);
+ }
+
+ insertFields = b.build();
}
/** {@inheritDoc} */
@Override public RelDataType insertRowType(IgniteTypeFactory factory) {
- return rowType(factory, true);
+ return rowType(factory, insertFields);
}
/** {@inheritDoc} */
@@ -185,17 +190,28 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
}
/** {@inheritDoc} */
- @Override public <Row> Row toRow(ExecutionContext<Row> ectx, CacheDataRow
row, RowHandler.RowFactory<Row> factory) throws IgniteCheckedException {
+ @Override public <Row> Row toRow(
+ ExecutionContext<Row> ectx,
+ CacheDataRow row,
+ RowHandler.RowFactory<Row> factory,
+ @Nullable ImmutableBitSet requiredColunms
+ ) throws IgniteCheckedException {
RowHandler<Row> handler = factory.handler();
assert handler == ectx.rowHandler();
Row res = factory.create();
- assert handler.columnCount(res) == descriptors.length;
+ assert handler.columnCount(res) == (requiredColunms == null ?
descriptors.length : requiredColunms.cardinality());
- for (int i = 0; i < descriptors.length; i++)
- handler.set(i, res, descriptors[i].value(ectx, cctx, row));
+ if (requiredColunms == null) {
+ for (int i = 0; i < descriptors.length; i++)
+ handler.set(i, res, descriptors[i].value(ectx, cctx, row));
+ }
+ else {
+ for (int i = 0, j = requiredColunms.nextSetBit(0); j != -1; j =
requiredColunms.nextSetBit(j + 1), i++)
+ handler.set(i, res, descriptors[j].value(ectx, cctx, row));
+ }
return res;
}
@@ -399,15 +415,17 @@ public class TableDescriptorImpl extends
NullInitializerExpressionFactory
return F.t(Objects.requireNonNull(key), null);
}
- /** */
- private RelDataType rowType(IgniteTypeFactory factory, boolean
skipVirtual) {
+ /** {@inheritDoc} */
+ @Override public RelDataType rowType(IgniteTypeFactory factory,
ImmutableBitSet usedColumns) {
RelDataTypeFactory.Builder b = new RelDataTypeFactory.Builder(factory);
- for (int i = 0; i < descriptors.length; i++) {
- if (skipVirtual && virtualFlags.get(i))
- continue;
-
- b.add(descriptors[i].name(), descriptors[i].logicalType(factory));
+ if (usedColumns == null) {
+ for (int i = 0; i < descriptors.length; i++)
+ b.add(descriptors[i].name(),
descriptors[i].logicalType(factory));
+ }
+ else {
+ for (int i = usedColumns.nextSetBit(0); i != -1; i =
usedColumns.nextSetBit(i + 1))
+ b.add(descriptors[i].name(),
descriptors[i].logicalType(factory));
}
return b.build();
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitUtils.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitUtils.java
index 602db23..4c7afbc 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitUtils.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitUtils.java
@@ -20,7 +20,6 @@ package
org.apache.ignite.internal.processors.query.calcite.trait;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.apache.calcite.plan.RelOptCluster;
@@ -32,6 +31,7 @@ import org.apache.calcite.plan.RelTraitDef;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelCollationTraitDef;
+import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelDistribution;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
@@ -41,6 +41,7 @@ import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.Util;
+import org.apache.calcite.util.mapping.Mappings;
import
org.apache.ignite.internal.processors.query.calcite.rel.IgniteConvention;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteExchange;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteRel;
@@ -49,8 +50,10 @@ import
org.apache.ignite.internal.processors.query.calcite.rel.IgniteTrimExchang
import org.apache.ignite.internal.util.typedef.F;
import org.jetbrains.annotations.Nullable;
+import static org.apache.calcite.plan.RelOptUtil.permutationPushDownProject;
import static
org.apache.calcite.rel.RelDistribution.Type.BROADCAST_DISTRIBUTED;
import static org.apache.calcite.rel.RelDistribution.Type.HASH_DISTRIBUTED;
+import static org.apache.calcite.rel.core.Project.getPartialMapping;
import static
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions.any;
import static
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions.single;
@@ -332,4 +335,24 @@ public class TraitUtils {
return out;
}
+
+ /** */
+ public static RelCollation projectCollation(RelCollation collation,
List<RexNode> projects, RelDataType inputRowType) {
+ if (collation.getFieldCollations().isEmpty())
+ return RelCollations.EMPTY;
+
+ Mappings.TargetMapping mapping = permutationPushDownProject(projects,
inputRowType, 0, 0);
+
+ return collation.apply(mapping);
+ }
+
+ /** */
+ public static IgniteDistribution projectDistribution(IgniteDistribution
distribution, List<RexNode> projects, RelDataType inputRowType) {
+ if (distribution.getType() != HASH_DISTRIBUTED)
+ return distribution;
+
+ Mappings.TargetMapping mapping =
getPartialMapping(inputRowType.getFieldCount(), projects);
+
+ return distribution.apply(mapping);
+ }
}
diff --git
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java
index 0aae659..2e1dc1a 100644
---
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java
+++
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java
@@ -18,18 +18,24 @@
package org.apache.ignite.internal.processors.query.calcite.util;
import java.util.Arrays;
-
+import java.util.List;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptPredicateList;
+import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexExecutor;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLocalRef;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexSimplify;
+import org.apache.calcite.rex.RexSlot;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Litmus;
import org.apache.calcite.util.Util;
import org.apache.ignite.internal.util.typedef.internal.U;
@@ -81,4 +87,33 @@ public class RexUtils {
return builder.makeCall(SqlStdOperatorTable.CASE, operands);
}
+
+ /** Returns whether a list of expressions projects the incoming fields. */
+ public static boolean isIdentity(List<? extends RexNode> projects,
RelDataType inputRowType) {
+ return isIdentity(projects, inputRowType, false);
+ }
+
+ /** Returns whether a list of expressions projects the incoming fields. */
+ public static boolean isIdentity(List<? extends RexNode> projects,
RelDataType inputRowType, boolean local) {
+ if (inputRowType.getFieldCount() != projects.size())
+ return false;
+
+ final List<RelDataTypeField> fields = inputRowType.getFieldList();
+ Class<? extends RexSlot> clazz = local ? RexLocalRef.class :
RexInputRef.class;
+
+ for (int i = 0; i < fields.size(); i++) {
+ if (!clazz.isInstance(projects.get(i)))
+ return false;
+
+ RexSlot ref = (RexSlot) projects.get(i);
+
+ if (ref.getIndex() != i)
+ return false;
+
+ if (!RelOptUtil.eq("t1", projects.get(i).getType(), "t2",
fields.get(i).getType(), Litmus.IGNORE))
+ return false;
+ }
+
+ return true;
+ }
}
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessorTest.java
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessorTest.java
index 12448a9..04b854c 100644
---
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessorTest.java
+++
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessorTest.java
@@ -408,7 +408,8 @@ public class CalciteQueryProcessorTest extends
GridCommonAbstractTest {
assertNull(row);
}
-
+
+ /** for test purpose only. */
public void testThroughput() {
IgniteCache<Integer, Developer> developer =
ignite.getOrCreateCache(new CacheConfiguration<Integer, Developer>()
.setCacheMode(CacheMode.REPLICATED)
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/PlannerTest.java
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/PlannerTest.java
index c864e44..fa2f9c8 100644
---
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/PlannerTest.java
+++
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/PlannerTest.java
@@ -25,7 +25,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.apache.calcite.config.CalciteConnectionConfig;
@@ -775,11 +777,17 @@ public class PlannerTest extends GridCommonAbstractTest {
.build()) {
@Override public IgniteIndex getIndex(String idxName) {
return new IgniteIndex(null, null, null, null) {
- @Override public <Row> Iterable<Row>
scan(ExecutionContext<Row> execCtx, Predicate<Row> filters,
- Supplier<Row> lowerIdxConditions, Supplier<Row>
upperIdxConditions) {
+ @Override public <Row> Iterable<Row> scan(
+ ExecutionContext<Row> execCtx,
+ Predicate<Row> filters,
+ Supplier<Row> lowerIdxConditions,
+ Supplier<Row> upperIdxConditions,
+ Function<Row, Row> rowTransformer,
+ @Nullable ImmutableBitSet requiredColunms
+ ) {
return Linq4j.asEnumerable(Arrays.asList(
- row(execCtx, 0, "Igor", 0),
- row(execCtx, 1, "Roman", 0)
+ row(execCtx, requiredColunms, 0, "Igor", 0),
+ row(execCtx, requiredColunms, 1, "Roman", 0)
));
}
};
@@ -808,11 +816,17 @@ public class PlannerTest extends GridCommonAbstractTest {
.build()) {
@Override public IgniteIndex getIndex(String idxName) {
return new IgniteIndex(null, null, null, null) {
- @Override public <Row> Iterable<Row>
scan(ExecutionContext<Row> execCtx, Predicate<Row> filters,
- Supplier<Row> lowerIdxConditions, Supplier<Row>
upperIdxConditions) {
+ @Override public <Row> Iterable<Row> scan(
+ ExecutionContext<Row> execCtx,
+ Predicate<Row> filters,
+ Supplier<Row> lowerIdxConditions,
+ Supplier<Row> upperIdxConditions,
+ Function<Row, Row> rowTransformer,
+ @Nullable ImmutableBitSet requiredColunms
+ ) {
return Linq4j.asEnumerable(Arrays.asList(
- row(execCtx, 0, "Calcite", 1),
- row(execCtx, 1, "Ignite", 1)
+ row(execCtx, requiredColunms, 0, "Calcite", 1),
+ row(execCtx, requiredColunms, 1, "Ignite", 1)
));
}
};
@@ -1143,10 +1157,15 @@ public class PlannerTest extends GridCommonAbstractTest
{
.add("NAME", f.createJavaType(String.class))
.add("PROJECTID", f.createJavaType(Integer.class))
.build()) {
- @Override public <Row> Iterable<Row> scan(ExecutionContext<Row>
execCtx, Predicate<Row> filter) {
+ @Override public <Row> Iterable<Row> scan(
+ ExecutionContext<Row> execCtx,
+ Predicate<Row> filter,
+ Function<Row, Row> transformer,
+ ImmutableBitSet requiredColunms
+ ) {
return Arrays.asList(
- row(execCtx, 0, "Igor", 0),
- row(execCtx, 1, "Roman", 0)
+ row(execCtx, requiredColunms, 0, "Igor", 0),
+ row(execCtx, requiredColunms, 1, "Roman", 0)
);
}
@@ -1165,10 +1184,15 @@ public class PlannerTest extends GridCommonAbstractTest
{
.add("NAME", f.createJavaType(String.class))
.add("VER", f.createJavaType(Integer.class))
.build()) {
- @Override public <Row> Iterable<Row> scan(ExecutionContext<Row>
execCtx, Predicate<Row> filter) {
+ @Override public <Row> Iterable<Row> scan(
+ ExecutionContext<Row> execCtx,
+ Predicate<Row> filter,
+ Function<Row, Row> transformer,
+ ImmutableBitSet requiredColunms
+ ) {
return Arrays.asList(
- row(execCtx, 0, "Calcite", 1),
- row(execCtx, 1, "Ignite", 1)
+ row(execCtx, requiredColunms, 0, "Calcite", 1),
+ row(execCtx, requiredColunms, 1, "Ignite", 1)
);
}
@@ -1406,17 +1430,35 @@ public class PlannerTest extends GridCommonAbstractTest
{
IgniteTypeFactory f = new IgniteTypeFactory(IgniteTypeSystem.INSTANCE);
+ ThreadLocal<List<?>> checkRes = new ThreadLocal<>();
+
TestTable testTbl = new TestTable(
new RelDataTypeFactory.Builder(f)
.add("ID0", f.createJavaType(Integer.class))
.add("ID1", f.createJavaType(Integer.class))
.build()) {
+ @Override public <Row> Iterable<Row> scan(
+ ExecutionContext<Row> execCtx,
+ Predicate<Row> filter,
+ Function<Row, Row> rowTransformer,
+ ImmutableBitSet requiredColunms
+ ) {
+ List<Row> checkRes0 = new ArrayList<>();
- @Override public <Row> Iterable<Row> scan(ExecutionContext<Row>
execCtx, Predicate<Row> filter) {
- return Arrays.asList(
- row(execCtx, 0, 1),
- row(execCtx, 1, 2)
- );
+ for (int i = 0; i < 10; ++i) {
+ int col = ThreadLocalRandom.current().nextInt(1_000);
+
+ Row r = row(execCtx, requiredColunms, col, col);
+
+ if (rowTransformer != null)
+ r = rowTransformer.apply(r);
+
+ checkRes0.add(r);
+ }
+
+ checkRes.set(checkRes0);
+
+ return checkRes0;
}
@Override public NodesMapping mapping(PlanningContext ctx) {
@@ -1633,8 +1675,10 @@ public class PlannerTest extends GridCommonAbstractTest {
assertFalse(res.isEmpty());
- Assert.assertArrayEquals(new Object[]{1}, res.get(0));
- Assert.assertArrayEquals(new Object[]{3}, res.get(1));
+ int pos = 0;
+
+ for (Object obj : checkRes.get())
+ Assert.assertArrayEquals((Object[]) obj, res.get(pos++));
}
}
@@ -2586,10 +2630,8 @@ public class PlannerTest extends GridCommonAbstractTest {
RelNode phys = planner.transform(PlannerPhase.OPTIMIZATION,
desired, rel);
assertNotNull(phys);
- assertEquals("" +
- "IgniteCorrelatedNestedLoopJoin(condition=[=(CAST(+($0,
$1)):INTEGER, 2)], joinType=[inner])\n" +
- " IgniteProject(DEPTNO=[$0])\n" +
- " IgniteTableScan(table=[[PUBLIC, DEPT]])\n" +
+
assertEquals("IgniteCorrelatedNestedLoopJoin(condition=[=(CAST(+($0,
$1)):INTEGER, 2)], joinType=[inner])\n" +
+ " IgniteTableScan(table=[[PUBLIC, DEPT]],
requiredColunms=[{0}])\n" +
" IgniteProject(DEPTNO=[$2])\n" +
" IgniteTableScan(table=[[PUBLIC, EMP]],
filters=[=(CAST(+($cor1.DEPTNO, $t2)):INTEGER, 2)])\n",
RelOptUtil.toString(phys));
@@ -2606,19 +2648,27 @@ public class PlannerTest extends GridCommonAbstractTest
{
private static <T> List<T> select(List<T> src, int... idxs) {
ArrayList<T> res = new ArrayList<>(idxs.length);
- for (int idx : idxs) {
+ for (int idx : idxs)
res.add(src.get(idx));
- }
return res;
}
/** */
- private <Row> Row row(ExecutionContext<Row> ctx, Object... fields) {
+ private <Row> Row row(ExecutionContext<Row> ctx, ImmutableBitSet
requiredColunms, Object... fields) {
Type[] types = new Type[fields.length];
for (int i = 0; i < fields.length; i++)
types[i] = fields[i] == null ? Object.class : fields[i].getClass();
+ if (requiredColunms == null) {
+ for (int i = 0; i < fields.length; i++)
+ types[i] = fields[i] == null ? Object.class :
fields[i].getClass();
+ }
+ else {
+ for (int i = 0, j = requiredColunms.nextSetBit(0); j != -1; j =
requiredColunms.nextSetBit(j + 1), i++)
+ types[i] = fields[i] == null ? Object.class :
fields[i].getClass();
+ }
+
return ctx.rowHandler().factory(types).create(fields);
}
@@ -2643,7 +2693,7 @@ public class PlannerTest extends GridCommonAbstractTest {
.replaceIf(RewindabilityTraitDef.INSTANCE, () ->
RewindabilityTrait.REWINDABLE)
.replaceIf(DistributionTraitDef.INSTANCE, this::distribution);
- return new IgniteTableScan(cluster, traitSet, relOptTbl, null);
+ return new IgniteTableScan(cluster, traitSet, relOptTbl);
}
/** {@inheritDoc} */
@@ -2651,12 +2701,21 @@ public class PlannerTest extends GridCommonAbstractTest
{
RelTraitSet traitSet =
cluster.traitSetOf(IgniteConvention.INSTANCE)
.replaceIf(DistributionTraitDef.INSTANCE, this::distribution);
- return new IgniteIndexScan(cluster, traitSet, relOptTbl, idxName,
null);
+ return new IgniteIndexScan(cluster, traitSet, relOptTbl, idxName);
}
/** {@inheritDoc} */
- @Override public RelDataType getRowType(RelDataTypeFactory
typeFactory) {
- return protoType.apply(typeFactory);
+ @Override public RelDataType getRowType(RelDataTypeFactory
typeFactory, ImmutableBitSet bitSet) {
+ RelDataType rowType = protoType.apply(typeFactory);
+
+ if (bitSet != null) {
+ RelDataTypeFactory.Builder b = new
RelDataTypeFactory.Builder(typeFactory);
+ for (int i = bitSet.nextSetBit(0); i != -1; i =
bitSet.nextSetBit(i + 1))
+ b.add(rowType.getFieldList().get(i));
+ rowType = b.build();
+ }
+
+ return rowType;
}
/** {@inheritDoc} */
@@ -2695,7 +2754,12 @@ public class PlannerTest extends GridCommonAbstractTest {
}
/** {@inheritDoc} */
- @Override public <Row> Iterable<Row> scan(ExecutionContext<Row> root,
Predicate<Row> filter) {
+ @Override public <Row> Iterable<Row> scan(
+ ExecutionContext<Row> execCtx,
+ Predicate<Row> filter,
+ Function<Row, Row> transformer,
+ ImmutableBitSet bitSet
+ ) {
throw new AssertionError();
}
@@ -2732,7 +2796,7 @@ public class PlannerTest extends GridCommonAbstractTest {
/** {@inheritDoc} */
@Override public Map<String, IgniteIndex> indexes() {
- return indexes;
+ return Collections.unmodifiableMap(indexes);
}
/** {@inheritDoc} */
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/QueryChecker.java
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/QueryChecker.java
index 5510ab9..51f4af1 100644
---
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/QueryChecker.java
+++
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/QueryChecker.java
@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.internal.processors.query.QueryEngine;
@@ -31,6 +32,7 @@ import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
import org.hamcrest.CoreMatchers;
import org.hamcrest.Matcher;
+import org.hamcrest.core.SubstringMatcher;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -52,6 +54,59 @@ public abstract class QueryChecker {
}
/**
+ * Ignite table scan with projects matcher.
+ *
+ * @param schema Schema name.
+ * @param tblName Table name.
+ * @return Matcher.
+ */
+ public static Matcher<String> containsAnyProject(String schema, String
tblName) {
+ return containsSubPlan("IgniteTableScan(table=[[" + schema + ", " +
tblName + "]], " + "requiredColunms=");
+ }
+
+ /**
+ * Ignite table scan with projects unmatcher.
+ *
+ * @param schema Schema name.
+ * @param tblName Table name.
+ * @return Matcher.
+ */
+ public static Matcher<String> notContainsProject(String schema, String
tblName) {
+ return CoreMatchers.not(containsSubPlan("IgniteTableScan(table=[[" +
schema + ", " +
+ tblName + "]], " + "requiredColunms="));
+ }
+
+ /**
+ * Ignite table scan with projects unmatcher.
+ *
+ * @param schema Schema name.
+ * @param tblName Table name.
+ * @return Matcher.
+ */
+ public static Matcher<String> containsProject(String schema, String
tblName, int... requiredColunms) {
+ return matches(".*IgniteTableScan\\(table=\\[\\[" + schema + ", " +
+ tblName + "\\]\\], " + "requiredColunms=\\[\\{" +
+ Arrays.toString(requiredColunms)
+ .replaceAll("\\[", "")
+ .replaceAll("]", "") + "\\}\\]\\).*");
+ }
+
+ /**
+ * Ignite table scan with projects unmatcher.
+ *
+ * @param schema Schema name.
+ * @param tblName Table name.
+ * @return Matcher.
+ */
+ public static Matcher<String> containsOneProject(String schema, String
tblName, int... requiredColunms) {
+ return matchesOnce(".*IgniteTableScan\\(table=\\[\\[" + schema + ", " +
+ tblName + "\\]\\], " + "requiredColunms=\\[\\{" +
+ Arrays.toString(requiredColunms)
+ .replaceAll("\\[", "")
+ .replaceAll("]", "") + "\\}\\]\\).*");
+ }
+
+ /**
* Ignite index scan matcher.
*
* @param schema Schema name.
@@ -73,6 +128,51 @@ public abstract class QueryChecker {
return CoreMatchers.containsString(subPlan);
}
+ /** */
+ public static Matcher<String> matches(final String substring) {
+ return new SubstringMatcher(substring) {
+ /** {@inheritDoc} */
+ @Override protected boolean evalSubstringOf(String sIn) {
+ sIn = sIn.replaceAll("\n", "");
+
+ return sIn.matches(substring);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected String relationship() {
+ return null;
+ }
+ };
+ }
+
+ /** */
+ public static Matcher<String> matchesOnce(final String substring) {
+ return new SubstringMatcher(substring) {
+ /** {@inheritDoc} */
+ @Override protected boolean evalSubstringOf(String sIn) {
+ sIn = sIn.replaceAll("\n", "");
+
+ return containsOnce(sIn, substring);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected String relationship() {
+ return null;
+ }
+ };
+ }
+
+ /** Check only single matching. */
+ public static boolean containsOnce(final String s, final CharSequence
substring) {
+ Pattern pattern = Pattern.compile(substring.toString());
+ java.util.regex.Matcher matcher = pattern.matcher(s);
+
+ if (matcher.find())
+ return !matcher.find();
+
+ return false;
+ }
+
/**
* Ignite any index can matcher.
*
@@ -93,7 +193,7 @@ public abstract class QueryChecker {
private final ArrayList<Matcher<String>> planMatchers = new ArrayList<>();
/** */
- private List<List<?>> expectedResult = null;
+ private List<List<?>> expectedResult;
/** */
private boolean ordered;
@@ -168,9 +268,8 @@ public abstract class QueryChecker {
assertThat(actualPlan, matcher);
}
- if (exactPlan != null) {
+ if (exactPlan != null)
assertEquals(exactPlan, actualPlan);
- }
// Check result.
List<FieldsQueryCursor<List<?>>> cursors =
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/QueryCheckerTest.java
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/QueryCheckerTest.java
new file mode 100644
index 0000000..045c235
--- /dev/null
+++
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/QueryCheckerTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import org.hamcrest.Matcher;
+import org.junit.Test;
+
+import static
org.apache.ignite.internal.processors.query.calcite.QueryChecker.matchesOnce;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/** Query checker tests. */
+public class QueryCheckerTest {
+ /** */
+ @Test
+ public void testMatchesOnce() {
+ String plan = "PLAN=IgniteExchange(distribution=[single])\n " +
+ "IgniteProject(NAME=[$2])\n " +
+ "IgniteTableScan(table=[[PUBLIC, DEVELOPER]], projects=[[$t0]],
requiredColunms=[{2}])\n " +
+ "IgniteTableScan(table=[[PUBLIC, DEVELOPER]], projects=[[$t1]],
requiredColunms=[{2, 3}])";
+
+ Matcher<String> matcherTbl = matchesOnce("IgniteTableScan");
+ Matcher<String> matcherPrj = matchesOnce("IgniteProject");
+
+ assertFalse(matcherTbl.matches(plan));
+ assertTrue(matcherPrj.matches(plan));
+ }
+}
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/OrToUnionRuleTest.java
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/OrToUnionRuleTest.java
index 6f8382b..48e041a 100644
---
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/OrToUnionRuleTest.java
+++
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/OrToUnionRuleTest.java
@@ -246,7 +246,7 @@ public class OrToUnionRuleTest extends
GridCommonAbstractTest {
/**
*
*/
- static class Product {
+ public static class Product {
/** */
long id;
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/ProjectScanMergeRuleTest.java
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/ProjectScanMergeRuleTest.java
new file mode 100644
index 0000000..146e256
--- /dev/null
+++
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/ProjectScanMergeRuleTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.rules;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.processors.query.QueryEngine;
+import org.apache.ignite.internal.processors.query.calcite.QueryChecker;
+import org.apache.ignite.internal.processors.query.calcite.util.Commons;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+import static java.util.Collections.singletonList;
+import static
org.apache.ignite.internal.processors.query.calcite.QueryChecker.containsOneProject;
+import static
org.apache.ignite.internal.processors.query.calcite.QueryChecker.containsScan;
+import static
org.apache.ignite.internal.processors.query.calcite.QueryChecker.notContainsProject;
+import static
org.apache.ignite.internal.processors.query.calcite.rules.OrToUnionRuleTest.Product;
+
+/**
+ * Tests projection rule {@code
org.apache.ignite.internal.processors.query.calcite.rule.ProjectScanMergeRule}
+ * This rule have a deal with only useful columns and.
+ * For example for tables: T1(f12, f12, f13) and T2(f21, f22, f23)
+ * sql execution: SELECT t1.f11, t2.f21 FROM T1 t1 INNER JOIN T2 t2 on t1.f11
= t2.f22"
+ * need to eleminate all unused coluns and take into account only: f11, f21
and f22 cols.
+ */
+public class ProjectScanMergeRuleTest extends GridCommonAbstractTest {
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ Ignite grid = startGridsMultiThreaded(2);
+
+ QueryEntity qryEnt = new QueryEntity();
+ qryEnt.setKeyFieldName("ID");
+ qryEnt.setKeyType(Integer.class.getName());
+ qryEnt.setValueType(Product.class.getName());
+
+ qryEnt.addQueryField("ID", Integer.class.getName(), null);
+ qryEnt.addQueryField("CATEGORY", String.class.getName(), null);
+ qryEnt.addQueryField("CAT_ID", Integer.class.getName(), null);
+ qryEnt.addQueryField("SUBCATEGORY", String.class.getName(), null);
+ qryEnt.addQueryField("SUBCAT_ID", Integer.class.getName(), null);
+ qryEnt.addQueryField("NAME", String.class.getName(), null);
+
+ qryEnt.setTableName("products");
+
+ final CacheConfiguration<Integer, Product> cfg = new
CacheConfiguration<>(qryEnt.getTableName());
+
+ cfg.setCacheMode(CacheMode.PARTITIONED)
+ .setBackups(1)
+ .setQueryEntities(singletonList(qryEnt))
+ .setSqlSchema("PUBLIC");
+
+ IgniteCache<Integer, Product> devCache = grid.createCache(cfg);
+
+ devCache.put(1, new Product(1, "prod1", 1, "cat1", 11, "noname1"));
+ devCache.put(2, new Product(2, "prod2", 2, "cat1", 11, "noname2"));
+ devCache.put(3, new Product(3, "prod3", 3, "cat1", 12, "noname3"));
+
+ awaitPartitionMapExchange();
+ }
+
+ /** */
+ private QueryChecker checkQuery(String qry) {
+ return new QueryChecker(qry) {
+ @Override protected QueryEngine getEngine() {
+ return Commons.lookupComponent(grid(0).context(),
QueryEngine.class);
+ }
+ };
+ }
+
+ /**
+ * Tests that the projects exist only for simple expressions without any
predicates.
+ */
+ @Test
+ public void testProjects() {
+ checkQuery("SELECT NAME FROM products d;")
+ .matches(containsScan("PUBLIC", "PRODUCTS"))
+ .matches(containsOneProject("PUBLIC", "PRODUCTS", 7))
+ .returns("noname1")
+ .returns("noname2")
+ .returns("noname3")
+ .check();
+
+ checkQuery("SELECT SUBCAT_ID, NAME FROM products d;")
+ .matches(containsScan("PUBLIC", "PRODUCTS"))
+ .matches(containsOneProject("PUBLIC", "PRODUCTS", 6, 7))
+ .returns(11, "noname1")
+ .returns(11, "noname2")
+ .returns(12, "noname3")
+ .check();
+
+ checkQuery("SELECT NAME FROM products d WHERE CAT_ID > 1;")
+ .matches(containsScan("PUBLIC", "PRODUCTS"))
+ .matches(notContainsProject("PUBLIC", "PRODUCTS"))
+ .returns("noname2")
+ .returns("noname3")
+ .check();
+ }
+}
diff --git
a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
index 912d8dc..6742375 100644
---
a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
+++
b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
@@ -21,11 +21,13 @@ import
org.apache.ignite.internal.processors.query.calcite.CalciteBasicSecondary
import
org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessorTest;
import org.apache.ignite.internal.processors.query.calcite.CancelTest;
import org.apache.ignite.internal.processors.query.calcite.PlannerTest;
+import org.apache.ignite.internal.processors.query.calcite.QueryCheckerTest;
import
org.apache.ignite.internal.processors.query.calcite.exec.ClosableIteratorsHolderTest;
import
org.apache.ignite.internal.processors.query.calcite.exec.rel.ContinuousExecutionTest;
import
org.apache.ignite.internal.processors.query.calcite.exec.rel.ExecutionTest;
import org.apache.ignite.internal.processors.query.calcite.jdbc.JdbcQueryTest;
import
org.apache.ignite.internal.processors.query.calcite.rules.OrToUnionRuleTest;
+import
org.apache.ignite.internal.processors.query.calcite.rules.ProjectScanMergeRuleTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@@ -36,13 +38,16 @@ import org.junit.runners.Suite;
@Suite.SuiteClasses({
PlannerTest.class,
OrToUnionRuleTest.class,
+ ProjectScanMergeRuleTest.class,
ExecutionTest.class,
ClosableIteratorsHolderTest.class,
ContinuousExecutionTest.class,
CalciteQueryProcessorTest.class,
JdbcQueryTest.class,
CalciteBasicSecondaryIndexIntegrationTest.class,
- CancelTest.class
+ CancelTest.class,
+ QueryCheckerTest.class,
+ ProjectScanMergeRuleTest.class
})
public class IgniteCalciteTestSuite {
}