You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ca...@apache.org on 2024/04/01 11:29:39 UTC
(iotdb) branch beyyes/TableModelGrammar updated: add logical planner node impl
This is an automated email from the ASF dual-hosted git repository.
caogaofei pushed a commit to branch beyyes/TableModelGrammar
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/beyyes/TableModelGrammar by this push:
new 73efd98bca5 add logical planner node impl
73efd98bca5 is described below
commit 73efd98bca5e9489516c55c0b905f4bece4e75a1
Author: Beyyes <cg...@foxmail.com>
AuthorDate: Mon Apr 1 19:29:15 2024 +0800
add logical planner node impl
---
iotdb-core/datanode/pom.xml | 2 +-
.../relational/cost/CachingTableStatsProvider.java | 46 +-
.../plan/relational/cost/ColumnStatistics.java | 242 ++--
.../plan/relational/cost/DoubleRange.java | 138 +--
.../queryengine/plan/relational/cost/Estimate.java | 106 +-
.../plan/relational/cost/StatsUtil.java | 35 +-
.../plan/relational/cost/TableStatistics.java | 158 ++-
.../plan/relational/cost/TableStatsProvider.java | 2 +-
.../plan/relational/planner/Assignments.java | 351 +++---
.../plan/relational/planner/LogicalPlanner.java | 173 ++-
.../plan/relational/planner/OrderingScheme.java | 89 ++
.../relational/planner/OrderingTranslator.java | 34 +
.../plan/relational/planner/PlanBuilder.java | 161 +--
.../plan/relational/planner/QueryPlanner.java | 1271 +++++---------------
.../plan/relational/planner/RelationPlan.java | 83 +-
.../plan/relational/planner/RelationPlanner.java | 405 +++----
.../plan/relational/planner/SortOrder.java | 44 +
.../plan/relational/planner/SymbolAllocator.java | 169 ++-
.../plan/relational/planner/node/FilterNode.java | 39 +-
.../plan/relational/planner/node/LimitNode.java | 43 +
.../plan/relational/planner/node/OffsetNode.java | 35 +
.../plan/relational/planner/node/OutputNode.java | 59 +-
.../plan/relational/planner/node/ProjectNode.java | 38 +-
.../plan/relational/planner/node/SortNode.java | 38 +
.../relational/planner/node/TableScanNode.java | 82 +-
.../plan/relational/analyzer/AnalyzerTest.java | 4 +-
.../plan/relational/analyzer/TestMatadata.java | 93 +-
27 files changed, 1638 insertions(+), 2302 deletions(-)
diff --git a/iotdb-core/datanode/pom.xml b/iotdb-core/datanode/pom.xml
index 32b150ee56f..3f6f142ba83 100644
--- a/iotdb-core/datanode/pom.xml
+++ b/iotdb-core/datanode/pom.xml
@@ -528,7 +528,7 @@
<usedDependency>org.glassfish.jersey.inject:jersey-hk2</usedDependency>
</usedDependencies>
</configuration>
- </plugin>
+ </plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>8</source><target>8</target></configuration></plugin>
</plugins>
</build>
<profiles>
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/CachingTableStatsProvider.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/CachingTableStatsProvider.java
index 0706a8ac3e4..d3c9da443ab 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/CachingTableStatsProvider.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/CachingTableStatsProvider.java
@@ -13,43 +13,39 @@
*/
package org.apache.iotdb.db.queryengine.plan.relational.cost;
-import com.google.common.collect.ImmutableMap;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableHandle;
import org.apache.iotdb.session.Session;
+import com.google.common.collect.ImmutableMap;
+
import java.util.Map;
import java.util.WeakHashMap;
import static java.util.Objects.requireNonNull;
-public class CachingTableStatsProvider
- implements TableStatsProvider
-{
- private final Metadata metadata;
- private final Session session;
+public class CachingTableStatsProvider implements TableStatsProvider {
+ private final Metadata metadata;
+ private final Session session;
- private final Map<TableHandle, TableStatistics> cache = new WeakHashMap<>();
+ private final Map<TableHandle, TableStatistics> cache = new WeakHashMap<>();
- public CachingTableStatsProvider(Metadata metadata, Session session)
- {
- this.metadata = requireNonNull(metadata, "metadata is null");
- this.session = requireNonNull(session, "session is null");
- }
+ public CachingTableStatsProvider(Metadata metadata, Session session) {
+ this.metadata = requireNonNull(metadata, "metadata is null");
+ this.session = requireNonNull(session, "session is null");
+ }
- @Override
- public TableStatistics getTableStatistics(TableHandle tableHandle)
- {
- TableStatistics stats = cache.get(tableHandle);
- if (stats == null) {
- // stats = metadata.getTableStatistics(session, tableHandle);
- cache.put(tableHandle, TableStatistics.empty());
- }
- return stats;
+ @Override
+ public TableStatistics getTableStatistics(TableHandle tableHandle) {
+ TableStatistics stats = cache.get(tableHandle);
+ if (stats == null) {
+ // stats = metadata.getTableStatistics(session, tableHandle);
+ cache.put(tableHandle, TableStatistics.empty());
}
+ return stats;
+ }
- public Map<TableHandle, TableStatistics> getCachedTableStatistics()
- {
- return ImmutableMap.copyOf(cache);
- }
+ public Map<TableHandle, TableStatistics> getCachedTableStatistics() {
+ return ImmutableMap.copyOf(cache);
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/ColumnStatistics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/ColumnStatistics.java
index 37afd7b1dbe..4895d9096e6 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/ColumnStatistics.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/ColumnStatistics.java
@@ -19,146 +19,138 @@ import java.util.Optional;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
-public final class ColumnStatistics
-{
- private static final ColumnStatistics EMPTY = new ColumnStatistics(Estimate.unknown(), Estimate.unknown(), Estimate.unknown(), Optional.empty());
-
- private final Estimate nullsFraction;
- private final Estimate distinctValuesCount;
- private final Estimate dataSize;
- private final Optional<DoubleRange> range;
-
- public static ColumnStatistics empty()
- {
- return EMPTY;
+public final class ColumnStatistics {
+ private static final ColumnStatistics EMPTY =
+ new ColumnStatistics(
+ Estimate.unknown(), Estimate.unknown(), Estimate.unknown(), Optional.empty());
+
+ private final Estimate nullsFraction;
+ private final Estimate distinctValuesCount;
+ private final Estimate dataSize;
+ private final Optional<DoubleRange> range;
+
+ public static ColumnStatistics empty() {
+ return EMPTY;
+ }
+
+ public ColumnStatistics(
+ Estimate nullsFraction,
+ Estimate distinctValuesCount,
+ Estimate dataSize,
+ Optional<DoubleRange> range) {
+ this.nullsFraction = requireNonNull(nullsFraction, "nullsFraction is null");
+ if (!nullsFraction.isUnknown()) {
+ if (nullsFraction.getValue() < 0 || nullsFraction.getValue() > 1) {
+ throw new IllegalArgumentException(
+ format("nullsFraction must be between 0 and 1: %s", nullsFraction.getValue()));
+ }
}
-
- public ColumnStatistics(
- Estimate nullsFraction,
- Estimate distinctValuesCount,
- Estimate dataSize,
- Optional<DoubleRange> range)
- {
- this.nullsFraction = requireNonNull(nullsFraction, "nullsFraction is null");
- if (!nullsFraction.isUnknown()) {
- if (nullsFraction.getValue() < 0 || nullsFraction.getValue() > 1) {
- throw new IllegalArgumentException(format("nullsFraction must be between 0 and 1: %s", nullsFraction.getValue()));
- }
- }
- this.distinctValuesCount = requireNonNull(distinctValuesCount, "distinctValuesCount is null");
- if (!distinctValuesCount.isUnknown() && distinctValuesCount.getValue() < 0) {
- throw new IllegalArgumentException(format("distinctValuesCount must be greater than or equal to 0: %s", distinctValuesCount.getValue()));
- }
- this.dataSize = requireNonNull(dataSize, "dataSize is null");
- if (!dataSize.isUnknown() && dataSize.getValue() < 0) {
- throw new IllegalArgumentException(format("dataSize must be greater than or equal to 0: %s", dataSize.getValue()));
- }
- this.range = requireNonNull(range, "range is null");
+ this.distinctValuesCount = requireNonNull(distinctValuesCount, "distinctValuesCount is null");
+ if (!distinctValuesCount.isUnknown() && distinctValuesCount.getValue() < 0) {
+ throw new IllegalArgumentException(
+ format(
+ "distinctValuesCount must be greater than or equal to 0: %s",
+ distinctValuesCount.getValue()));
}
-
- public Estimate getNullsFraction()
- {
- return nullsFraction;
+ this.dataSize = requireNonNull(dataSize, "dataSize is null");
+ if (!dataSize.isUnknown() && dataSize.getValue() < 0) {
+ throw new IllegalArgumentException(
+ format("dataSize must be greater than or equal to 0: %s", dataSize.getValue()));
}
+ this.range = requireNonNull(range, "range is null");
+ }
- public Estimate getDistinctValuesCount()
- {
- return distinctValuesCount;
- }
+ public Estimate getNullsFraction() {
+ return nullsFraction;
+ }
- public Estimate getDataSize()
- {
- return dataSize;
- }
+ public Estimate getDistinctValuesCount() {
+ return distinctValuesCount;
+ }
- public Optional<DoubleRange> getRange()
- {
- return range;
+ public Estimate getDataSize() {
+ return dataSize;
+ }
+
+ public Optional<DoubleRange> getRange() {
+ return range;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ColumnStatistics that = (ColumnStatistics) o;
+ return Objects.equals(nullsFraction, that.nullsFraction)
+ && Objects.equals(distinctValuesCount, that.distinctValuesCount)
+ && Objects.equals(dataSize, that.dataSize)
+ && Objects.equals(range, that.range);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(nullsFraction, distinctValuesCount, dataSize, range);
+ }
+
+ @Override
+ public String toString() {
+ return "ColumnStatistics{"
+ + "nullsFraction="
+ + nullsFraction
+ + ", distinctValuesCount="
+ + distinctValuesCount
+ + ", dataSize="
+ + dataSize
+ + ", range="
+ + range
+ + '}';
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * If one of the estimates below is unspecified (i.e. left as "unknown"), the optimizer may not be
+ * able to derive the statistics needed for performing cost-based query plan optimizations.
+ */
+ public static final class Builder {
+ private Estimate nullsFraction = Estimate.unknown();
+ private Estimate distinctValuesCount = Estimate.unknown();
+ private Estimate dataSize = Estimate.unknown();
+ private Optional<DoubleRange> range = Optional.empty();
+
+ public Builder setNullsFraction(Estimate nullsFraction) {
+ this.nullsFraction = requireNonNull(nullsFraction, "nullsFraction is null");
+ return this;
}
- @Override
- public boolean equals(Object o)
- {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- ColumnStatistics that = (ColumnStatistics) o;
- return Objects.equals(nullsFraction, that.nullsFraction) &&
- Objects.equals(distinctValuesCount, that.distinctValuesCount) &&
- Objects.equals(dataSize, that.dataSize) &&
- Objects.equals(range, that.range);
+ public Builder setDistinctValuesCount(Estimate distinctValuesCount) {
+ this.distinctValuesCount = requireNonNull(distinctValuesCount, "distinctValuesCount is null");
+ return this;
}
- @Override
- public int hashCode()
- {
- return Objects.hash(nullsFraction, distinctValuesCount, dataSize, range);
+ public Builder setDataSize(Estimate dataSize) {
+ this.dataSize = requireNonNull(dataSize, "dataSize is null");
+ return this;
}
- @Override
- public String toString()
- {
- return "ColumnStatistics{" +
- "nullsFraction=" + nullsFraction +
- ", distinctValuesCount=" + distinctValuesCount +
- ", dataSize=" + dataSize +
- ", range=" + range +
- '}';
+ public Builder setRange(DoubleRange range) {
+ this.range = Optional.of(requireNonNull(range, "range is null"));
+ return this;
}
- public static Builder builder()
- {
- return new Builder();
+ public Builder setRange(Optional<DoubleRange> range) {
+ this.range = requireNonNull(range, "range is null");
+ return this;
}
- /**
- * If one of the estimates below is unspecified (i.e. left as "unknown"),
- * the optimizer may not be able to derive the statistics needed for
- * performing cost-based query plan optimizations.
- */
- public static final class Builder
- {
- private Estimate nullsFraction = Estimate.unknown();
- private Estimate distinctValuesCount = Estimate.unknown();
- private Estimate dataSize = Estimate.unknown();
- private Optional<DoubleRange> range = Optional.empty();
-
- public Builder setNullsFraction(Estimate nullsFraction)
- {
- this.nullsFraction = requireNonNull(nullsFraction, "nullsFraction is null");
- return this;
- }
-
- public Builder setDistinctValuesCount(Estimate distinctValuesCount)
- {
- this.distinctValuesCount = requireNonNull(distinctValuesCount, "distinctValuesCount is null");
- return this;
- }
-
- public Builder setDataSize(Estimate dataSize)
- {
- this.dataSize = requireNonNull(dataSize, "dataSize is null");
- return this;
- }
-
- public Builder setRange(DoubleRange range)
- {
- this.range = Optional.of(requireNonNull(range, "range is null"));
- return this;
- }
-
- public Builder setRange(Optional<DoubleRange> range)
- {
- this.range = requireNonNull(range, "range is null");
- return this;
- }
-
- public ColumnStatistics build()
- {
- return new ColumnStatistics(nullsFraction, distinctValuesCount, dataSize, range);
- }
+ public ColumnStatistics build() {
+ return new ColumnStatistics(nullsFraction, distinctValuesCount, dataSize, range);
}
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/DoubleRange.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/DoubleRange.java
index 11e97226224..fb3ea2a1366 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/DoubleRange.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/DoubleRange.java
@@ -26,93 +26,79 @@ import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static org.apache.iotdb.db.queryengine.plan.relational.cost.StatsUtil.toStatsRepresentation;
-public class DoubleRange
-{
- private final double min;
- private final double max;
+public class DoubleRange {
+ private final double min;
+ private final double max;
- /**
- * Creates DoubleRange from Trino native representation.
- */
- public static Optional<DoubleRange> from(Type type, Object minTrinoNativeValue, Object maxTrinoNativeValue)
- {
- requireNonNull(minTrinoNativeValue, "minTrinoNativeValue is null");
- requireNonNull(maxTrinoNativeValue, "maxTrinoNativeValue is null");
+ /** Creates DoubleRange from Trino native representation. */
+ public static Optional<DoubleRange> from(
+ Type type, Object minTrinoNativeValue, Object maxTrinoNativeValue) {
+ requireNonNull(minTrinoNativeValue, "minTrinoNativeValue is null");
+ requireNonNull(maxTrinoNativeValue, "maxTrinoNativeValue is null");
- OptionalDouble min = toStatsRepresentation(type, minTrinoNativeValue);
- OptionalDouble max = toStatsRepresentation(type, maxTrinoNativeValue);
+ OptionalDouble min = toStatsRepresentation(type, minTrinoNativeValue);
+ OptionalDouble max = toStatsRepresentation(type, maxTrinoNativeValue);
- if (!min.isPresent() && !max.isPresent()) {
- return Optional.empty();
- }
- if (!min.isPresent() || !max.isPresent()) {
- throw new IllegalStateException(format(
- "One of min/max was converted to stats representation while the other was not for type %s: %s, %s",
- type,
- min,
- max));
- }
- return Optional.of(new DoubleRange(min.getAsDouble(), max.getAsDouble()));
+ if (!min.isPresent() && !max.isPresent()) {
+ return Optional.empty();
}
-
- public DoubleRange(double min, double max)
- {
- if (isNaN(min)) {
- throw new IllegalArgumentException("min must not be NaN");
- }
- if (isNaN(max)) {
- throw new IllegalArgumentException("max must not be NaN");
- }
- if (min > max) {
- throw new IllegalArgumentException(format("max must be greater than or equal to min. min: %s. max: %s. ", min, max));
- }
- this.min = min;
- this.max = max;
+ if (!min.isPresent() || !max.isPresent()) {
+ throw new IllegalStateException(
+ format(
+ "One of min/max was converted to stats representation while the other was not for type %s: %s, %s",
+ type, min, max));
}
+ return Optional.of(new DoubleRange(min.getAsDouble(), max.getAsDouble()));
+ }
- public double getMin()
- {
- return min;
+ public DoubleRange(double min, double max) {
+ if (isNaN(min)) {
+ throw new IllegalArgumentException("min must not be NaN");
}
-
- public double getMax()
- {
- return max;
+ if (isNaN(max)) {
+ throw new IllegalArgumentException("max must not be NaN");
}
-
- public static DoubleRange union(DoubleRange first, DoubleRange second)
- {
- requireNonNull(first, "first is null");
- requireNonNull(second, "second is null");
- return new DoubleRange(min(first.min, second.min), max(first.max, second.max));
+ if (min > max) {
+ throw new IllegalArgumentException(
+ format("max must be greater than or equal to min. min: %s. max: %s. ", min, max));
}
+ this.min = min;
+ this.max = max;
+ }
- @Override
- public boolean equals(Object o)
- {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- DoubleRange range = (DoubleRange) o;
- return Double.compare(range.min, min) == 0 &&
- Double.compare(range.max, max) == 0;
- }
+ public double getMin() {
+ return min;
+ }
- @Override
- public int hashCode()
- {
- return Objects.hash(min, max);
- }
+ public double getMax() {
+ return max;
+ }
- @Override
- public String toString()
- {
- return "DoubleRange{" +
- "min=" + min +
- ", max=" + max +
- '}';
+ public static DoubleRange union(DoubleRange first, DoubleRange second) {
+ requireNonNull(first, "first is null");
+ requireNonNull(second, "second is null");
+ return new DoubleRange(min(first.min, second.min), max(first.max, second.max));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
}
+ DoubleRange range = (DoubleRange) o;
+ return Double.compare(range.min, min) == 0 && Double.compare(range.max, max) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(min, max);
+ }
+
+ @Override
+ public String toString() {
+ return "DoubleRange{" + "min=" + min + ", max=" + max + '}';
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/Estimate.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/Estimate.java
index 93ce50eb182..85366b89090 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/Estimate.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/Estimate.java
@@ -20,75 +20,65 @@ import static java.lang.Double.NaN;
import static java.lang.Double.isInfinite;
import static java.lang.Double.isNaN;
-public final class Estimate
-{
- // todo eventually add some notion of statistic reliability
- // Skipping for now as there hard to compute it properly and so far we do not have
- // usecase for that.
+public final class Estimate {
+ // todo eventually add some notion of statistic reliability
+ // Skipping for now as there hard to compute it properly and so far we do not have
+ // usecase for that.
- private static final Estimate UNKNOWN = new Estimate(NaN);
- private static final Estimate ZERO = new Estimate(0);
+ private static final Estimate UNKNOWN = new Estimate(NaN);
+ private static final Estimate ZERO = new Estimate(0);
- private final double value;
+ private final double value;
- public static Estimate unknown()
- {
- return UNKNOWN;
- }
+ public static Estimate unknown() {
+ return UNKNOWN;
+ }
- public static Estimate zero()
- {
- return ZERO;
- }
+ public static Estimate zero() {
+ return ZERO;
+ }
- public static Estimate of(double value)
- {
- if (isNaN(value)) {
- throw new IllegalArgumentException("value is NaN");
- }
- if (isInfinite(value)) {
- throw new IllegalArgumentException("value is infinite");
- }
- return new Estimate(value);
+ public static Estimate of(double value) {
+ if (isNaN(value)) {
+ throw new IllegalArgumentException("value is NaN");
}
-
- private Estimate(double value)
- {
- this.value = value;
+ if (isInfinite(value)) {
+ throw new IllegalArgumentException("value is infinite");
}
+ return new Estimate(value);
+ }
- public boolean isUnknown()
- {
- return isNaN(value);
- }
+ private Estimate(double value) {
+ this.value = value;
+ }
- public double getValue()
- {
- return value;
- }
+ public boolean isUnknown() {
+ return isNaN(value);
+ }
- @Override
- public boolean equals(Object o)
- {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- Estimate estimate = (Estimate) o;
- return Double.compare(estimate.value, value) == 0;
- }
+ public double getValue() {
+ return value;
+ }
- @Override
- public int hashCode()
- {
- return Objects.hash(value);
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
}
-
- @Override
- public String toString()
- {
- return String.valueOf(value);
+ if (o == null || getClass() != o.getClass()) {
+ return false;
}
+ Estimate estimate = (Estimate) o;
+ return Double.compare(estimate.value, value) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value);
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/StatsUtil.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/StatsUtil.java
index 4d4ee8b3388..94d89301ba9 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/StatsUtil.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/StatsUtil.java
@@ -13,36 +13,31 @@
*/
package org.apache.iotdb.db.queryengine.plan.relational.cost;
-
import org.apache.iotdb.tsfile.read.common.type.Type;
import java.util.OptionalDouble;
-import static java.lang.Float.intBitsToFloat;
-import static java.lang.Math.toIntExact;
import static java.util.Objects.requireNonNull;
import static org.apache.iotdb.tsfile.read.common.type.BooleanType.BOOLEAN;
import static org.apache.iotdb.tsfile.read.common.type.DoubleType.DOUBLE;
import static org.apache.iotdb.tsfile.read.common.type.IntType.INT32;
-public final class StatsUtil
-{
- private StatsUtil() {}
+public final class StatsUtil {
+ private StatsUtil() {}
- public static OptionalDouble toStatsRepresentation(Type type, Object value)
- {
- requireNonNull(type, "type is null");
- requireNonNull(value, "value is null");
+ public static OptionalDouble toStatsRepresentation(Type type, Object value) {
+ requireNonNull(type, "type is null");
+ requireNonNull(value, "value is null");
- if (type == BOOLEAN) {
- return OptionalDouble.of((boolean) value ? 1 : 0);
- }
- if (type == INT32) {
- return OptionalDouble.of((long) value);
- }
- if (type == DOUBLE) {
- return OptionalDouble.of((double) value);
- }
- return OptionalDouble.empty();
+ if (type == BOOLEAN) {
+ return OptionalDouble.of((boolean) value ? 1 : 0);
+ }
+ if (type == INT32) {
+ return OptionalDouble.of((long) value);
+ }
+ if (type == DOUBLE) {
+ return OptionalDouble.of((double) value);
}
+ return OptionalDouble.empty();
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/TableStatistics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/TableStatistics.java
index 5a3db41a242..9a610107d91 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/TableStatistics.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/TableStatistics.java
@@ -14,7 +14,6 @@
package org.apache.iotdb.db.queryengine.plan.relational.cost;
-
import org.apache.iotdb.db.queryengine.plan.relational.metadata.ColumnHandle;
import java.util.LinkedHashMap;
@@ -25,98 +24,89 @@ import static java.lang.String.format;
import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.requireNonNull;
-public final class TableStatistics
-{
- private static final TableStatistics EMPTY = TableStatistics.builder().build();
-
- private final Estimate rowCount;
- private final Map<ColumnHandle, ColumnStatistics> columnStatistics;
+public final class TableStatistics {
+ private static final TableStatistics EMPTY = TableStatistics.builder().build();
- public static TableStatistics empty()
- {
- return EMPTY;
- }
+ private final Estimate rowCount;
+ private final Map<ColumnHandle, ColumnStatistics> columnStatistics;
- public TableStatistics(Estimate rowCount, Map<ColumnHandle, ColumnStatistics> columnStatistics)
- {
- this.rowCount = requireNonNull(rowCount, "rowCount cannot be null");
- if (!rowCount.isUnknown() && rowCount.getValue() < 0) {
- throw new IllegalArgumentException(format("rowCount must be greater than or equal to 0: %s", rowCount.getValue()));
- }
- this.columnStatistics = unmodifiableMap(requireNonNull(columnStatistics, "columnStatistics cannot be null"));
- }
+ public static TableStatistics empty() {
+ return EMPTY;
+ }
- public Estimate getRowCount()
- {
- return rowCount;
+ public TableStatistics(Estimate rowCount, Map<ColumnHandle, ColumnStatistics> columnStatistics) {
+ this.rowCount = requireNonNull(rowCount, "rowCount cannot be null");
+ if (!rowCount.isUnknown() && rowCount.getValue() < 0) {
+ throw new IllegalArgumentException(
+ format("rowCount must be greater than or equal to 0: %s", rowCount.getValue()));
}
-
- public Map<ColumnHandle, ColumnStatistics> getColumnStatistics()
- {
- return columnStatistics;
+ this.columnStatistics =
+ unmodifiableMap(requireNonNull(columnStatistics, "columnStatistics cannot be null"));
+ }
+
+ public Estimate getRowCount() {
+ return rowCount;
+ }
+
+ public Map<ColumnHandle, ColumnStatistics> getColumnStatistics() {
+ return columnStatistics;
+ }
+
+ public boolean isEmpty() {
+ return equals(empty());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
}
-
- public boolean isEmpty()
- {
- return equals(empty());
+ if (o == null || getClass() != o.getClass()) {
+ return false;
}
-
- @Override
- public boolean equals(Object o)
- {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- TableStatistics that = (TableStatistics) o;
- return Objects.equals(rowCount, that.rowCount) &&
- Objects.equals(columnStatistics, that.columnStatistics);
- }
-
- @Override
- public int hashCode()
- {
- return Objects.hash(rowCount, columnStatistics);
- }
-
- @Override
- public String toString()
- {
- return "TableStatistics{" +
- "rowCount=" + rowCount +
- ", columnStatistics=" + columnStatistics +
- '}';
+ TableStatistics that = (TableStatistics) o;
+ return Objects.equals(rowCount, that.rowCount)
+ && Objects.equals(columnStatistics, that.columnStatistics);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(rowCount, columnStatistics);
+ }
+
+ @Override
+ public String toString() {
+ return "TableStatistics{"
+ + "rowCount="
+ + rowCount
+ + ", columnStatistics="
+ + columnStatistics
+ + '}';
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private final Map<ColumnHandle, ColumnStatistics> columnStatisticsMap = new LinkedHashMap<>();
+ private Estimate rowCount = Estimate.unknown();
+
+ public Builder setRowCount(Estimate rowCount) {
+ this.rowCount = requireNonNull(rowCount, "rowCount cannot be null");
+ return this;
}
- public static Builder builder()
- {
- return new Builder();
+ public Builder setColumnStatistics(
+ ColumnHandle columnHandle, ColumnStatistics columnStatistics) {
+ requireNonNull(columnHandle, "columnHandle cannot be null");
+ requireNonNull(columnStatistics, "columnStatistics cannot be null");
+ this.columnStatisticsMap.put(columnHandle, columnStatistics);
+ return this;
}
- public static final class Builder
- {
- private final Map<ColumnHandle, ColumnStatistics> columnStatisticsMap = new LinkedHashMap<>();
- private Estimate rowCount = Estimate.unknown();
-
- public Builder setRowCount(Estimate rowCount)
- {
- this.rowCount = requireNonNull(rowCount, "rowCount cannot be null");
- return this;
- }
-
- public Builder setColumnStatistics(ColumnHandle columnHandle, ColumnStatistics columnStatistics)
- {
- requireNonNull(columnHandle, "columnHandle cannot be null");
- requireNonNull(columnStatistics, "columnStatistics cannot be null");
- this.columnStatisticsMap.put(columnHandle, columnStatistics);
- return this;
- }
-
- public TableStatistics build()
- {
- return new TableStatistics(rowCount, columnStatisticsMap);
- }
+ public TableStatistics build() {
+ return new TableStatistics(rowCount, columnStatisticsMap);
}
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/TableStatsProvider.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/TableStatsProvider.java
index 899b5683bcf..72fd4d6932e 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/TableStatsProvider.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/cost/TableStatsProvider.java
@@ -16,5 +16,5 @@ package org.apache.iotdb.db.queryengine.plan.relational.cost;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableHandle;
public interface TableStatsProvider {
- TableStatistics getTableStatistics(TableHandle tableHandle);
+ TableStatistics getTableStatistics(TableHandle tableHandle);
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/Assignments.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/Assignments.java
index 50703a756ab..992ada2a745 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/Assignments.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/Assignments.java
@@ -13,12 +13,13 @@
*/
package org.apache.iotdb.db.queryengine.plan.relational.planner;
+import org.apache.iotdb.db.relational.sql.tree.Expression;
+
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
-import org.apache.iotdb.db.relational.sql.tree.Expression;
import java.util.Collection;
import java.util.LinkedHashMap;
@@ -35,234 +36,196 @@ import static com.google.common.base.Preconditions.checkState;
import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;
-public class Assignments
-{
- public static Builder builder()
- {
- return new Builder();
- }
+public class Assignments {
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static Assignments identity(Symbol... symbols) {
+ return identity(asList(symbols));
+ }
+
+ public static Assignments identity(Iterable<Symbol> symbols) {
+ return builder().putIdentities(symbols).build();
+ }
+
+ public static Assignments copyOf(Map<Symbol, Expression> assignments) {
+ return builder().putAll(assignments).build();
+ }
+
+ public static Assignments of() {
+ return builder().build();
+ }
+
+ public static Assignments of(Symbol symbol, Expression expression) {
+ return builder().put(symbol, expression).build();
+ }
+
+ public static Assignments of(
+ Symbol symbol1, Expression expression1, Symbol symbol2, Expression expression2) {
+ return builder().put(symbol1, expression1).put(symbol2, expression2).build();
+ }
- public static Assignments identity(Symbol... symbols)
- {
- return identity(asList(symbols));
- }
+ private final Map<Symbol, Expression> assignments;
- public static Assignments identity(Iterable<Symbol> symbols)
- {
- return builder().putIdentities(symbols).build();
- }
+ @JsonCreator
+ public Assignments(@JsonProperty("assignments") Map<Symbol, Expression> assignments) {
+ this.assignments = ImmutableMap.copyOf(requireNonNull(assignments, "assignments is null"));
+ }
- public static Assignments copyOf(Map<Symbol, Expression> assignments)
- {
- return builder()
- .putAll(assignments)
- .build();
- }
+ public List<Symbol> getOutputs() {
+ return ImmutableList.copyOf(assignments.keySet());
+ }
- public static Assignments of()
- {
- return builder().build();
- }
+ @JsonProperty("assignments")
+ public Map<Symbol, Expression> getMap() {
+ return assignments;
+ }
- public static Assignments of(Symbol symbol, Expression expression)
- {
- return builder().put(symbol, expression).build();
- }
+ public Assignments rewrite(Function<Expression, Expression> rewrite) {
+ return assignments.entrySet().stream()
+ .map(entry -> Maps.immutableEntry(entry.getKey(), rewrite.apply(entry.getValue())))
+ .collect(toAssignments());
+ }
- public static Assignments of(Symbol symbol1, Expression expression1, Symbol symbol2, Expression expression2)
- {
- return builder().put(symbol1, expression1).put(symbol2, expression2).build();
- }
+ public Assignments filter(Collection<Symbol> symbols) {
+ return filter(symbols::contains);
+ }
- private final Map<Symbol, Expression> assignments;
+ public Assignments filter(Predicate<Symbol> predicate) {
+ return assignments.entrySet().stream()
+ .filter(entry -> predicate.test(entry.getKey()))
+ .collect(toAssignments());
+ }
- @JsonCreator
- public Assignments(@JsonProperty("assignments") Map<Symbol, Expression> assignments)
- {
- this.assignments = ImmutableMap.copyOf(requireNonNull(assignments, "assignments is null"));
- }
+ private Collector<Entry<Symbol, Expression>, Builder, Assignments> toAssignments() {
+ return Collector.of(
+ Assignments::builder,
+ (builder, entry) -> builder.put(entry.getKey(), entry.getValue()),
+ (left, right) -> {
+ left.putAll(right.build());
+ return left;
+ },
+ Builder::build);
+ }
- public List<Symbol> getOutputs()
- {
- return ImmutableList.copyOf(assignments.keySet());
- }
+ public Collection<Expression> getExpressions() {
+ return assignments.values();
+ }
- @JsonProperty("assignments")
- public Map<Symbol, Expression> getMap()
- {
- return assignments;
- }
+ public Set<Symbol> getSymbols() {
+ return assignments.keySet();
+ }
- public Assignments rewrite(Function<Expression, Expression> rewrite)
- {
- return assignments.entrySet().stream()
- .map(entry -> Maps.immutableEntry(entry.getKey(), rewrite.apply(entry.getValue())))
- .collect(toAssignments());
- }
+ public Set<Entry<Symbol, Expression>> entrySet() {
+ return assignments.entrySet();
+ }
- public Assignments filter(Collection<Symbol> symbols)
- {
- return filter(symbols::contains);
- }
+ public Expression get(Symbol symbol) {
+ return assignments.get(symbol);
+ }
- public Assignments filter(Predicate<Symbol> predicate)
- {
- return assignments.entrySet().stream()
- .filter(entry -> predicate.test(entry.getKey()))
- .collect(toAssignments());
- }
+ public int size() {
+ return assignments.size();
+ }
- private Collector<Entry<Symbol, Expression>, Builder, Assignments> toAssignments()
- {
- return Collector.of(
- Assignments::builder,
- (builder, entry) -> builder.put(entry.getKey(), entry.getValue()),
- (left, right) -> {
- left.putAll(right.build());
- return left;
- },
- Builder::build);
- }
+ public boolean isEmpty() {
+ return size() == 0;
+ }
- public Collection<Expression> getExpressions()
- {
- return assignments.values();
- }
+ public void forEach(BiConsumer<Symbol, Expression> consumer) {
+ assignments.forEach(consumer);
+ }
- public Set<Symbol> getSymbols()
- {
- return assignments.keySet();
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
}
- public Set<Entry<Symbol, Expression>> entrySet()
- {
- return assignments.entrySet();
+ Assignments that = (Assignments) o;
+
+ return assignments.equals(that.assignments);
+ }
+
+ @Override
+ public int hashCode() {
+ return assignments.hashCode();
+ }
+
+ public static class Assignment {
+ private final Symbol output;
+ private final Expression expression;
+
+ public Assignment(Symbol output, Expression expression) {
+ this.output = requireNonNull(output, "output is null");
+ this.expression = requireNonNull(expression, "expression is null");
}
- public Expression get(Symbol symbol)
- {
- return assignments.get(symbol);
+ public Symbol getOutput() {
+ return output;
}
- public int size()
- {
- return assignments.size();
+ public Expression getExpression() {
+ return expression;
}
+ }
- public boolean isEmpty()
- {
- return size() == 0;
+ public static class Builder {
+ private final Map<Symbol, Expression> assignments = new LinkedHashMap<>();
+
+ public Builder putAll(Assignments assignments) {
+ return putAll(assignments.getMap());
}
- public void forEach(BiConsumer<Symbol, Expression> consumer)
- {
- assignments.forEach(consumer);
+ public Builder putAll(Map<Symbol, ? extends Expression> assignments) {
+ for (Entry<Symbol, ? extends Expression> assignment : assignments.entrySet()) {
+ put(assignment.getKey(), assignment.getValue());
+ }
+ return this;
}
- @Override
- public boolean equals(Object o)
- {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
+ public Builder put(Symbol symbol, Expression expression) {
+ if (assignments.containsKey(symbol)) {
+ Expression assignment = assignments.get(symbol);
+ checkState(
+ assignment.equals(expression),
+ "Symbol %s already has assignment %s, while adding %s",
+ symbol,
+ assignment,
+ expression);
+ }
+ assignments.put(symbol, expression);
+ return this;
+ }
- Assignments that = (Assignments) o;
+ public Builder put(Entry<Symbol, Expression> assignment) {
+ put(assignment.getKey(), assignment.getValue());
+ return this;
+ }
- return assignments.equals(that.assignments);
+ public Builder putIdentities(Iterable<Symbol> symbols) {
+ for (Symbol symbol : symbols) {
+ putIdentity(symbol);
+ }
+ return this;
}
- @Override
- public int hashCode()
- {
- return assignments.hashCode();
+ public Builder putIdentity(Symbol symbol) {
+ put(symbol, symbol.toSymbolReference());
+ return this;
}
- public static class Assignment
- {
- private final Symbol output;
- private final Expression expression;
-
- public Assignment(Symbol output, Expression expression)
- {
- this.output = requireNonNull(output, "output is null");
- this.expression = requireNonNull(expression, "expression is null");
- }
-
- public Symbol getOutput()
- {
- return output;
- }
-
- public Expression getExpression()
- {
- return expression;
- }
+ public Assignments build() {
+ return new Assignments(assignments);
}
- public static class Builder
- {
- private final Map<Symbol, Expression> assignments = new LinkedHashMap<>();
-
- public Builder putAll(Assignments assignments)
- {
- return putAll(assignments.getMap());
- }
-
- public Builder putAll(Map<Symbol, ? extends Expression> assignments)
- {
- for (Entry<Symbol, ? extends Expression> assignment : assignments.entrySet()) {
- put(assignment.getKey(), assignment.getValue());
- }
- return this;
- }
-
- public Builder put(Symbol symbol, Expression expression)
- {
- if (assignments.containsKey(symbol)) {
- Expression assignment = assignments.get(symbol);
- checkState(
- assignment.equals(expression),
- "Symbol %s already has assignment %s, while adding %s",
- symbol,
- assignment,
- expression);
- }
- assignments.put(symbol, expression);
- return this;
- }
-
- public Builder put(Entry<Symbol, Expression> assignment)
- {
- put(assignment.getKey(), assignment.getValue());
- return this;
- }
-
- public Builder putIdentities(Iterable<Symbol> symbols)
- {
- for (Symbol symbol : symbols) {
- putIdentity(symbol);
- }
- return this;
- }
-
- public Builder putIdentity(Symbol symbol)
- {
- put(symbol, symbol.toSymbolReference());
- return this;
- }
-
- public Assignments build()
- {
- return new Assignments(assignments);
- }
-
- public Builder add(Assignment assignment)
- {
- put(assignment.getOutput(), assignment.getExpression());
- return this;
- }
+ public Builder add(Assignment assignment) {
+ put(assignment.getOutput(), assignment.getExpression());
+ return this;
}
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java
index e89bad05779..1f578074143 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java
@@ -13,123 +13,102 @@
*/
package org.apache.iotdb.db.queryengine.plan.relational.planner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.errorprone.annotations.MustBeClosed;
-import io.airlift.log.Logger;
import org.apache.iotdb.commons.exception.IoTDBException;
import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
import org.apache.iotdb.db.queryengine.execution.warnings.WarningCollector;
-import org.apache.iotdb.db.queryengine.plan.analyze.TypeProvider;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Field;
-import org.apache.iotdb.db.queryengine.plan.relational.analyzer.NodeRef;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.RelationType;
-import org.apache.iotdb.db.queryengine.plan.relational.cost.CachingTableStatsProvider;
-import org.apache.iotdb.db.queryengine.plan.relational.cost.TableStatistics;
-import org.apache.iotdb.db.queryengine.plan.relational.cost.TableStatsProvider;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata;
-import org.apache.iotdb.db.queryengine.plan.relational.metadata.ResolvedFunction;
-import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableHandle;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.OutputNode;
-import org.apache.iotdb.db.relational.sql.tree.Expression;
import org.apache.iotdb.db.relational.sql.tree.Query;
import org.apache.iotdb.db.relational.sql.tree.Statement;
import org.apache.iotdb.db.relational.sql.tree.Table;
import org.apache.iotdb.session.Session;
-import javax.annotation.Nonnull;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.function.Function;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
-import static com.google.common.collect.Streams.forEachPair;
-import static com.google.common.collect.Streams.zip;
-import static java.util.Objects.requireNonNull;
-
-public class LogicalPlanner
-{
- private static final Logger LOG = Logger.get(LogicalPlanner.class);
-
- public enum Stage {
- CREATED, OPTIMIZED, OPTIMIZED_AND_VALIDATED
- }
-
- private final MPPQueryContext context;
-
- private final Session session;
- private final SymbolAllocator symbolAllocator = new SymbolAllocator();
- private final Metadata metadata;
- private final WarningCollector warningCollector;
-
- public LogicalPlanner(
- MPPQueryContext context,
- Metadata metadata,
- Session session,
- WarningCollector warningCollector) {
- this.context = context;
- this.metadata = metadata;
- this.session = requireNonNull(session, "session is null");
- this.warningCollector = requireNonNull(warningCollector, "warningCollector is null");
- }
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import io.airlift.log.Logger;
- public PlanNode plan(Analysis analysis) throws IoTDBException {
- return planStatement(analysis, analysis.getStatement());
- }
+import static java.util.Objects.requireNonNull;
- public PlanNode planStatement(Analysis analysis, Statement statement) throws IoTDBException {
- return createOutputPlan(planStatementWithoutOutput(analysis, statement), analysis);
+public class LogicalPlanner {
+ private static final Logger LOG = Logger.get(LogicalPlanner.class);
+
+ public enum Stage {
+ CREATED,
+ OPTIMIZED,
+ OPTIMIZED_AND_VALIDATED
+ }
+
+ private final MPPQueryContext context;
+
+ private final Session session;
+ private final SymbolAllocator symbolAllocator = new SymbolAllocator();
+ private final Metadata metadata;
+ private final WarningCollector warningCollector;
+
+ public LogicalPlanner(
+ MPPQueryContext context,
+ Metadata metadata,
+ Session session,
+ WarningCollector warningCollector) {
+ this.context = context;
+ this.metadata = metadata;
+ this.session = requireNonNull(session, "session is null");
+ this.warningCollector = requireNonNull(warningCollector, "warningCollector is null");
+ }
+
+ public PlanNode plan(Analysis analysis) throws IoTDBException {
+ return planStatement(analysis, analysis.getStatement());
+ }
+
+ public PlanNode planStatement(Analysis analysis, Statement statement) throws IoTDBException {
+ return createOutputPlan(planStatementWithoutOutput(analysis, statement), analysis);
+ }
+
+ private RelationPlan planStatementWithoutOutput(Analysis analysis, Statement statement)
+ throws IoTDBException {
+ if (statement instanceof Query) {
+ return createRelationPlan(analysis, (Query) statement);
}
-
- private RelationPlan planStatementWithoutOutput(Analysis analysis, Statement statement) throws IoTDBException {
- if (statement instanceof Query) {
- return createRelationPlan(analysis, (Query) statement);
- }
- throw new IoTDBException("Unsupported statement type " + statement.getClass().getSimpleName(), -1);
+ throw new IoTDBException(
+ "Unsupported statement type " + statement.getClass().getSimpleName(), -1);
+ }
+
+ private PlanNode createOutputPlan(RelationPlan plan, Analysis analysis) {
+ ImmutableList.Builder<Symbol> outputs = ImmutableList.builder();
+ ImmutableList.Builder<String> names = ImmutableList.builder();
+
+ int columnNumber = 0;
+ // TODO perfect the logic of outputDescriptor
+ RelationType outputDescriptor = analysis.getOutputDescriptor();
+ for (Field field : outputDescriptor.getVisibleFields()) {
+ String name = field.getName().orElse("_col" + columnNumber);
+ names.add(name);
+
+ int fieldIndex = outputDescriptor.indexOf(field);
+ Symbol symbol = plan.getSymbol(fieldIndex);
+ outputs.add(symbol);
+
+ columnNumber++;
}
- private PlanNode createOutputPlan(RelationPlan plan, Analysis analysis) {
- ImmutableList.Builder<Symbol> outputs = ImmutableList.builder();
- ImmutableList.Builder<String> names = ImmutableList.builder();
+ return new OutputNode(
+ context.getQueryId().genPlanNodeId(), plan.getRoot(), names.build(), outputs.build());
+ }
- int columnNumber = 0;
- // TODO perfect the logic of outputDescriptor
- RelationType outputDescriptor = analysis.getOutputDescriptor();
- for (Field field : outputDescriptor.getVisibleFields()) {
- String name = field.getName().orElse("_col" + columnNumber);
- names.add(name);
+ private RelationPlan createRelationPlan(Analysis analysis, Query query) {
+ return getRelationPlanner(analysis).process(query, null);
+ }
- int fieldIndex = outputDescriptor.indexOf(field);
- Symbol symbol = plan.getSymbol(fieldIndex);
- outputs.add(symbol);
-
- columnNumber++;
- }
-
- return new OutputNode(context.getQueryId().genPlanNodeId(), plan.getRoot(), names.build(), outputs.build());
- }
+ private RelationPlan createRelationPlan(Analysis analysis, Table table) {
+ return getRelationPlanner(analysis).process(table, null);
+ }
- private RelationPlan createRelationPlan(Analysis analysis, Query query) {
- return getRelationPlanner(analysis).process(query, null);
- }
-
- private RelationPlan createRelationPlan(Analysis analysis, Table table) {
- return getRelationPlanner(analysis).process(table, null);
- }
-
- private RelationPlanner getRelationPlanner(Analysis analysis) {
- return new RelationPlanner(analysis, symbolAllocator, context.getQueryId(), session, ImmutableMap.of());
- }
+ private RelationPlanner getRelationPlanner(Analysis analysis) {
+ return new RelationPlanner(
+ analysis, symbolAllocator, context.getQueryId(), session, ImmutableMap.of());
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/OrderingScheme.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/OrderingScheme.java
new file mode 100644
index 00000000000..73ba7044f35
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/OrderingScheme.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed 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.iotdb.db.queryengine.plan.relational.planner;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static java.util.Objects.requireNonNull;
+
+public class OrderingScheme {
+ private final List<Symbol> orderBy;
+ private final Map<Symbol, SortOrder> orderings;
+
+ @JsonCreator
+ public OrderingScheme(
+ @JsonProperty("orderBy") List<Symbol> orderBy,
+ @JsonProperty("orderings") Map<Symbol, SortOrder> orderings) {
+ requireNonNull(orderBy, "orderBy is null");
+ requireNonNull(orderings, "orderings is null");
+ checkArgument(!orderBy.isEmpty(), "orderBy is empty");
+ checkArgument(
+ orderings.keySet().equals(ImmutableSet.copyOf(orderBy)),
+ "orderBy keys and orderings don't match");
+ this.orderBy = ImmutableList.copyOf(orderBy);
+ this.orderings = ImmutableMap.copyOf(orderings);
+ }
+
+ @JsonProperty
+ public List<Symbol> getOrderBy() {
+ return orderBy;
+ }
+
+ @JsonProperty
+ public Map<Symbol, SortOrder> getOrderings() {
+ return orderings;
+ }
+
+ public List<SortOrder> getOrderingList() {
+ return orderBy.stream().map(orderings::get).collect(toImmutableList());
+ }
+
+ public SortOrder getOrdering(Symbol symbol) {
+ checkArgument(orderings.containsKey(symbol), "No ordering for symbol: %s", symbol);
+ return orderings.get(symbol);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ OrderingScheme that = (OrderingScheme) o;
+ return Objects.equals(orderBy, that.orderBy) && Objects.equals(orderings, that.orderings);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(orderBy, orderings);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this).add("orderBy", orderBy).add("orderings", orderings).toString();
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/OrderingTranslator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/OrderingTranslator.java
new file mode 100644
index 00000000000..faf4ba41387
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/OrderingTranslator.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed 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.iotdb.db.queryengine.plan.relational.planner;
+
+import org.apache.iotdb.db.relational.sql.tree.SortItem;
+
+public class OrderingTranslator {
+ private OrderingTranslator() {}
+
+ public static SortOrder sortItemToSortOrder(SortItem sortItem) {
+ if (sortItem.getOrdering() == SortItem.Ordering.ASCENDING) {
+ if (sortItem.getNullOrdering() == SortItem.NullOrdering.FIRST) {
+ return SortOrder.ASC_NULLS_FIRST;
+ }
+ return SortOrder.ASC_NULLS_LAST;
+ }
+
+ if (sortItem.getNullOrdering() == SortItem.NullOrdering.FIRST) {
+ return SortOrder.DESC_NULLS_FIRST;
+ }
+ return SortOrder.DESC_NULLS_LAST;
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanBuilder.java
index 4dcb6b30041..cd29f30a1c4 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanBuilder.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanBuilder.java
@@ -13,7 +13,6 @@
*/
package org.apache.iotdb.db.queryengine.plan.relational.planner;
-import com.google.common.collect.ImmutableMap;
import org.apache.iotdb.db.queryengine.common.QueryId;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis;
@@ -22,108 +21,70 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ProjectNode;
import org.apache.iotdb.db.relational.sql.tree.Expression;
import org.apache.iotdb.session.Session;
+import com.google.common.collect.ImmutableMap;
+
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.function.BiFunction;
-import java.util.function.BiPredicate;
import static java.util.Objects.requireNonNull;
-import static org.apache.iotdb.db.queryengine.plan.relational.planner.ScopeAware.scopeAwareKey;
-
-class PlanBuilder
-{
- private final PlanNode root;
-
- public PlanBuilder(PlanNode root)
- {
- requireNonNull(root, "root is null");
-
- this.root = root;
- }
-
- public static PlanBuilder newPlanBuilder(RelationPlan plan, Analysis analysis,
- Session session)
- {
- return newPlanBuilder(plan, analysis, ImmutableMap.of(), session);
- }
-
- public static PlanBuilder newPlanBuilder(RelationPlan plan, Analysis analysis,
- Map<ScopeAware<Expression>, Symbol> mappings,
- Session session)
- {
- return new PlanBuilder(plan.getRoot());
- }
-
- public PlanBuilder withNewRoot(PlanNode root)
- {
- return new PlanBuilder(root);
- }
-
- public PlanBuilder withScope(Scope scope, List<Symbol> fields)
- {
- return new PlanBuilder(root);
- }
-
- public PlanNode getRoot()
- {
- return root;
- }
-
- public boolean canTranslate(Expression expression)
- {
- return translations.canTranslate(expression);
- }
-
- public Symbol translate(Expression expression)
- {
- return Symbol.from(translations.rewrite(expression));
- }
-
- public Expression rewrite(Expression expression)
- {
- return translations.rewrite(expression);
- }
-
- public TranslationMap getTranslations()
- {
- return translations;
- }
-
- public Scope getScope()
- {
- return translations.getScope();
- }
-
- public PlanBuilder appendProjections(Expression expressions, SymbolAllocator symbolAllocator, QueryId idAllocator)
- {
- return appendProjections(expressions, symbolAllocator, idAllocator, TranslationMap::rewrite, TranslationMap::canTranslate);
- }
-
- public <T extends Expression> PlanBuilder appendProjections(
- Iterable<T> expressions,
- SymbolAllocator symbolAllocator,
- QueryId idAllocator,
- BiFunction<TranslationMap, T, Expression> rewriter,
- BiPredicate<TranslationMap, T> alreadyHasTranslation)
- {
- Assignments.Builder projections = Assignments.builder();
-
- // add an identity projection for underlying plan
- projections.putIdentities(root.getOutputSymbols());
-
- Map<ScopeAware<Expression>, Symbol> mappings = new HashMap<>();
- for (T expression : expressions) {
- // Skip any expressions that have already been translated and recorded in the translation map, or that are duplicated in the list of exp
- if (!mappings.containsKey(scopeAwareKey(expression, translations.getAnalysis(), translations.getScope())) && !alreadyHasTranslation.test(translations, expression)) {
- Symbol symbol = symbolAllocator.newSymbol("expr", translations.getAnalysis().getType(expression));
- projections.put(symbol, rewriter.apply(translations, expression));
- mappings.put(scopeAwareKey(expression, translations.getAnalysis(), translations.getScope()), symbol);
- }
- }
-
- return new PlanBuilder(
- getTranslations().withAdditionalMappings(mappings),
- new ProjectNode(idAllocator.genPlanNodeId(), root, projections.build()));
- }
+
+class PlanBuilder {
+ private final PlanNode root;
+
+ public PlanBuilder(PlanNode root) {
+ requireNonNull(root, "root is null");
+
+ this.root = root;
+ }
+
+ public static PlanBuilder newPlanBuilder(RelationPlan plan, Analysis analysis, Session session) {
+ return newPlanBuilder(plan, analysis, ImmutableMap.of(), session);
+ }
+
+ public static PlanBuilder newPlanBuilder(
+ RelationPlan plan,
+ Analysis analysis,
+ Map<ScopeAware<Expression>, Symbol> mappings,
+ Session session) {
+ return new PlanBuilder(plan.getRoot());
+ }
+
+ public PlanBuilder withNewRoot(PlanNode root) {
+ return new PlanBuilder(root);
+ }
+
+ public PlanBuilder withScope(Scope scope, List<Symbol> fields) {
+ return new PlanBuilder(root);
+ }
+
+ public PlanNode getRoot() {
+ return root;
+ }
+
+ public <T extends Expression> PlanBuilder appendProjections(
+ Iterable<T> expressions, SymbolAllocator symbolAllocator, QueryId idAllocator) {
+ Assignments.Builder projections = Assignments.builder();
+
+ // add an identity projection for underlying plan
+ // TODO needed?
+ // projections.putIdentities(root.getOutputSymbols());
+
+ Map<ScopeAware<Expression>, Symbol> mappings = new HashMap<>();
+ // for (T expression : expressions) {
+ // // Skip any expressions that have already been translated and recorded in the
+ // translation map, or that are duplicated in the list of exp
+ // if (!mappings.containsKey(scopeAwareKey(expression, translations.getAnalysis(),
+ // translations.getScope())) && !alreadyHasTranslation.test(translations,
+ // expression)) {
+ // Symbol symbol = symbolAllocator.newSymbol("expr",
+ // translations.getAnalysis().getType(expression));
+ // projections.put(symbol, rewriter.apply(translations, expression));
+ // mappings.put(scopeAwareKey(expression, translations.getAnalysis(),
+ // translations.getScope()), symbol);
+ // }
+ // }
+
+ return new PlanBuilder(new ProjectNode(idAllocator.genPlanNodeId(), root, projections.build()));
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java
index 10b1f2fdbd2..fde2f1530ae 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java
@@ -13,1029 +13,298 @@
*/
package org.apache.iotdb.db.queryengine.plan.relational.planner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
import org.apache.iotdb.db.queryengine.common.QueryId;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.NodeRef;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.LimitNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.OffsetNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.SortNode;
import org.apache.iotdb.db.relational.sql.tree.Delete;
import org.apache.iotdb.db.relational.sql.tree.Expression;
import org.apache.iotdb.db.relational.sql.tree.Node;
import org.apache.iotdb.db.relational.sql.tree.Offset;
+import org.apache.iotdb.db.relational.sql.tree.OrderBy;
import org.apache.iotdb.db.relational.sql.tree.Query;
import org.apache.iotdb.db.relational.sql.tree.QuerySpecification;
-import org.apache.iotdb.db.relational.sql.tree.Relation;
-import org.apache.iotdb.db.relational.sql.tree.Union;
+import org.apache.iotdb.db.relational.sql.tree.SortItem;
import org.apache.iotdb.session.Session;
+import org.apache.iotdb.tsfile.read.common.type.Type;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
+import static org.apache.iotdb.db.queryengine.plan.relational.planner.OrderingTranslator.sortItemToSortOrder;
import static org.apache.iotdb.db.queryengine.plan.relational.planner.PlanBuilder.newPlanBuilder;
-class QueryPlanner
-{
- private static final int MAX_BIGINT_PRECISION = 19;
- private final Analysis analysis;
- private final SymbolAllocator symbolAllocator;
- private final QueryId idAllocator;
- private final Map<NodeRef<LambdaArgumentDeclaration>, Symbol> lambdaDeclarationToSymbolMap;
- private final PlannerContext plannerContext;
- private final Session session;
- private final SubqueryPlanner subqueryPlanner;
- private final Map<NodeRef<Node>, RelationPlan> recursiveSubqueries;
-
- QueryPlanner(
- Analysis analysis,
- SymbolAllocator symbolAllocator,
- QueryId idAllocator,
- Session session,
- Map<NodeRef<Node>, RelationPlan> recursiveSubqueries) {
- requireNonNull(analysis, "analysis is null");
- requireNonNull(symbolAllocator, "symbolAllocator is null");
- requireNonNull(idAllocator, "idAllocator is null");
- requireNonNull(session, "session is null");
- requireNonNull(recursiveSubqueries, "recursiveSubqueries is null");
-
- this.analysis = analysis;
- this.symbolAllocator = symbolAllocator;
- this.idAllocator = idAllocator;
- this.session = session;
- this.subqueryPlanner = null;
- this.recursiveSubqueries = recursiveSubqueries;
- }
-
- public RelationPlan plan(Query query) {
- PlanBuilder builder = planQueryBody(query);
-
- List<Expression> orderBy = analysis.getOrderByExpressions(query);
- // builder = subqueryPlanner.handleSubqueries(builder, orderBy, analysis.getSubqueries(query));
-
- List<Analysis.SelectExpression> selectExpressions = analysis.getSelectExpressions(query);
- List<Expression> outputs = selectExpressions.stream()
- .map(SelectExpression::getExpression)
- .collect(toImmutableList());
- builder = builder.appendProjections(Iterables.concat(orderBy, outputs), symbolAllocator, idAllocator);
-
- Optional<OrderingScheme> orderingScheme = orderingScheme(builder, query.getOrderBy(), analysis.getOrderByExpressions(query));
- builder = sort(builder, orderingScheme);
- builder = offset(builder, query.getOffset());
- builder = limit(builder, query.getLimit(), orderingScheme);
- builder = builder.appendProjections(outputs, symbolAllocator, idAllocator);
-
- return new RelationPlan(
- builder.getRoot(),
- analysis.getScope(query),
- computeOutputs(builder, outputs));
- }
-
- public RelationPlan planExpand(Query query) {
- checkArgument(analysis.isExpandableQuery(query), "query is not registered as expandable");
-
- Union union = (Union) query.getQueryBody();
- ImmutableList.Builder<NodeAndMappings> recursionSteps = ImmutableList.builder();
-
- // plan anchor relation
- Relation anchorNode = union.getRelations().get(0);
- RelationPlan anchorPlan = new RelationPlanner(analysis, symbolAllocator, idAllocator, session, recursiveSubqueries)
- .process(anchorNode, null);
-
- // prune anchor plan outputs to contain only the symbols exposed in the scope
- NodeAndMappings prunedAnchorPlan = pruneInvisibleFields(anchorPlan, idAllocator);
-
- // if the anchor plan has duplicate output symbols, add projection on top to make the symbols unique
- // This is necessary to successfully unroll recursion: the recursion step relation must follow
- // the same layout while it might not have duplicate outputs where the anchor plan did
- NodeAndMappings disambiguatedAnchorPlan = disambiguateOutputs(prunedAnchorPlan, symbolAllocator, idAllocator);
- anchorPlan = new RelationPlan(disambiguatedAnchorPlan.getNode(), analysis.getScope(query), disambiguatedAnchorPlan.getFields());
-
- recursionSteps.add(copy(anchorPlan.getRoot(), anchorPlan.getFieldMappings()));
-
- // plan recursion step
- Relation recursionStepRelation = union.getRelations().get(1);
- RelationPlan recursionStepPlan = new RelationPlanner(
- analysis,
- symbolAllocator,
- idAllocator,
- session,
- ImmutableMap.of(NodeRef.of(analysis.getRecursiveReference(query)), anchorPlan))
- .process(recursionStepRelation, null);
-
- // coerce recursion step outputs and prune them to contain only the symbols exposed in the scope
- NodeAndMappings coercedRecursionStep;
- List<Type> types = analysis.getRelationCoercion(recursionStepRelation);
- if (types == null) {
- coercedRecursionStep = pruneInvisibleFields(recursionStepPlan, idAllocator);
- }
- else {
- coercedRecursionStep = coerce(recursionStepPlan, types, symbolAllocator, idAllocator);
- }
-
- NodeAndMappings replacementSpot = new NodeAndMappings(anchorPlan.getRoot(), anchorPlan.getFieldMappings());
- PlanNode recursionStep = coercedRecursionStep.getNode();
- List<Symbol> mappings = coercedRecursionStep.getFields();
-
- // unroll recursion
- int maxRecursionDepth = getMaxRecursionDepth(session);
- for (int i = 0; i < maxRecursionDepth; i++) {
- recursionSteps.add(copy(recursionStep, mappings));
- NodeAndMappings replacement = copy(recursionStep, mappings);
-
- // if the recursion step plan has duplicate output symbols, add projection on top to make the symbols unique
- // This is necessary to successfully unroll recursion: the relation on the next recursion step must follow
- // the same layout while it might not have duplicate outputs where the plan for this step did
- replacement = disambiguateOutputs(replacement, symbolAllocator, idAllocator);
- recursionStep = replace(recursionStep, replacementSpot, replacement);
- replacementSpot = replacement;
- }
-
- // after the last recursion step, check if the recursion converged. the last step is expected to return empty result
- // 1. append window to count rows
- NodeAndMappings checkConvergenceStep = copy(recursionStep, mappings);
- Symbol countSymbol = symbolAllocator.newSymbol("count", BIGINT);
- ResolvedFunction function = .resolveBuiltinFunction("count", ImmutableList.of());
- WindowNode.Function countFunction = new WindowNode.Function(function, ImmutableList.of(), DEFAULT_FRAME, false);
-
- WindowNode windowNode = new WindowNode(
- idAllocator.getNextId(),
- checkConvergenceStep.getNode(),
- new DataOrganizationSpecification(ImmutableList.of(), Optional.empty()),
- ImmutableMap.of(countSymbol, countFunction),
- Optional.empty(),
- ImmutableSet.of(),
- 0);
-
- // 2. append filter to fail on non-empty result
- String recursionLimitExceededMessage = format("Recursion depth limit exceeded (%s). Use 'max_recursion_depth' session property to modify the limit.", maxRecursionDepth);
- Expression predicate = ifExpression(
- new ComparisonExpression(
- GREATER_THAN_OR_EQUAL,
- countSymbol.toSymbolReference(),
- new Constant(BIGINT, 0L)),
- new Cast(
- failFunction(plannerContext.getMetadata(), NOT_SUPPORTED, recursionLimitExceededMessage),
- BOOLEAN),
- TRUE_LITERAL);
- FilterNode filterNode = new FilterNode(idAllocator.getNextId(), windowNode, predicate);
-
- recursionSteps.add(new NodeAndMappings(filterNode, checkConvergenceStep.getFields()));
-
- // union all the recursion steps
- List<NodeAndMappings> recursionStepsToUnion = recursionSteps.build();
-
- List<Symbol> unionOutputSymbols = anchorPlan.getFieldMappings().stream()
- .map(symbol -> symbolAllocator.newSymbol(symbol, "_expanded"))
- .collect(toImmutableList());
-
- ImmutableListMultimap.Builder<Symbol, Symbol> unionSymbolMapping = ImmutableListMultimap.builder();
- for (NodeAndMappings plan : recursionStepsToUnion) {
- for (int i = 0; i < unionOutputSymbols.size(); i++) {
- unionSymbolMapping.put(unionOutputSymbols.get(i), plan.getFields().get(i));
- }
- }
-
- List<PlanNode> nodesToUnion = recursionStepsToUnion.stream()
- .map(NodeAndMappings::getNode)
- .collect(toImmutableList());
-
- PlanNode result = new UnionNode(idAllocator.getNextId(), nodesToUnion, unionSymbolMapping.build(), unionOutputSymbols);
-
- if (union.isDistinct()) {
- result = singleAggregation(
- idAllocator.getNextId(),
- result,
- ImmutableMap.of(),
- singleGroupingSet(result.getOutputSymbols()));
- }
-
- return new RelationPlan(result, anchorPlan.getScope(), unionOutputSymbols, outerContext);
- }
-
- // Return a copy of the plan and remapped field mappings. In the copied plan:
- // - all PlanNodeIds are replaced with new values,
- // - all symbols are replaced with new symbols.
- // Copying the plan might reorder symbols. The returned field mappings keep the original
- // order and might be used to identify the original output symbols with their copies.
- private NodeAndMappings copy(PlanNode plan, List<Symbol> fields)
- {
- return PlanCopier.copyPlan(plan, fields, symbolAllocator, idAllocator);
- }
-
- private PlanNode replace(PlanNode plan, NodeAndMappings replacementSpot, NodeAndMappings replacement)
- {
- checkArgument(
- replacementSpot.getFields().size() == replacement.getFields().size(),
- "mismatching outputs in replacement, expected: %s, got: %s",
- replacementSpot.getFields().size(),
- replacement.getFields().size());
-
- return SimplePlanRewriter.rewriteWith(new SimplePlanRewriter<Void>()
- {
- @Override
- protected PlanNode visitPlan(PlanNode node, RewriteContext<Void> context)
- {
- return node.replaceChildren(node.getSources().stream()
- .map(child -> {
- if (child == replacementSpot.getNode()) {
- // add projection to adjust symbols
- Assignments.Builder assignments = Assignments.builder();
- for (int i = 0; i < replacementSpot.getFields().size(); i++) {
- assignments.put(replacementSpot.getFields().get(i), replacement.getFields().get(i).toSymbolReference());
- }
- return new ProjectNode(idAllocator.getNextId(), replacement.getNode(), assignments.build());
- }
- return context.rewrite(child);
- })
- .collect(toImmutableList()));
- }
- }, plan, null);
- }
-
- public RelationPlan plan(QuerySpecification node) {
- PlanBuilder builder = planFrom(node);
-
- builder = filter(builder, analysis.getWhere(node), node);
- // builder = aggregate(builder, node);
- builder = filter(builder, analysis.getHaving(node), node);
- // builder = planWindowFunctions(node, builder, ImmutableList.copyOf(analysis.getWindowFunctions(node)));
- // builder = planWindowMeasures(node, builder, ImmutableList.copyOf(analysis.getWindowMeasures(node)));
-
- List<Analysis.SelectExpression> selectExpressions = analysis.getSelectExpressions(node);
- List<Expression> expressions = selectExpressions.stream()
- .map(SelectExpression::getExpression)
- .collect(toImmutableList());
- builder = subqueryPlanner.handleSubqueries(builder, expressions, analysis.getSubqueries(node));
-
- if (hasExpressionsToUnfold(selectExpressions)) {
- // pre-project the folded expressions to preserve any non-deterministic semantics of functions that might be referenced
- builder = builder.appendProjections(expressions, symbolAllocator, idAllocator);
- }
-
- List<Expression> outputs = outputExpressions(selectExpressions);
- if (node.getOrderBy().isPresent()) {
- // ORDER BY requires outputs of SELECT to be visible.
- // For queries with aggregation, it also requires grouping keys and translated aggregations.
- if (analysis.isAggregation(node)) {
- // Add projections for aggregations required by ORDER BY. After this step, grouping keys and translated
- // aggregations are visible.
- List<Expression> orderByAggregates = analysis.getOrderByAggregates(node.getOrderBy().get());
- builder = builder.appendProjections(orderByAggregates, symbolAllocator, idAllocator);
- }
-
- // Add projections for the outputs of SELECT, but stack them on top of the ones from the FROM clause so both are visible
- // when resolving the ORDER BY clause.
- builder = builder.appendProjections(outputs, symbolAllocator, idAllocator);
-
- // The new scope is the composite of the fields from the FROM and SELECT clause (local nested scopes). Fields from the bottom of
- // the scope stack need to be placed first to match the expected layout for nested scopes.
- List<Symbol> newFields = new ArrayList<>();
- newFields.addAll(builder.getTranslations().getFieldSymbols());
-
- outputs.stream()
- .map(builder::translate)
- .forEach(newFields::add);
-
- builder = builder.withScope(analysis.getScope(node.getOrderBy().get()), newFields);
-
- builder = planWindowFunctions(node, builder, ImmutableList.copyOf(analysis.getOrderByWindowFunctions(node.getOrderBy().get())));
- builder = planWindowMeasures(node, builder, ImmutableList.copyOf(analysis.getOrderByWindowMeasures(node.getOrderBy().get())));
- }
-
- List<io.trino.sql.tree.Expression> orderBy = analysis.getOrderByExpressions(node);
- builder = subqueryPlanner.handleSubqueries(builder, orderBy, analysis.getSubqueries(node));
- builder = builder.appendProjections(Iterables.concat(orderBy, outputs), symbolAllocator, idAllocator);
-
- builder = distinct(builder, node, outputs);
- Optional<OrderingScheme> orderingScheme = orderingScheme(builder, node.getOrderBy(), analysis.getOrderByExpressions(node));
- builder = sort(builder, orderingScheme);
- builder = offset(builder, node.getOffset());
- builder = limit(builder, node.getLimit(), orderingScheme);
- builder = builder.appendProjections(outputs, symbolAllocator, idAllocator);
-
- return new RelationPlan(
- builder.getRoot(),
- analysis.getScope(node),
- computeOutputs(builder, outputs),
- outerContext);
- }
-
- private static boolean hasExpressionsToUnfold(List<Analysis.SelectExpression> selectExpressions)
- {
- return selectExpressions.stream()
- .map(SelectExpression::getUnfoldedExpressions)
- .anyMatch(Optional::isPresent);
- }
-
- private static List<Expression> outputExpressions(List<Analysis.SelectExpression> selectExpressions)
- {
- ImmutableList.Builder<Expression> result = ImmutableList.builder();
- for (Analysis.SelectExpression selectExpression : selectExpressions) {
- if (selectExpression.getUnfoldedExpressions().isPresent()) {
- result.addAll(selectExpression.getUnfoldedExpressions().get());
- }
- else {
- result.add(selectExpression.getExpression());
- }
- }
- return result.build();
- }
-
- public PlanNode plan(Delete node) {
- // implement delete logic
- return null;
- }
-
- private static List<Symbol> computeOutputs(PlanBuilder builder, List<Expression> outputExpressions) {
- ImmutableList.Builder<Symbol> outputSymbols = ImmutableList.builder();
- for (Expression expression : outputExpressions) {
- outputSymbols.add(builder.translate(expression));
- }
- return outputSymbols.build();
- }
-
- private PlanBuilder planQueryBody(Query query) {
- RelationPlan relationPlan = new RelationPlanner(analysis, symbolAllocator, idAllocator, session, recursiveSubqueries)
- .process(query.getQueryBody(), null);
-
- return newPlanBuilder(relationPlan, analysis, session);
- }
-
- private PlanBuilder planFrom(QuerySpecification node) {
- if (node.getFrom().isPresent()) {
- RelationPlan relationPlan = new RelationPlanner(analysis, symbolAllocator, idAllocator, session, recursiveSubqueries)
- .process(node.getFrom().get(), null);
- return newPlanBuilder(relationPlan, analysis, session);
- }
-
- return new PlanBuilder(
- new TranslationMap(outerContext, analysis.getImplicitFromScope(node), analysis, lambdaDeclarationToSymbolMap, ImmutableList.of(), session, plannerContext),
- new ValuesNode(idAllocator.getNextId(), 1));
- }
-
- private PlanBuilder filter(PlanBuilder subPlan, Expression predicate, Node node)
- {
- if (predicate == null) {
- return subPlan;
- }
-
- // subPlan = subqueryPlanner.handleSubqueries(subPlan, predicate, analysis.getSubqueries(node));
-
- return subPlan.withNewRoot(new FilterNode(idAllocator.getNextId(), subPlan.getRoot(), coerceIfNecessary(analysis, predicate, subPlan.rewrite(predicate))));
- }
-
- private <T extends io.trino.sql.tree.Expression> List<T> scopeAwareDistinct(PlanBuilder subPlan, List<T> expressions)
- {
- return expressions.stream()
- .map(function -> scopeAwareKey(function, analysis, subPlan.getScope()))
- .distinct()
- .map(ScopeAware::getNode)
- .collect(toImmutableList());
- }
-
- private FrameBoundPlanAndSymbols planFrameBound(PlanBuilder subPlan, PlanAndMappings coercions, Optional<io.trino.sql.tree.Expression> frameOffset, ResolvedWindow window, Map<Type, Symbol> sortKeyCoercions)
- {
- Optional<ResolvedFunction> frameBoundCalculationFunction = frameOffset.map(analysis::getFrameBoundCalculation);
-
- // Empty frameBoundCalculationFunction indicates that frame bound type is CURRENT ROW or UNBOUNDED.
- // Handling it doesn't require any additional symbols.
- if (frameBoundCalculationFunction.isEmpty()) {
- return new FrameBoundPlanAndSymbols(subPlan, Optional.empty(), Optional.empty());
- }
-
- // Present frameBoundCalculationFunction indicates that frame bound type is <expression> PRECEDING or <expression> FOLLOWING.
- // It requires adding certain projections to the plan so that the operator can determine frame bounds.
-
- // First, append filter to validate offset values. They mustn't be negative or null.
- Symbol offsetSymbol = coercions.get(frameOffset.get());
- Expression zeroOffset = zeroOfType(symbolAllocator.getTypes().get(offsetSymbol));
- Expression predicate = ifExpression(
- new ComparisonExpression(
- GREATER_THAN_OR_EQUAL,
- offsetSymbol.toSymbolReference(),
- zeroOffset),
- TRUE_LITERAL,
- new Cast(
- failFunction(plannerContext.getMetadata(), INVALID_WINDOW_FRAME, "Window frame offset value must not be negative or null"),
- BOOLEAN));
- subPlan = subPlan.withNewRoot(new FilterNode(
- idAllocator.getNextId(),
- subPlan.getRoot(),
- predicate));
-
- // Then, coerce the sortKey so that we can add / subtract the offset.
- // Note: for that we cannot rely on the usual mechanism of using the coerce() method. The coerce() method can only handle one coercion for a node,
- // while the sortKey node might require several different coercions, e.g. one for frame start and one for frame end.
- io.trino.sql.tree.Expression sortKey = Iterables.getOnlyElement(window.getOrderBy().orElseThrow().getSortItems()).getSortKey();
- Symbol sortKeyCoercedForFrameBoundCalculation = coercions.get(sortKey);
- Optional<Type> coercion = frameOffset.map(analysis::getSortKeyCoercionForFrameBoundCalculation);
- if (coercion.isPresent()) {
- Type expectedType = coercion.get();
- Symbol alreadyCoerced = sortKeyCoercions.get(expectedType);
- if (alreadyCoerced != null) {
- sortKeyCoercedForFrameBoundCalculation = alreadyCoerced;
- }
- else {
- Expression cast = new Cast(
- coercions.get(sortKey).toSymbolReference(),
- expectedType,
- false);
- sortKeyCoercedForFrameBoundCalculation = symbolAllocator.newSymbol(cast, expectedType);
- sortKeyCoercions.put(expectedType, sortKeyCoercedForFrameBoundCalculation);
- subPlan = subPlan.withNewRoot(new ProjectNode(
- idAllocator.getNextId(),
- subPlan.getRoot(),
- Assignments.builder()
- .putIdentities(subPlan.getRoot().getOutputSymbols())
- .put(sortKeyCoercedForFrameBoundCalculation, cast)
- .build()));
- }
- }
-
- // Next, pre-project the function which combines sortKey with the offset.
- // Note: if frameOffset needs a coercion, it was added before by a call to coerce() method.
- ResolvedFunction function = frameBoundCalculationFunction.get();
- Expression functionCall = new FunctionCall(
- function,
- ImmutableList.of(
- sortKeyCoercedForFrameBoundCalculation.toSymbolReference(),
- offsetSymbol.toSymbolReference()));
- Symbol frameBoundSymbol = symbolAllocator.newSymbol(functionCall, function.getSignature().getReturnType());
- subPlan = subPlan.withNewRoot(new ProjectNode(
- idAllocator.getNextId(),
- subPlan.getRoot(),
- Assignments.builder()
- .putIdentities(subPlan.getRoot().getOutputSymbols())
- .put(frameBoundSymbol, functionCall)
- .build()));
-
- // Finally, coerce the sortKey to the type of frameBound so that the operator can perform comparisons on them
- Optional<Symbol> sortKeyCoercedForFrameBoundComparison = Optional.of(coercions.get(sortKey));
- coercion = frameOffset.map(analysis::getSortKeyCoercionForFrameBoundComparison);
- if (coercion.isPresent()) {
- Type expectedType = coercion.get();
- Symbol alreadyCoerced = sortKeyCoercions.get(expectedType);
- if (alreadyCoerced != null) {
- sortKeyCoercedForFrameBoundComparison = Optional.of(alreadyCoerced);
- }
- else {
- Expression cast = new Cast(
- coercions.get(sortKey).toSymbolReference(),
- expectedType,
- false);
- Symbol castSymbol = symbolAllocator.newSymbol(cast, expectedType);
- sortKeyCoercions.put(expectedType, castSymbol);
- subPlan = subPlan.withNewRoot(new ProjectNode(
- idAllocator.getNextId(),
- subPlan.getRoot(),
- Assignments.builder()
- .putIdentities(subPlan.getRoot().getOutputSymbols())
- .put(castSymbol, cast)
- .build()));
- sortKeyCoercedForFrameBoundComparison = Optional.of(castSymbol);
- }
- }
-
- return new FrameBoundPlanAndSymbols(subPlan, Optional.of(frameBoundSymbol), sortKeyCoercedForFrameBoundComparison);
- }
-
- private FrameOffsetPlanAndSymbol planFrameOffset(PlanBuilder subPlan, Optional<Symbol> frameOffset)
- {
- if (frameOffset.isEmpty()) {
- return new FrameOffsetPlanAndSymbol(subPlan, Optional.empty());
- }
-
- Symbol offsetSymbol = frameOffset.get();
- Type offsetType = symbolAllocator.getTypes().get(offsetSymbol);
-
- // Append filter to validate offset values. They mustn't be negative or null.
- Expression zeroOffset = zeroOfType(offsetType);
- Expression predicate = ifExpression(
- new ComparisonExpression(GREATER_THAN_OR_EQUAL, offsetSymbol.toSymbolReference(), zeroOffset),
- TRUE_LITERAL,
- new Cast(
- failFunction(plannerContext.getMetadata(), INVALID_WINDOW_FRAME, "Window frame offset value must not be negative or null"),
- BOOLEAN));
- subPlan = subPlan.withNewRoot(new FilterNode(
- idAllocator.getNextId(),
- subPlan.getRoot(),
- predicate));
-
- if (offsetType.equals(BIGINT)) {
- return new FrameOffsetPlanAndSymbol(subPlan, Optional.of(offsetSymbol));
- }
-
- Expression offsetToBigint;
-
- if (offsetType instanceof DecimalType decimalType && !decimalType.isShort()) {
- int actualPrecision = decimalType.getPrecision();
-
- if (actualPrecision < MAX_BIGINT_PRECISION) {
- offsetToBigint = new Cast(offsetSymbol.toSymbolReference(), BIGINT);
- }
- else if (actualPrecision > MAX_BIGINT_PRECISION) {
- // If the offset value exceeds max bigint, it implies that the frame bound falls beyond the partition bound.
- // In such case, the frame bound is set to the partition bound. Passing max bigint as the offset value has
- // the same effect. The offset value can be truncated to max bigint for the purpose of cast.
- offsetToBigint = new Constant(BIGINT, Long.MAX_VALUE);
- }
- else {
- offsetToBigint = ifExpression(
- new ComparisonExpression(LESS_THAN_OR_EQUAL, offsetSymbol.toSymbolReference(), new Constant(decimalType, Int128.valueOf(Long.MAX_VALUE))),
- new Cast(offsetSymbol.toSymbolReference(), BIGINT),
- new Constant(BIGINT, Long.MAX_VALUE));
- }
- }
- else {
- offsetToBigint = new Cast(
- offsetSymbol.toSymbolReference(),
- BIGINT,
- false);
- }
-
- Symbol coercedOffsetSymbol = symbolAllocator.newSymbol(offsetToBigint, BIGINT);
- subPlan = subPlan.withNewRoot(new ProjectNode(
- idAllocator.getNextId(),
- subPlan.getRoot(),
- Assignments.builder()
- .putIdentities(subPlan.getRoot().getOutputSymbols())
- .put(coercedOffsetSymbol, offsetToBigint)
- .build()));
-
- return new FrameOffsetPlanAndSymbol(subPlan, Optional.of(coercedOffsetSymbol));
- }
-
- private static Expression zeroOfType(Type type)
- {
- if (type.equals(BIGINT) ||
- type.equals(INTEGER) ||
- type.equals(SMALLINT) ||
- type.equals(TINYINT) ||
- type.equals(INTERVAL_DAY_TIME) ||
- type.equals(INTERVAL_YEAR_MONTH)) {
- return new Constant(type, 0L);
- }
-
- if (type.equals(DOUBLE)) {
- return new Constant(DOUBLE, 0.0);
- }
-
- if (type instanceof DecimalType decimal) {
- if (decimal.isShort()) {
- return new Constant(type, 0L);
- }
-
- return new Constant(type, Int128.valueOf(0));
- }
-
- if (type.equals(REAL)) {
- return new Constant(type, Reals.toReal(0));
- }
-
- throw new IllegalArgumentException("unexpected type: " + type);
- }
-
- private FrameBoundType mapFrameBoundType(FrameBound.Type type)
- {
- return switch (type) {
- case UNBOUNDED_PRECEDING -> FrameBoundType.UNBOUNDED_PRECEDING;
- case PRECEDING -> FrameBoundType.PRECEDING;
- case CURRENT_ROW -> CURRENT_ROW;
- case FOLLOWING -> FrameBoundType.FOLLOWING;
- case UNBOUNDED_FOLLOWING -> FrameBoundType.UNBOUNDED_FOLLOWING;
- };
- }
-
- public static DataOrganizationSpecification planWindowSpecification(List<io.trino.sql.tree.Expression> partitionBy, Optional<OrderBy> orderBy, Function<io.trino.sql.tree.Expression, Symbol> expressionRewrite)
- {
- // Rewrite PARTITION BY
- ImmutableList.Builder<Symbol> partitionBySymbols = ImmutableList.builder();
- for (io.trino.sql.tree.Expression expression : partitionBy) {
- partitionBySymbols.add(expressionRewrite.apply(expression));
- }
-
- // Rewrite ORDER BY
- LinkedHashMap<Symbol, SortOrder> orderings = new LinkedHashMap<>();
- for (SortItem item : getSortItemsFromOrderBy(orderBy)) {
- Symbol symbol = expressionRewrite.apply(item.getSortKey());
- // don't override existing keys, i.e. when "ORDER BY a ASC, a DESC" is specified
- orderings.putIfAbsent(symbol, sortItemToSortOrder(item));
- }
-
- Optional<OrderingScheme> orderingScheme = Optional.empty();
- if (!orderings.isEmpty()) {
- orderingScheme = Optional.of(new OrderingScheme(ImmutableList.copyOf(orderings.keySet()), orderings));
- }
-
- return new DataOrganizationSpecification(partitionBySymbols.build(), orderingScheme);
- }
-
- private PlanBuilder planWindowMeasures(Node node, PlanBuilder subPlan, List<WindowOperation> windowMeasures)
- {
- if (windowMeasures.isEmpty()) {
- return subPlan;
- }
-
- for (WindowOperation windowMeasure : scopeAwareDistinct(subPlan, windowMeasures)) {
- ResolvedWindow window = analysis.getWindow(windowMeasure);
- checkState(window != null, "no resolved window for: " + windowMeasure);
-
- // pre-project inputs
- ImmutableList.Builder<io.trino.sql.tree.Expression> inputsBuilder = ImmutableList.<io.trino.sql.tree.Expression>builder()
- .addAll(window.getPartitionBy())
- .addAll(getSortItemsFromOrderBy(window.getOrderBy()).stream()
- .map(SortItem::getSortKey)
- .iterator());
- WindowFrame frame = window.getFrame().orElseThrow();
- Optional<io.trino.sql.tree.Expression> endValue = frame.getEnd().orElseThrow().getValue();
-
- List<io.trino.sql.tree.Expression> inputs = inputsBuilder.build();
-
- subPlan = subqueryPlanner.handleSubqueries(subPlan, inputs, analysis.getSubqueries(node));
- subPlan = subPlan.appendProjections(inputs, symbolAllocator, idAllocator);
-
- // Add projection for frame end, since WindowNode expects a symbol and does not support literals
- // We don't use appendProjects because we don't want a mapping to be added for the literal
- Optional<Symbol> endValueSymbol = Optional.empty();
- if (endValue.isPresent()) {
- io.trino.sql.tree.Expression expression = endValue.get();
- Assignments.Builder assignments = Assignments.builder();
- assignments.putIdentities(subPlan.getRoot().getOutputSymbols());
- Symbol symbol = symbolAllocator.newSymbol("end", analysis.getType(expression));
- assignments.put(symbol, subPlan.rewrite(expression));
-
- endValueSymbol = Optional.of(symbol);
- subPlan = subPlan.withNewRoot(new ProjectNode(idAllocator.getNextId(), subPlan.getRoot(), assignments.build()));
- }
-
- // process frame end
- FrameOffsetPlanAndSymbol plan = planFrameOffset(subPlan, endValueSymbol);
- subPlan = plan.getSubPlan();
- Optional<Symbol> frameEnd = plan.getFrameOffsetSymbol();
-
- subPlan = subqueryPlanner.handleSubqueries(subPlan, extractPatternRecognitionExpressions(frame.getVariableDefinitions(), frame.getMeasures()), analysis.getSubqueries(node));
- subPlan = planPatternRecognition(subPlan, windowMeasure, window, frameEnd);
- }
-
- return subPlan;
- }
-
- public static List<io.trino.sql.tree.Expression> extractPatternRecognitionExpressions(List<VariableDefinition> variableDefinitions, List<MeasureDefinition> measureDefinitions)
- {
- ImmutableList.Builder<io.trino.sql.tree.Expression> expressions = ImmutableList.builder();
-
- variableDefinitions.stream()
- .map(VariableDefinition::getExpression)
- .forEach(expressions::add);
-
- measureDefinitions.stream()
- .map(MeasureDefinition::getExpression)
- .forEach(expressions::add);
-
- return expressions.build();
- }
-
- /**
- * Creates a projection with any additional coercions by identity of the provided expressions.
- *
- * @return the new subplan and a mapping of each expression to the symbol representing the coercion or an existing symbol if a coercion wasn't needed
- */
- public static PlanAndMappings coerce(PlanBuilder subPlan, List<io.trino.sql.tree.Expression> expressions, Analysis analysis, PlanNodeIdAllocator idAllocator, SymbolAllocator symbolAllocator)
- {
- Assignments.Builder assignments = Assignments.builder();
- assignments.putIdentities(subPlan.getRoot().getOutputSymbols());
-
- Map<NodeRef<io.trino.sql.tree.Expression>, Symbol> mappings = new HashMap<>();
- for (io.trino.sql.tree.Expression expression : expressions) {
- Type coercion = analysis.getCoercion(expression);
-
- // expressions may be repeated, for example, when resolving ordinal references in a GROUP BY clause
- if (!mappings.containsKey(NodeRef.of(expression))) {
- if (coercion != null) {
- Symbol symbol = symbolAllocator.newSymbol("expr", coercion);
-
- assignments.put(symbol, new Cast(
- subPlan.rewrite(expression),
- coercion,
- false));
-
- mappings.put(NodeRef.of(expression), symbol);
- }
- else {
- mappings.put(NodeRef.of(expression), subPlan.translate(expression));
- }
- }
- }
-
- subPlan = subPlan.withNewRoot(
- new ProjectNode(
- idAllocator.getNextId(),
- subPlan.getRoot(),
- assignments.build()));
-
- return new PlanAndMappings(subPlan, mappings);
- }
-
- public static Expression coerceIfNecessary(Analysis analysis, io.trino.sql.tree.Expression original, Expression rewritten)
- {
- Type coercion = analysis.getCoercion(original);
- if (coercion == null) {
- return rewritten;
- }
-
- return new Cast(rewritten, coercion, false);
- }
-
- public static NodeAndMappings coerce(RelationPlan plan, List<Type> types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator)
- {
- List<Symbol> visibleFields = visibleFields(plan);
- checkArgument(visibleFields.size() == types.size());
-
- Assignments.Builder assignments = Assignments.builder();
- ImmutableList.Builder<Symbol> mappings = ImmutableList.builder();
- for (int i = 0; i < types.size(); i++) {
- Symbol input = visibleFields.get(i);
- Type type = types.get(i);
-
- if (!symbolAllocator.getTypes().get(input).equals(type)) {
- Symbol coerced = symbolAllocator.newSymbol(input.getName(), type);
- assignments.put(coerced, new Cast(input.toSymbolReference(), type));
- mappings.add(coerced);
- }
- else {
- assignments.putIdentity(input);
- mappings.add(input);
- }
- }
-
- ProjectNode coerced = new ProjectNode(idAllocator.getNextId(), plan.getRoot(), assignments.build());
- return new NodeAndMappings(coerced, mappings.build());
- }
-
- public static List<Symbol> visibleFields(RelationPlan subPlan)
- {
- RelationType descriptor = subPlan.getDescriptor();
- return descriptor.getAllFields().stream()
- .filter(field -> !field.isHidden())
- .map(descriptor::indexOf)
- .map(subPlan.getFieldMappings()::get)
- .collect(toImmutableList());
- }
-
- public static NodeAndMappings pruneInvisibleFields(RelationPlan plan, PlanNodeIdAllocator idAllocator)
- {
- List<Symbol> visibleFields = visibleFields(plan);
- ProjectNode pruned = new ProjectNode(idAllocator.getNextId(), plan.getRoot(), Assignments.identity(visibleFields));
- return new NodeAndMappings(pruned, visibleFields);
- }
-
- public static NodeAndMappings disambiguateOutputs(NodeAndMappings plan, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator)
- {
- Set<Symbol> distinctOutputs = ImmutableSet.copyOf(plan.getFields());
-
- if (distinctOutputs.size() < plan.getFields().size()) {
- Assignments.Builder assignments = Assignments.builder();
- ImmutableList.Builder<Symbol> newOutputs = ImmutableList.builder();
- Set<Symbol> uniqueOutputs = new HashSet<>();
-
- for (Symbol output : plan.getFields()) {
- if (uniqueOutputs.add(output)) {
- assignments.putIdentity(output);
- newOutputs.add(output);
- }
- else {
- Symbol newOutput = symbolAllocator.newSymbol(output);
- assignments.put(newOutput, output.toSymbolReference());
- newOutputs.add(newOutput);
- }
- }
-
- return new NodeAndMappings(new ProjectNode(idAllocator.getNextId(), plan.getNode(), assignments.build()), newOutputs.build());
- }
-
- return plan;
- }
-
- private PlanBuilder distinct(PlanBuilder subPlan, QuerySpecification node, List<io.trino.sql.tree.Expression> expressions)
- {
- if (node.getSelect().isDistinct()) {
- List<Symbol> symbols = expressions.stream()
- .map(subPlan::translate)
- .collect(Collectors.toList());
-
- return subPlan.withNewRoot(
- singleAggregation(
- idAllocator.getNextId(),
- subPlan.getRoot(),
- ImmutableMap.of(),
- singleGroupingSet(symbols)));
- }
-
- return subPlan;
- }
-
- private Optional<OrderingScheme> orderingScheme(PlanBuilder subPlan, Optional<OrderBy> orderBy, List<io.trino.sql.tree.Expression> orderByExpressions)
- {
- if (orderBy.isEmpty() || (isSkipRedundantSort(session) && analysis.isOrderByRedundant(orderBy.get()))) {
- return Optional.empty();
- }
-
- Iterator<SortItem> sortItems = orderBy.get().getSortItems().iterator();
-
- ImmutableList.Builder<Symbol> orderBySymbols = ImmutableList.builder();
- Map<Symbol, SortOrder> orderings = new HashMap<>();
- for (io.trino.sql.tree.Expression fieldOrExpression : orderByExpressions) {
- Symbol symbol = subPlan.translate(fieldOrExpression);
-
- SortItem sortItem = sortItems.next();
- if (!orderings.containsKey(symbol)) {
- orderBySymbols.add(symbol);
- orderings.put(symbol, sortItemToSortOrder(sortItem));
- }
- }
- return Optional.of(new OrderingScheme(orderBySymbols.build(), orderings));
- }
-
- private PlanBuilder sort(PlanBuilder subPlan, Optional<OrderingScheme> orderingScheme)
- {
- if (orderingScheme.isEmpty()) {
- return subPlan;
- }
-
- return subPlan.withNewRoot(
- new SortNode(
- idAllocator.getNextId(),
- subPlan.getRoot(),
- orderingScheme.get(),
- false));
- }
-
- private PlanBuilder offset(PlanBuilder subPlan, Optional<Offset> offset)
- {
- if (offset.isEmpty()) {
- return subPlan;
- }
-
- return subPlan.withNewRoot(
- new OffsetNode(
- idAllocator.getNextId(),
- subPlan.getRoot(),
- analysis.getOffset(offset.get())));
- }
-
- private PlanBuilder limit(PlanBuilder subPlan, Optional<Node> limit, Optional<OrderingScheme> orderingScheme)
- {
- if (limit.isPresent() && analysis.getLimit(limit.get()).isPresent()) {
- Optional<OrderingScheme> tiesResolvingScheme = Optional.empty();
- if (limit.get() instanceof FetchFirst && ((FetchFirst) limit.get()).isWithTies()) {
- tiesResolvingScheme = orderingScheme;
- }
- return subPlan.withNewRoot(
- new LimitNode(
- idAllocator.getNextId(),
- subPlan.getRoot(),
- analysis.getLimit(limit.get()).getAsLong(),
- tiesResolvingScheme,
- false,
- ImmutableList.of()));
- }
- return subPlan;
- }
-
- public static class PlanAndMappings
- {
- private final PlanBuilder subPlan;
- private final Map<NodeRef<io.trino.sql.tree.Expression>, Symbol> mappings;
-
- public PlanAndMappings(PlanBuilder subPlan, Map<NodeRef<io.trino.sql.tree.Expression>, Symbol> mappings)
- {
- this.subPlan = subPlan;
- this.mappings = ImmutableMap.copyOf(mappings);
- }
-
- public PlanBuilder getSubPlan()
- {
- return subPlan;
- }
-
- public Symbol get(Expression expression)
- {
- return tryGet(expression)
- .orElseThrow(() -> new IllegalArgumentException(format("No mapping for expression: %s (%s)", expression, System.identityHashCode(expression))));
- }
-
- public Optional<Symbol> tryGet(Expression expression)
- {
- Symbol result = mappings.get(NodeRef.of(expression));
-
- if (result != null) {
- return Optional.of(result);
- }
-
- return Optional.empty();
- }
- }
-
- private static class AggregationAssignment {
- private final Symbol symbol;
- private final Expression astExpression;
- private final Aggregation aggregation;
-
- public AggregationAssignment(Symbol symbol, Expression astExpression, Aggregation aggregation) {
- this.astExpression = astExpression;
- this.symbol = symbol;
- this.aggregation = aggregation;
- }
-
- public Symbol getSymbol()
- {
- return symbol;
- }
-
- public Expression getAstExpression()
- {
- return astExpression;
- }
-
- public Aggregation getRewritten()
- {
- return aggregation;
- }
- }
-
- private static class FrameBoundPlanAndSymbols
- {
- private final PlanBuilder subPlan;
- private final Optional<Symbol> frameBoundSymbol;
- private final Optional<Symbol> sortKeyCoercedForFrameBoundComparison;
-
- public FrameBoundPlanAndSymbols(PlanBuilder subPlan, Optional<Symbol> frameBoundSymbol, Optional<Symbol> sortKeyCoercedForFrameBoundComparison)
- {
- this.subPlan = subPlan;
- this.frameBoundSymbol = frameBoundSymbol;
- this.sortKeyCoercedForFrameBoundComparison = sortKeyCoercedForFrameBoundComparison;
- }
-
- public PlanBuilder getSubPlan()
- {
- return subPlan;
- }
-
- public Optional<Symbol> getFrameBoundSymbol()
- {
- return frameBoundSymbol;
- }
-
- public Optional<Symbol> getSortKeyCoercedForFrameBoundComparison()
- {
- return sortKeyCoercedForFrameBoundComparison;
- }
- }
-
- private static class FrameOffsetPlanAndSymbol
- {
- private final PlanBuilder subPlan;
- private final Optional<Symbol> frameOffsetSymbol;
-
- public FrameOffsetPlanAndSymbol(PlanBuilder subPlan, Optional<Symbol> frameOffsetSymbol)
- {
- this.subPlan = subPlan;
- this.frameOffsetSymbol = frameOffsetSymbol;
- }
-
- public PlanBuilder getSubPlan()
- {
- return subPlan;
- }
-
- public Optional<Symbol> getFrameOffsetSymbol()
- {
- return frameOffsetSymbol;
- }
- }
+class QueryPlanner {
+ private static final int MAX_BIGINT_PRECISION = 19;
+ private final Analysis analysis;
+ private final SymbolAllocator symbolAllocator;
+ private final QueryId idAllocator;
+ // private final Map<NodeRef<LambdaArgumentDeclaration>, Symbol> lambdaDeclarationToSymbolMap;
+ // private final PlannerContext plannerContext;
+ private final Session session;
+ // private final SubqueryPlanner subqueryPlanner;
+ private final Map<NodeRef<Node>, RelationPlan> recursiveSubqueries;
+
+ QueryPlanner(
+ Analysis analysis,
+ SymbolAllocator symbolAllocator,
+ QueryId idAllocator,
+ Session session,
+ Map<NodeRef<Node>, RelationPlan> recursiveSubqueries) {
+ requireNonNull(analysis, "analysis is null");
+ requireNonNull(symbolAllocator, "symbolAllocator is null");
+ requireNonNull(idAllocator, "idAllocator is null");
+ requireNonNull(session, "session is null");
+ requireNonNull(recursiveSubqueries, "recursiveSubqueries is null");
+
+ this.analysis = analysis;
+ this.symbolAllocator = symbolAllocator;
+ this.idAllocator = idAllocator;
+ this.session = session;
+ // this.subqueryPlanner = null;
+ this.recursiveSubqueries = recursiveSubqueries;
+ }
+
+ public RelationPlan plan(Query query) {
+ PlanBuilder builder = planQueryBody(query);
+
+ List<Expression> orderBy = analysis.getOrderByExpressions(query);
+ // builder = subqueryPlanner.handleSubqueries(builder, orderBy, analysis.getSubqueries(query));
+
+ List<Analysis.SelectExpression> selectExpressions = analysis.getSelectExpressions(query);
+ List<Expression> outputs =
+ selectExpressions.stream()
+ .map(Analysis.SelectExpression::getExpression)
+ .collect(toImmutableList());
+
+ builder =
+ builder.appendProjections(Iterables.concat(orderBy, outputs), symbolAllocator, idAllocator);
+
+ Optional<OrderingScheme> orderingScheme =
+ orderingScheme(builder, query.getOrderBy(), analysis.getOrderByExpressions(query));
+ builder = sort(builder, orderingScheme);
+ builder = offset(builder, query.getOffset());
+ builder = limit(builder, query.getLimit(), orderingScheme);
+ builder = builder.appendProjections(outputs, symbolAllocator, idAllocator);
+
+ return new RelationPlan(
+ builder.getRoot(), analysis.getScope(query), computeOutputs(builder, outputs));
+ }
+
+ public RelationPlan plan(QuerySpecification node) {
+ PlanBuilder builder = planFrom(node);
+
+ builder = filter(builder, analysis.getWhere(node), node);
+ // builder = aggregate(builder, node);
+ builder = filter(builder, analysis.getHaving(node), node);
+ // builder = planWindowFunctions(node, builder,
+ // ImmutableList.copyOf(analysis.getWindowFunctions(node)));
+ // builder = planWindowMeasures(node, builder,
+ // ImmutableList.copyOf(analysis.getWindowMeasures(node)));
+
+ List<Analysis.SelectExpression> selectExpressions = analysis.getSelectExpressions(node);
+ List<Expression> expressions =
+ selectExpressions.stream()
+ .map(Analysis.SelectExpression::getExpression)
+ .collect(toImmutableList());
+ // builder = subqueryPlanner.handleSubqueries(builder, expressions,
+ // analysis.getSubqueries(node));
+
+ if (hasExpressionsToUnfold(selectExpressions)) {
+ // pre-project the folded expressions to preserve any non-deterministic semantics of functions
+ // that might be referenced
+ builder = builder.appendProjections(expressions, symbolAllocator, idAllocator);
+ }
+
+ List<Expression> outputs = outputExpressions(selectExpressions);
+ if (node.getOrderBy().isPresent()) {
+ // ORDER BY requires outputs of SELECT to be visible.
+ // For queries with aggregation, it also requires grouping keys and translated aggregations.
+ if (analysis.isAggregation(node)) {
+ // Add projections for aggregations required by ORDER BY. After this step, grouping keys and
+ // translated
+ // aggregations are visible.
+ List<Expression> orderByAggregates = analysis.getOrderByAggregates(node.getOrderBy().get());
+ builder = builder.appendProjections(orderByAggregates, symbolAllocator, idAllocator);
+ }
+
+ // Add projections for the outputs of SELECT, but stack them on top of the ones from the FROM
+ // clause so both are visible
+ // when resolving the ORDER BY clause.
+ builder = builder.appendProjections(outputs, symbolAllocator, idAllocator);
+
+ // The new scope is the composite of the fields from the FROM and SELECT clause (local nested
+ // scopes). Fields from the bottom of
+ // the scope stack need to be placed first to match the expected layout for nested scopes.
+ List<Symbol> newFields = new ArrayList<>();
+ // newFields.addAll(builder.getTranslations().getFieldSymbols());
+
+ // outputs.stream()
+ // .map(builder::translate)
+ // .forEach(newFields::add);
+
+ builder = builder.withScope(analysis.getScope(node.getOrderBy().get()), newFields);
+ }
+
+ List<Expression> orderBy = analysis.getOrderByExpressions(node);
+ builder =
+ builder.appendProjections(Iterables.concat(orderBy, outputs), symbolAllocator, idAllocator);
+
+ // builder = distinct(builder, node, outputs);
+ Optional<OrderingScheme> orderingScheme =
+ orderingScheme(builder, node.getOrderBy(), analysis.getOrderByExpressions(node));
+ builder = sort(builder, orderingScheme);
+ builder = offset(builder, node.getOffset());
+ builder = limit(builder, node.getLimit(), orderingScheme);
+ builder = builder.appendProjections(outputs, symbolAllocator, idAllocator);
+
+ return new RelationPlan(
+ builder.getRoot(), analysis.getScope(node), computeOutputs(builder, outputs));
+ }
+
+ private static boolean hasExpressionsToUnfold(List<Analysis.SelectExpression> selectExpressions) {
+ return selectExpressions.stream()
+ .map(Analysis.SelectExpression::getUnfoldedExpressions)
+ .anyMatch(Optional::isPresent);
+ }
+
+ private static List<Expression> outputExpressions(
+ List<Analysis.SelectExpression> selectExpressions) {
+ ImmutableList.Builder<Expression> result = ImmutableList.builder();
+ for (Analysis.SelectExpression selectExpression : selectExpressions) {
+ if (selectExpression.getUnfoldedExpressions().isPresent()) {
+ result.addAll(selectExpression.getUnfoldedExpressions().get());
+ } else {
+ result.add(selectExpression.getExpression());
+ }
+ }
+ return result.build();
+ }
+
+ public PlanNode plan(Delete node) {
+ // implement delete logic
+ return null;
+ }
+
+ private static List<Symbol> computeOutputs(
+ PlanBuilder builder, List<Expression> outputExpressions) {
+ ImmutableList.Builder<Symbol> outputSymbols = ImmutableList.builder();
+ // for (Expression expression : outputExpressions) {
+ // // outputSymbols.add(builder.translate(expression));
+ // }
+ return outputSymbols.build();
+ }
+
+ private PlanBuilder planQueryBody(Query query) {
+ RelationPlan relationPlan =
+ new RelationPlanner(analysis, symbolAllocator, idAllocator, session, recursiveSubqueries)
+ .process(query.getQueryBody(), null);
+
+ return newPlanBuilder(relationPlan, analysis, session);
+ }
+
+ private PlanBuilder planFrom(QuerySpecification node) {
+ if (node.getFrom().isPresent()) {
+ RelationPlan relationPlan =
+ new RelationPlanner(analysis, symbolAllocator, idAllocator, session, recursiveSubqueries)
+ .process(node.getFrom().get(), null);
+ return newPlanBuilder(relationPlan, analysis, session);
+ } else {
+ throw new RuntimeException("From clause must not by empty");
+ }
+ }
+
+ private PlanBuilder filter(PlanBuilder subPlan, Expression predicate, Node node) {
+ if (predicate == null) {
+ return subPlan;
+ }
+
+ // subPlan = subqueryPlanner.handleSubqueries(subPlan, predicate, analysis.getSubqueries(node));
+
+ return subPlan.withNewRoot(
+ new FilterNode(idAllocator.genPlanNodeId(), subPlan.getRoot(), predicate));
+ }
+
+ public static Expression coerceIfNecessary(
+ Analysis analysis, Expression original, Expression rewritten) {
+ Type coercion = analysis.getCoercion(original);
+ if (coercion == null) {
+ return rewritten;
+ } else {
+ throw new RuntimeException("Coercion result in analysis only can be empty");
+ }
+ }
+
+ private Optional<OrderingScheme> orderingScheme(
+ PlanBuilder subPlan, Optional<OrderBy> orderBy, List<Expression> orderByExpressions) {
+ if (!orderBy.isPresent() || (analysis.isOrderByRedundant(orderBy.get()))) {
+ return Optional.empty();
+ }
+
+ Iterator<SortItem> sortItems = orderBy.get().getSortItems().iterator();
+
+ ImmutableList.Builder<Symbol> orderBySymbols = ImmutableList.builder();
+ Map<Symbol, SortOrder> orderings = new HashMap<>();
+ for (Expression fieldOrExpression : orderByExpressions) {
+ Symbol symbol = null;
+ // subPlan.translate(fieldOrExpression);
+
+ SortItem sortItem = sortItems.next();
+ if (!orderings.containsKey(symbol)) {
+ orderBySymbols.add(symbol);
+ orderings.put(symbol, sortItemToSortOrder(sortItem));
+ }
+ }
+ return Optional.of(new OrderingScheme(orderBySymbols.build(), orderings));
+ }
+
+ private PlanBuilder sort(PlanBuilder subPlan, Optional<OrderingScheme> orderingScheme) {
+ if (!orderingScheme.isPresent()) {
+ return subPlan;
+ }
+
+ return subPlan.withNewRoot(
+ new SortNode(idAllocator.genPlanNodeId(), subPlan.getRoot(), orderingScheme.get(), false));
+ }
+
+ private PlanBuilder offset(PlanBuilder subPlan, Optional<Offset> offset) {
+ if (!offset.isPresent()) {
+ return subPlan;
+ }
+
+ return subPlan.withNewRoot(
+ new OffsetNode(
+ idAllocator.genPlanNodeId(), subPlan.getRoot(), analysis.getOffset(offset.get())));
+ }
+
+ private PlanBuilder limit(
+ PlanBuilder subPlan, Optional<Node> limit, Optional<OrderingScheme> orderingScheme) {
+ if (limit.isPresent() && analysis.getLimit(limit.get()).isPresent()) {
+ Optional<OrderingScheme> tiesResolvingScheme = Optional.empty();
+ // if (limit.get() instanceof FetchFirst && ((FetchFirst)
+ // limit.get()).isWithTies()) {
+ // tiesResolvingScheme = orderingScheme;
+ // }
+ return subPlan.withNewRoot(
+ new LimitNode(
+ idAllocator.genPlanNodeId(),
+ subPlan.getRoot(),
+ analysis.getLimit(limit.get()).getAsLong(),
+ tiesResolvingScheme));
+ }
+ return subPlan;
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlan.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlan.java
index 7f292bf5b30..de035fd48fd 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlan.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlan.java
@@ -13,67 +13,66 @@
*/
package org.apache.iotdb.db.queryengine.plan.relational.planner;
-import com.google.common.collect.ImmutableList;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.RelationType;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Scope;
+import com.google.common.collect.ImmutableList;
+
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
/**
- * The purpose of this class is to hold the current plan built so far
- * for a relation (query, table, values, etc.), and the mapping to
- * indicate how the fields (by position) in the relation map to
+ * The purpose of this class is to hold the current plan built so far for a relation (query, table,
+ * values, etc.), and the mapping to indicate how the fields (by position) in the relation map to
* the outputs of the plan.
*/
class RelationPlan {
- private final PlanNode root;
- private final List<Symbol> fieldMappings; // for each field in the relation, the corresponding symbol from "root"
- private final Scope scope;
+ private final PlanNode root;
+ private final List<Symbol>
+ fieldMappings; // for each field in the relation, the corresponding symbol from "root"
+ private final Scope scope;
- public RelationPlan(PlanNode root, Scope scope, List<Symbol> fieldMappings) {
- requireNonNull(root, "root is null");
- requireNonNull(fieldMappings, "fieldMappings is null");
- requireNonNull(scope, "scope is null");
+ public RelationPlan(PlanNode root, Scope scope, List<Symbol> fieldMappings) {
+ requireNonNull(root, "root is null");
+ requireNonNull(fieldMappings, "fieldMappings is null");
+ requireNonNull(scope, "scope is null");
- int allFieldCount = scope.getLocalScopeFieldCount();
- checkArgument(allFieldCount == fieldMappings.size(),
- "Number of outputs (%s) doesn't match number of fields in local scope (%s)",
- fieldMappings.size(),
- allFieldCount);
+ int allFieldCount = scope.getLocalScopeFieldCount();
+ checkArgument(
+ allFieldCount == fieldMappings.size(),
+ "Number of outputs (%s) doesn't match number of fields in local scope (%s)",
+ fieldMappings.size(),
+ allFieldCount);
- this.root = root;
- this.scope = scope;
- this.fieldMappings = ImmutableList.copyOf(fieldMappings);
- }
+ this.root = root;
+ this.scope = scope;
+ this.fieldMappings = ImmutableList.copyOf(fieldMappings);
+ }
- public Symbol getSymbol(int fieldIndex)
- {
- checkArgument(fieldIndex >= 0 && fieldIndex < fieldMappings.size(),
- "No field->symbol mapping for field %s", fieldIndex);
- return fieldMappings.get(fieldIndex);
- }
+ public Symbol getSymbol(int fieldIndex) {
+ checkArgument(
+ fieldIndex >= 0 && fieldIndex < fieldMappings.size(),
+ "No field->symbol mapping for field %s",
+ fieldIndex);
+ return fieldMappings.get(fieldIndex);
+ }
- public PlanNode getRoot()
- {
- return root;
- }
+ public PlanNode getRoot() {
+ return root;
+ }
- public List<Symbol> getFieldMappings()
- {
- return fieldMappings;
- }
+ public List<Symbol> getFieldMappings() {
+ return fieldMappings;
+ }
- public RelationType getDescriptor()
- {
- return scope.getRelationType();
- }
+ public RelationType getDescriptor() {
+ return scope.getRelationType();
+ }
- public Scope getScope()
- {
- return scope;
- }
+ public Scope getScope() {
+ return scope;
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java
index f3b28dec1f4..a3ae2c9e715 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java
@@ -13,282 +13,229 @@
*/
package org.apache.iotdb.db.queryengine.plan.relational.planner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
import org.apache.iotdb.db.queryengine.common.QueryId;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Field;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.NodeRef;
-import org.apache.iotdb.db.queryengine.plan.relational.analyzer.RelationType;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Scope;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.ColumnHandle;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableHandle;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
-import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ProjectNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode;
import org.apache.iotdb.db.relational.sql.tree.AliasedRelation;
import org.apache.iotdb.db.relational.sql.tree.AstVisitor;
-import org.apache.iotdb.db.relational.sql.tree.Except;
import org.apache.iotdb.db.relational.sql.tree.Expression;
-import org.apache.iotdb.db.relational.sql.tree.Intersect;
-import org.apache.iotdb.db.relational.sql.tree.Join;
import org.apache.iotdb.db.relational.sql.tree.Node;
import org.apache.iotdb.db.relational.sql.tree.Query;
import org.apache.iotdb.db.relational.sql.tree.QuerySpecification;
-import org.apache.iotdb.db.relational.sql.tree.Relation;
-import org.apache.iotdb.db.relational.sql.tree.Row;
-import org.apache.iotdb.db.relational.sql.tree.SetOperation;
import org.apache.iotdb.db.relational.sql.tree.SubqueryExpression;
import org.apache.iotdb.db.relational.sql.tree.Table;
-import org.apache.iotdb.db.relational.sql.tree.TableSubquery;
-import org.apache.iotdb.db.relational.sql.tree.Union;
-import org.apache.iotdb.db.relational.sql.tree.Values;
import org.apache.iotdb.session.Session;
-import org.apache.iotdb.tsfile.read.common.type.RowType;
-import java.util.ArrayList;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static java.lang.Boolean.TRUE;
+
import static java.util.Objects.requireNonNull;
import static org.apache.iotdb.db.queryengine.plan.relational.planner.PlanBuilder.newPlanBuilder;
-
-class RelationPlanner
- extends AstVisitor<RelationPlan, Void>
-{
- private final Analysis analysis;
- private final SymbolAllocator symbolAllocator;
- private final QueryId idAllocator;
- private final Session session;
- private final Map<NodeRef<Node>, RelationPlan> recursiveSubqueries;
-
- RelationPlanner(
- Analysis analysis,
- SymbolAllocator symbolAllocator,
- QueryId idAllocator,
- Session session,
- Map<NodeRef<Node>, RelationPlan> recursiveSubqueries) {
- requireNonNull(analysis, "analysis is null");
- requireNonNull(symbolAllocator, "symbolAllocator is null");
- requireNonNull(idAllocator, "idAllocator is null");
- requireNonNull(session, "session is null");
- requireNonNull(recursiveSubqueries, "recursiveSubqueries is null");
-
- this.analysis = analysis;
- this.symbolAllocator = symbolAllocator;
- this.idAllocator = idAllocator;
- this.session = session;
- this.recursiveSubqueries = recursiveSubqueries;
+import static org.apache.iotdb.db.queryengine.plan.relational.planner.QueryPlanner.coerceIfNecessary;
+
+class RelationPlanner extends AstVisitor<RelationPlan, Void> {
+ private final Analysis analysis;
+ private final SymbolAllocator symbolAllocator;
+ private final QueryId idAllocator;
+ private final Session session;
+ private final Map<NodeRef<Node>, RelationPlan> recursiveSubqueries;
+
+ RelationPlanner(
+ Analysis analysis,
+ SymbolAllocator symbolAllocator,
+ QueryId idAllocator,
+ Session session,
+ Map<NodeRef<Node>, RelationPlan> recursiveSubqueries) {
+ requireNonNull(analysis, "analysis is null");
+ requireNonNull(symbolAllocator, "symbolAllocator is null");
+ requireNonNull(idAllocator, "idAllocator is null");
+ requireNonNull(session, "session is null");
+ requireNonNull(recursiveSubqueries, "recursiveSubqueries is null");
+
+ this.analysis = analysis;
+ this.symbolAllocator = symbolAllocator;
+ this.idAllocator = idAllocator;
+ this.session = session;
+ this.recursiveSubqueries = recursiveSubqueries;
+ }
+
+ @Override
+ protected RelationPlan visitNode(Node node, Void context) {
+ throw new IllegalStateException("Unsupported node type: " + node.getClass().getName());
+ }
+
+ @Override
+ protected RelationPlan visitTable(Table node, Void context) {
+ // is this a recursive reference in expandable named query? If so, there's base relation already
+ // planned.
+ RelationPlan expansion = recursiveSubqueries.get(NodeRef.of(node));
+ if (expansion != null) {
+ // put the pre-planned recursive subquery in the actual outer context to enable resolving
+ // correlation
+ return new RelationPlan(
+ expansion.getRoot(), expansion.getScope(), expansion.getFieldMappings());
}
- @Override
- protected RelationPlan visitNode(Node node, Void context) {
- throw new IllegalStateException("Unsupported node type: " + node.getClass().getName());
- }
+ Query namedQuery = analysis.getNamedQuery(node);
+ Scope scope = analysis.getScope(node);
- @Override
- protected RelationPlan visitTable(Table node, Void context) {
- // is this a recursive reference in expandable named query? If so, there's base relation already planned.
- RelationPlan expansion = recursiveSubqueries.get(NodeRef.of(node));
- if (expansion != null) {
- // put the pre-planned recursive subquery in the actual outer context to enable resolving correlation
- return new RelationPlan(expansion.getRoot(), expansion.getScope(), expansion.getFieldMappings());
- }
+ RelationPlan plan;
+ if (namedQuery != null) {
+ throw new RuntimeException("NamedQuery is not supported");
+ } else {
+ TableHandle handle = analysis.getTableHandle(node);
- Query namedQuery = analysis.getNamedQuery(node);
- Scope scope = analysis.getScope(node);
-
- RelationPlan plan;
- if (namedQuery != null) {
- RelationPlan subPlan;
- if (analysis.isExpandableQuery(namedQuery)) {
- subPlan = new QueryPlanner(analysis, symbolAllocator, idAllocator,
- session, recursiveSubqueries)
- .planExpand(namedQuery);
- } else {
- subPlan = process(namedQuery, null);
- }
-
- // Add implicit coercions if view query produces types that don't match the declared output types
- // of the view (e.g., if the underlying tables referenced by the view changed)
-
- List<Type> types = analysis.getOutputDescriptor(node)
- .getAllFields().stream()
- .map(Field::getType)
- .collect(toImmutableList());
-
- NodeAndMappings coerced = coerce(subPlan, types, symbolAllocator, idAllocator);
-
- plan = new RelationPlan(coerced.getNode(), scope, coerced.getFields());
- } else {
- TableHandle handle = analysis.getTableHandle(node);
-
- ImmutableList.Builder<Symbol> outputSymbolsBuilder = ImmutableList.builder();
- ImmutableMap.Builder<Symbol, ColumnHandle> columns = ImmutableMap.builder();
-
- Collection<Field> fields = analysis.getMaterializedViewStorageTableFields(node);
- boolean hasStorageTableFields = fields != null;
- if (!hasStorageTableFields) {
- fields = scope.getRelationType().getAllFields();
- }
- for (Field field : fields) {
- Symbol symbol = symbolAllocator.newSymbol(field);
-
- outputSymbolsBuilder.add(symbol);
- columns.put(symbol, analysis.getColumn(field));
- }
-
- List<Symbol> outputSymbols = outputSymbolsBuilder.build();
- PlanNode root = TableScanNode.newInstance(idAllocator.genPlanNodeId(), handle, outputSymbols,
- columns.buildOrThrow(), Optional.empty());
-
- plan = new RelationPlan(root, scope, outputSymbols);
-
- if (hasStorageTableFields) {
- List<Type> types = scope.getRelationType().getAllFields().stream()
- .map(Field::getType)
- .collect(toImmutableList());
- // apply required coercion and prune invisible fields from child outputs
- NodeAndMappings coerced = coerce(plan, types, symbolAllocator, idAllocator);
- plan = new RelationPlan(coerced.getNode(), scope, coerced.getFields());
- }
- }
+ ImmutableList.Builder<Symbol> outputSymbolsBuilder = ImmutableList.builder();
+ ImmutableMap.Builder<Symbol, ColumnHandle> columns = ImmutableMap.builder();
- plan = addRowFilters(node, plan);
- plan = addColumnMasks(node, plan);
+ // Collection<Field> fields = analysis.getMaterializedViewStorageTableFields(node);
+ Collection<Field> fields = scope.getRelationType().getAllFields();
+ for (Field field : fields) {
+ Symbol symbol = symbolAllocator.newSymbol(field);
- return plan;
- }
+ outputSymbolsBuilder.add(symbol);
+ columns.put(symbol, analysis.getColumn(field));
+ }
- private RelationPlan addRowFilters(Table node, RelationPlan plan) {
- return addRowFilters(node, plan, Function.identity());
- }
+ List<Symbol> outputSymbols = outputSymbolsBuilder.build();
+ PlanNode root =
+ new TableScanNode(
+ idAllocator.genPlanNodeId(), handle, outputSymbols, columns.buildOrThrow());
- public RelationPlan addRowFilters(Table node, RelationPlan plan, Function<Expression, Expression> predicateTransformation) {
- return addRowFilters(node, plan, predicateTransformation, analysis::getAccessControlScope);
+ plan = new RelationPlan(root, scope, outputSymbols);
}
- public RelationPlan addRowFilters(Table node, RelationPlan plan,
- Function<Expression, Expression> predicateTransformation,
- Function<Table, Scope> accessControlScope) {
- List<Expression> filters = analysis.getRowFilters(node);
+ plan = addRowFilters(node, plan);
+ // TODO what's the meaning of addColumnMasks?
+ // plan = addColumnMasks(node, plan);
- if (filters.isEmpty()) {
- return plan;
- }
-
- PlanBuilder planBuilder = newPlanBuilder(plan, analysis, session)
- .withScope(accessControlScope.apply(node), plan.getFieldMappings()); // The fields in the access control scope has the same layout as those for the table scope
+ return plan;
+ }
- for (Expression filter : filters) {
- planBuilder = subqueryPlanner.handleSubqueries(planBuilder, filter, analysis.getSubqueries(filter));
+ private RelationPlan addRowFilters(Table node, RelationPlan plan) {
+ return addRowFilters(node, plan, Function.identity());
+ }
- Expression predicate = coerceIfNecessary(analysis, filter, planBuilder.rewrite(filter));
- predicate = predicateTransformation.apply(predicate);
- planBuilder = planBuilder.withNewRoot(new FilterNode(
- idAllocator.genPlanNodeId(),
- planBuilder.getRoot(),
- predicate));
- }
+ public RelationPlan addRowFilters(
+ Table node, RelationPlan plan, Function<Expression, Expression> predicateTransformation) {
+ List<Expression> filters = null;
+ // analysis.getRowFilters(node);
- return new RelationPlan(planBuilder.getRoot(), plan.getScope(), plan.getFieldMappings());
+ if (filters.isEmpty()) {
+ return plan;
}
- private RelationPlan addColumnMasks(Table table, RelationPlan plan) {
- Map<String, Expression> columnMasks = analysis.getColumnMasks(table);
-
- // A Table can represent a WITH query, which can have anonymous fields. On the other hand,
- // it can't have masks. The loop below expects fields to have proper names, so bail out
- // if the masks are missing
- if (columnMasks.isEmpty()) {
- return plan;
- }
-
- // The fields in the access control scope has the same layout as those for the table scope
- PlanBuilder planBuilder = newPlanBuilder(plan, analysis, session)
- .withScope(analysis.getAccessControlScope(table), plan.getFieldMappings());
-
- Assignments.Builder assignments = Assignments.builder();
- assignments.putIdentities(planBuilder.getRoot().getOutputSymbols());
-
- List<Symbol> fieldMappings = new ArrayList<>();
- for (int i = 0; i < plan.getDescriptor().getAllFieldCount(); i++) {
- Field field = plan.getDescriptor().getFieldByIndex(i);
+ // The fields in the access control scope has the same layout as those for the table scope
+ PlanBuilder planBuilder = newPlanBuilder(plan, analysis, session);
+ // .withScope(accessControlScope.apply(node), plan.getFieldMappings());
- Expression mask = columnMasks.get(field.getName().orElseThrow());
- Symbol symbol = plan.getFieldMappings().get(i);
- Expression projection = symbol.toSymbolReference();
- if (mask != null) {
- symbol = symbolAllocator.newSymbol(symbol);
- projection = coerceIfNecessary(analysis, mask, planBuilder.rewrite(mask));
- }
+ for (Expression filter : filters) {
+ // planBuilder = subqueryPlanner.handleSubqueries(planBuilder, filter,
+ // analysis.getSubqueries(filter));
- assignments.put(symbol, projection);
- fieldMappings.add(symbol);
- }
-
- planBuilder = planBuilder
- .withNewRoot(new ProjectNode(
- idAllocator.genPlanNodeId(),
- planBuilder.getRoot(),
- assignments.build()));
-
- return new RelationPlan(planBuilder.getRoot(), plan.getScope(), fieldMappings);
+ Expression predicate = coerceIfNecessary(analysis, filter, filter);
+ predicate = predicateTransformation.apply(predicate);
+ planBuilder =
+ planBuilder.withNewRoot(
+ new FilterNode(idAllocator.genPlanNodeId(), planBuilder.getRoot(), predicate));
}
- @Override
- protected RelationPlan visitAliasedRelation(AliasedRelation node, Void context) {
- RelationPlan subPlan = process(node.getRelation(), context);
-
- PlanNode root = subPlan.getRoot();
- List<Symbol> mappings = subPlan.getFieldMappings();
-
- if (node.getColumnNames() != null) {
- ImmutableList.Builder<Symbol> newMappings = ImmutableList.builder();
-
- // Adjust the mappings to expose only the columns visible in the scope of the aliased relation
- for (int i = 0; i < subPlan.getDescriptor().getAllFieldCount(); i++) {
- if (!subPlan.getDescriptor().getFieldByIndex(i).isHidden()) {
- newMappings.add(subPlan.getFieldMappings().get(i));
- }
- }
-
- mappings = newMappings.build();
+ return new RelationPlan(planBuilder.getRoot(), plan.getScope(), plan.getFieldMappings());
+ }
+
+ // private RelationPlan addColumnMasks(Table table, RelationPlan plan) {
+ // Map<String, Expression> columnMasks = analysis.getColumnMasks(table);
+ //
+ // // A Table can represent a WITH query, which can have anonymous fields. On the other
+ // hand,
+ // // it can't have masks. The loop below expects fields to have proper names, so bail out
+ // // if the masks are missing
+ // if (columnMasks.isEmpty()) {
+ // return plan;
+ // }
+ //
+ // // The fields in the access control scope has the same layout as those for the table
+ // scope
+ // PlanBuilder planBuilder = newPlanBuilder(plan, analysis, session)
+ // .withScope(analysis.getAccessControlScope(table), plan.getFieldMappings());
+ //
+ // Assignments.Builder assignments = Assignments.builder();
+ // assignments.putIdentities(planBuilder.getRoot().getOutputSymbols());
+ //
+ // List<Symbol> fieldMappings = new ArrayList<>();
+ // for (int i = 0; i < plan.getDescriptor().getAllFieldCount(); i++) {
+ // Field field = plan.getDescriptor().getFieldByIndex(i);
+ //
+ // Expression mask = columnMasks.get(field.getName().orElseThrow());
+ // Symbol symbol = plan.getFieldMappings().get(i);
+ // Expression projection = symbol.toSymbolReference();
+ // if (mask != null) {
+ // symbol = symbolAllocator.newSymbol(symbol);
+ // projection = coerceIfNecessary(analysis, mask, planBuilder.rewrite(mask));
+ // }
+ //
+ // assignments.put(symbol, projection);
+ // fieldMappings.add(symbol);
+ // }
+ //
+ // planBuilder = planBuilder
+ // .withNewRoot(new ProjectNode(
+ // idAllocator.genPlanNodeId(),
+ // planBuilder.getRoot(),
+ // assignments.build()));
+ //
+ // return new RelationPlan(planBuilder.getRoot(), plan.getScope(), fieldMappings);
+ // }
+
+ @Override
+ protected RelationPlan visitAliasedRelation(AliasedRelation node, Void context) {
+ RelationPlan subPlan = process(node.getRelation(), context);
+
+ PlanNode root = subPlan.getRoot();
+ List<Symbol> mappings = subPlan.getFieldMappings();
+
+ if (node.getColumnNames() != null) {
+ ImmutableList.Builder<Symbol> newMappings = ImmutableList.builder();
+
+ // Adjust the mappings to expose only the columns visible in the scope of the aliased relation
+ for (int i = 0; i < subPlan.getDescriptor().getAllFieldCount(); i++) {
+ if (!subPlan.getDescriptor().getFieldByIndex(i).isHidden()) {
+ newMappings.add(subPlan.getFieldMappings().get(i));
}
+ }
- return new RelationPlan(root, analysis.getScope(node), mappings);
- }
-
- @Override
- protected RelationPlan visitQuery(Query node, Void context) {
- return new QueryPlanner(analysis, symbolAllocator, idAllocator, session, recursiveSubqueries)
- .plan(node);
- }
-
- @Override
- protected RelationPlan visitQuerySpecification(QuerySpecification node, Void context) {
- return new QueryPlanner(analysis, symbolAllocator, idAllocator, session, recursiveSubqueries)
- .plan(node);
+ mappings = newMappings.build();
}
- @Override
- protected RelationPlan visitSubqueryExpression(SubqueryExpression node, Void context) {
- return process(node.getQuery(), context);
- }
+ return new RelationPlan(root, analysis.getScope(node), mappings);
+ }
+
+ @Override
+ protected RelationPlan visitQuery(Query node, Void context) {
+ return new QueryPlanner(analysis, symbolAllocator, idAllocator, session, recursiveSubqueries)
+ .plan(node);
+ }
+
+ @Override
+ protected RelationPlan visitQuerySpecification(QuerySpecification node, Void context) {
+ return new QueryPlanner(analysis, symbolAllocator, idAllocator, session, recursiveSubqueries)
+ .plan(node);
+ }
+
+ @Override
+ protected RelationPlan visitSubqueryExpression(SubqueryExpression node, Void context) {
+ return process(node.getQuery(), context);
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SortOrder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SortOrder.java
new file mode 100644
index 00000000000..70d2cf789a2
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SortOrder.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed 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.iotdb.db.queryengine.plan.relational.planner;
+
+import static java.lang.String.format;
+
+public enum SortOrder {
+ ASC_NULLS_FIRST(true, true),
+ ASC_NULLS_LAST(true, false),
+ DESC_NULLS_FIRST(false, true),
+ DESC_NULLS_LAST(false, false);
+
+ private final boolean ascending;
+ private final boolean nullsFirst;
+
+ SortOrder(boolean ascending, boolean nullsFirst) {
+ this.ascending = ascending;
+ this.nullsFirst = nullsFirst;
+ }
+
+ public boolean isAscending() {
+ return ascending;
+ }
+
+ public boolean isNullsFirst() {
+ return nullsFirst;
+ }
+
+ @Override
+ public String toString() {
+ return format("%s %s", ascending ? "ASC" : "DESC", nullsFirst ? "NULLS FIRST" : "NULLS LAST");
+ }
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java
index 2252c5f3a39..be6c360a5c4 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java
@@ -13,14 +13,15 @@
*/
package org.apache.iotdb.db.queryengine.plan.relational.planner;
+import org.apache.iotdb.db.queryengine.plan.analyze.TypeProvider;
+import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Field;
+import org.apache.iotdb.db.relational.sql.tree.Expression;
+import org.apache.iotdb.db.relational.sql.tree.FunctionCall;
+import org.apache.iotdb.db.relational.sql.tree.SymbolReference;
+import org.apache.iotdb.tsfile.read.common.type.LongType;
+import org.apache.iotdb.tsfile.read.common.type.Type;
+
import com.google.common.primitives.Ints;
-import io.trino.spi.type.BigintType;
-import io.trino.spi.type.Type;
-import io.trino.sql.analyzer.Field;
-import io.trino.sql.ir.Expression;
-import io.trino.sql.ir.FunctionCall;
-import io.trino.sql.ir.SymbolReference;
-import jakarta.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
@@ -29,107 +30,95 @@ import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Locale.ENGLISH;
import static java.util.Objects.requireNonNull;
-public class SymbolAllocator
-{
- private final Map<Symbol, Type> symbols;
- private int nextId;
-
- public SymbolAllocator()
- {
- symbols = new HashMap<>();
- }
+public class SymbolAllocator {
+ private final Map<Symbol, Type> symbols;
+ private int nextId;
- public SymbolAllocator(Map<Symbol, Type> initial)
- {
- symbols = new HashMap<>(initial);
- }
+ public SymbolAllocator() {
+ symbols = new HashMap<>();
+ }
- public Symbol newSymbol(Symbol symbolHint)
- {
- return newSymbol(symbolHint, null);
- }
-
- public Symbol newSymbol(Symbol symbolHint, String suffix)
- {
- checkArgument(symbols.containsKey(symbolHint), "symbolHint not in symbols map");
- return newSymbol(symbolHint.getName(), symbols.get(symbolHint), suffix);
- }
+ public SymbolAllocator(Map<Symbol, Type> initial) {
+ symbols = new HashMap<>(initial);
+ }
- public Symbol newSymbol(String nameHint, Type type)
- {
- return newSymbol(nameHint, type, null);
- }
+ public Symbol newSymbol(Symbol symbolHint) {
+ return newSymbol(symbolHint, null);
+ }
- public Symbol newHashSymbol()
- {
- return newSymbol("$hashValue", BigintType.BIGINT);
- }
+ public Symbol newSymbol(Symbol symbolHint, String suffix) {
+ checkArgument(symbols.containsKey(symbolHint), "symbolHint not in symbols map");
+ return newSymbol(symbolHint.getName(), symbols.get(symbolHint), suffix);
+ }
- public Symbol newSymbol(String nameHint, Type type, @Nullable String suffix)
- {
- requireNonNull(nameHint, "nameHint is null");
- requireNonNull(type, "type is null");
+ public Symbol newSymbol(String nameHint, Type type) {
+ return newSymbol(nameHint, type, null);
+ }
- // TODO: workaround for the fact that QualifiedName lowercases parts
- nameHint = nameHint.toLowerCase(ENGLISH);
+ public Symbol newHashSymbol() {
+ return newSymbol("$hashValue", LongType.getInstance());
+ }
- // don't strip the tail if the only _ is the first character
- int index = nameHint.lastIndexOf("_");
- if (index > 0) {
- String tail = nameHint.substring(index + 1);
+ public Symbol newSymbol(String nameHint, Type type, String suffix) {
+ requireNonNull(nameHint, "nameHint is null");
+ requireNonNull(type, "type is null");
- // only strip if tail is numeric or _ is the last character
- if (Ints.tryParse(tail) != null || index == nameHint.length() - 1) {
- nameHint = nameHint.substring(0, index);
- }
- }
+ // TODO: workaround for the fact that QualifiedName lowercases parts
+ nameHint = nameHint.toLowerCase(ENGLISH);
- String unique = nameHint;
+ // don't strip the tail if the only _ is the first character
+ int index = nameHint.lastIndexOf("_");
+ if (index > 0) {
+ String tail = nameHint.substring(index + 1);
- if (suffix != null) {
- unique = unique + "$" + suffix;
- }
+ // only strip if tail is numeric or _ is the last character
+ if (Ints.tryParse(tail) != null || index == nameHint.length() - 1) {
+ nameHint = nameHint.substring(0, index);
+ }
+ }
- Symbol symbol = new Symbol(unique);
- while (symbols.putIfAbsent(symbol, type) != null) {
- symbol = new Symbol(unique + "_" + nextId());
- }
+ String unique = nameHint;
- return symbol;
+ if (suffix != null) {
+ unique = unique + "$" + suffix;
}
- public Symbol newSymbol(Expression expression, Type type)
- {
- return newSymbol(expression, type, null);
+ Symbol symbol = new Symbol(unique);
+ while (symbols.putIfAbsent(symbol, type) != null) {
+ symbol = new Symbol(unique + "_" + nextId());
}
- public Symbol newSymbol(Expression expression, Type type, String suffix)
- {
- String nameHint = "expr";
- if (expression instanceof FunctionCall functionCall) {
- // symbol allocation can happen during planning, before function calls are rewritten
- nameHint = functionCall.getFunction().getName().getFunctionName();
- }
- else if (expression instanceof SymbolReference symbolReference) {
- nameHint = symbolReference.getName();
- }
-
- return newSymbol(nameHint, type, suffix);
+ return symbol;
+ }
+
+ public Symbol newSymbol(Expression expression, Type type) {
+ return newSymbol(expression, type, null);
+ }
+
+ public Symbol newSymbol(Expression expression, Type type, String suffix) {
+ String nameHint = "expr";
+ if (expression instanceof FunctionCall) {
+ FunctionCall functionCall = (FunctionCall) expression;
+ // symbol allocation can happen during planning, before function calls are rewritten
+ nameHint = functionCall.getName().toString();
+ } else if (expression instanceof SymbolReference) {
+ SymbolReference symbolReference = (SymbolReference) expression;
+ nameHint = symbolReference.getName();
}
- public Symbol newSymbol(Field field)
- {
- String nameHint = field.getName().orElse("field");
- return newSymbol(nameHint, field.getType());
- }
+ return newSymbol(nameHint, type, suffix);
+ }
- public TypeProvider getTypes()
- {
- return TypeProvider.viewOf(symbols);
- }
+ public Symbol newSymbol(Field field) {
+ String nameHint = field.getName().orElse("field");
+ return newSymbol(nameHint, field.getType());
+ }
- private int nextId()
- {
- return nextId++;
- }
+ public TypeProvider getTypes() {
+ return TypeProvider.viewOf(symbols);
+ }
+
+ private int nextId() {
+ return nextId++;
+ }
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/FilterNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/FilterNode.java
index 805078c8afd..e56946216fc 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/FilterNode.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/FilterNode.java
@@ -2,7 +2,6 @@ package org.apache.iotdb.db.queryengine.plan.relational.planner.node;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
-import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.ExchangeNode;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.SingleChildProcessNode;
import org.apache.iotdb.db.relational.sql.tree.Expression;
@@ -12,30 +11,26 @@ import java.nio.ByteBuffer;
import java.util.List;
public class FilterNode extends SingleChildProcessNode {
- private final Expression predicate;
+ private final Expression predicate;
- public FilterNode(PlanNodeId id, PlanNode child, Expression predicate) {
- super(id, child);
- this.predicate = predicate;
- }
+ public FilterNode(PlanNodeId id, PlanNode child, Expression predicate) {
+ super(id, child);
+ this.predicate = predicate;
+ }
- @Override
- public PlanNode clone() {
- return null;
- }
+ @Override
+ public PlanNode clone() {
+ return null;
+ }
- @Override
- public List<String> getOutputColumnNames() {
- return null;
- }
+ @Override
+ public List<String> getOutputColumnNames() {
+ return null;
+ }
- @Override
- protected void serializeAttributes(ByteBuffer byteBuffer) {
+ @Override
+ protected void serializeAttributes(ByteBuffer byteBuffer) {}
- }
-
- @Override
- protected void serializeAttributes(DataOutputStream stream) throws IOException {
-
- }
+ @Override
+ protected void serializeAttributes(DataOutputStream stream) throws IOException {}
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/LimitNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/LimitNode.java
new file mode 100644
index 00000000000..41b376e04be
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/LimitNode.java
@@ -0,0 +1,43 @@
+package org.apache.iotdb.db.queryengine.plan.relational.planner.node;
+
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.SingleChildProcessNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.OrderingScheme;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Optional;
+
+public class LimitNode extends SingleChildProcessNode {
+ private final long count;
+ // what's the meaning?
+ private final Optional<OrderingScheme> tiesResolvingScheme;
+ // private final boolean partial;
+ // private final List<Symbol> preSortedInputs;
+
+ public LimitNode(
+ PlanNodeId id, PlanNode child, long count, Optional<OrderingScheme> tiesResolvingScheme) {
+ super(id, child);
+ this.count = count;
+ this.tiesResolvingScheme = tiesResolvingScheme;
+ }
+
+ @Override
+ public PlanNode clone() {
+ return null;
+ }
+
+ @Override
+ public List<String> getOutputColumnNames() {
+ return null;
+ }
+
+ @Override
+ protected void serializeAttributes(ByteBuffer byteBuffer) {}
+
+ @Override
+ protected void serializeAttributes(DataOutputStream stream) throws IOException {}
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/OffsetNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/OffsetNode.java
new file mode 100644
index 00000000000..c4ea93a27cb
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/OffsetNode.java
@@ -0,0 +1,35 @@
+package org.apache.iotdb.db.queryengine.plan.relational.planner.node;
+
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.SingleChildProcessNode;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class OffsetNode extends SingleChildProcessNode {
+ private final long count;
+
+ public OffsetNode(PlanNodeId id, PlanNode child, long count) {
+ super(id, child);
+ this.count = count;
+ }
+
+ @Override
+ public PlanNode clone() {
+ return null;
+ }
+
+ @Override
+ public List<String> getOutputColumnNames() {
+ return null;
+ }
+
+ @Override
+ protected void serializeAttributes(ByteBuffer byteBuffer) {}
+
+ @Override
+ protected void serializeAttributes(DataOutputStream stream) throws IOException {}
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/OutputNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/OutputNode.java
index befc2af78c0..cb884b09a56 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/OutputNode.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/OutputNode.java
@@ -12,35 +12,32 @@ import java.util.List;
public class OutputNode extends SingleChildProcessNode {
- private final List<String> outputColumnNames;
-
- private final List<Symbol> symbols;
-
- public OutputNode(PlanNodeId id, PlanNode child, List<String> outputColumnNames, List<Symbol> symbols) {
- super(id, child);
- this.id = id;
- this.child = child;
- this.outputColumnNames = outputColumnNames;
- this.symbols = symbols;
- }
-
- @Override
- public PlanNode clone() {
- return null;
- }
-
- @Override
- public List<String> getOutputColumnNames() {
- return null;
- }
-
- @Override
- protected void serializeAttributes(ByteBuffer byteBuffer) {
-
- }
-
- @Override
- protected void serializeAttributes(DataOutputStream stream) throws IOException {
-
- }
+ private final List<String> outputColumnNames;
+
+ private final List<Symbol> symbols;
+
+ public OutputNode(
+ PlanNodeId id, PlanNode child, List<String> outputColumnNames, List<Symbol> symbols) {
+ super(id, child);
+ this.id = id;
+ this.child = child;
+ this.outputColumnNames = outputColumnNames;
+ this.symbols = symbols;
+ }
+
+ @Override
+ public PlanNode clone() {
+ return null;
+ }
+
+ @Override
+ public List<String> getOutputColumnNames() {
+ return null;
+ }
+
+ @Override
+ protected void serializeAttributes(ByteBuffer byteBuffer) {}
+
+ @Override
+ protected void serializeAttributes(DataOutputStream stream) throws IOException {}
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ProjectNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ProjectNode.java
index 20fbfd1d02f..ff8de50de27 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ProjectNode.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ProjectNode.java
@@ -11,30 +11,26 @@ import java.nio.ByteBuffer;
import java.util.List;
public class ProjectNode extends SingleChildProcessNode {
- private final Assignments assignments;
+ private final Assignments assignments;
- public ProjectNode(PlanNodeId id, PlanNode child, Assignments assignments) {
- super(id, child);
- this.assignments = assignments;
- }
+ public ProjectNode(PlanNodeId id, PlanNode child, Assignments assignments) {
+ super(id, child);
+ this.assignments = assignments;
+ }
- @Override
- public PlanNode clone() {
- return null;
- }
+ @Override
+ public PlanNode clone() {
+ return null;
+ }
- @Override
- public List<String> getOutputColumnNames() {
- return null;
- }
+ @Override
+ public List<String> getOutputColumnNames() {
+ return null;
+ }
- @Override
- protected void serializeAttributes(ByteBuffer byteBuffer) {
+ @Override
+ protected void serializeAttributes(ByteBuffer byteBuffer) {}
- }
-
- @Override
- protected void serializeAttributes(DataOutputStream stream) throws IOException {
-
- }
+ @Override
+ protected void serializeAttributes(DataOutputStream stream) throws IOException {}
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/SortNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/SortNode.java
new file mode 100644
index 00000000000..37358d3d696
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/SortNode.java
@@ -0,0 +1,38 @@
+package org.apache.iotdb.db.queryengine.plan.relational.planner.node;
+
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.SingleChildProcessNode;
+import org.apache.iotdb.db.queryengine.plan.relational.planner.OrderingScheme;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class SortNode extends SingleChildProcessNode {
+ private final OrderingScheme orderingScheme;
+ private final boolean partial;
+
+ public SortNode(PlanNodeId id, PlanNode planNode, OrderingScheme scheme, boolean partial) {
+ super(id, planNode);
+ this.orderingScheme = scheme;
+ this.partial = partial;
+ }
+
+ @Override
+ public PlanNode clone() {
+ return null;
+ }
+
+ @Override
+ public List<String> getOutputColumnNames() {
+ return null;
+ }
+
+ @Override
+ protected void serializeAttributes(ByteBuffer byteBuffer) {}
+
+ @Override
+ protected void serializeAttributes(DataOutputStream stream) throws IOException {}
+}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/TableScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/TableScanNode.java
index c6f6c64f671..860bbbab443 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/TableScanNode.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/TableScanNode.java
@@ -1,6 +1,7 @@
package org.apache.iotdb.db.queryengine.plan.relational.planner.node;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.ColumnHandle;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableHandle;
import org.apache.iotdb.db.queryengine.plan.relational.planner.Symbol;
@@ -12,42 +13,47 @@ import java.util.List;
import java.util.Map;
public class TableScanNode extends PlanNode {
- private final TableHandle table;
- private final List<Symbol> outputSymbols;
- private final Map<Symbol, ColumnHandle> assignments; // symbol -> column
-
- @Override
- public List<PlanNode> getChildren() {
- return null;
- }
-
- @Override
- public void addChild(PlanNode child) {
-
- }
-
- @Override
- public PlanNode clone() {
- return null;
- }
-
- @Override
- public int allowedChildCount() {
- return 0;
- }
-
- @Override
- public List<String> getOutputColumnNames() {
- return null;
- }
-
- @Override
- protected void serializeAttributes(ByteBuffer byteBuffer) {
-
- }
-
- @Override
- protected void serializeAttributes(DataOutputStream stream) throws IOException {
-
- }
+ private final TableHandle table;
+ private final List<Symbol> outputSymbols;
+ private final Map<Symbol, ColumnHandle> assignments; // symbol -> column
+
+ public TableScanNode(
+ PlanNodeId id,
+ TableHandle table,
+ List<Symbol> outputSymbols,
+ Map<Symbol, ColumnHandle> assignments) {
+ super(id);
+ this.table = table;
+ this.outputSymbols = outputSymbols;
+ this.assignments = assignments;
+ }
+
+ @Override
+ public List<PlanNode> getChildren() {
+ return null;
+ }
+
+ @Override
+ public void addChild(PlanNode child) {}
+
+ @Override
+ public PlanNode clone() {
+ return null;
+ }
+
+ @Override
+ public int allowedChildCount() {
+ return 0;
+ }
+
+ @Override
+ public List<String> getOutputColumnNames() {
+ return null;
+ }
+
+ @Override
+ protected void serializeAttributes(ByteBuffer byteBuffer) {}
+
+ @Override
+ protected void serializeAttributes(DataOutputStream stream) throws IOException {}
}
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
index 01b972ada52..35b7e24919c 100644
--- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
+++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java
@@ -122,8 +122,8 @@ public class AnalyzerTest {
@Test
public void testSingleTableQuery() throws OperatorNotFoundException {
String sql =
- "SELECT tag1 as tmp_tag, tag2, attribute1, s1+1 as add_s1, s2 FROM table1 " +
- "WHERE time>1 AND tag1=\"A\" and tag3=\"B\" AND s1=1 AND s3=3 ORDER BY time DESC OFFSET 10 LIMIT 5";
+ "SELECT tag1 as tmp_tag, tag2, attribute1, s1+1 as add_s1, s2 FROM table1 "
+ + "WHERE time>1 AND tag1=\"A\" and tag3=\"B\" AND s1=1 AND s3=3 ORDER BY time DESC OFFSET 10 LIMIT 5";
Metadata metadata = new TestMatadata();
TableHandle tableHandle = Mockito.mock(TableHandle.class);
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java
index 1ed6652d1a9..294dc28b354 100644
--- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java
+++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMatadata.java
@@ -12,6 +12,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableHandle;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableMetadata;
import org.apache.iotdb.db.queryengine.plan.relational.metadata.TableSchema;
import org.apache.iotdb.tsfile.read.common.type.Type;
+
import org.mockito.Mockito;
import java.util.Arrays;
@@ -25,55 +26,57 @@ import static org.apache.iotdb.tsfile.read.common.type.LongType.INT64;
public class TestMatadata implements Metadata {
- public static final String DB1 = "db1";
- public static final String DB2 = "db2";
- public static final String TABLE1 = "table1";
- public static final String TABLE2 = "table2";
-
+ public static final String DB1 = "db1";
+ public static final String DB2 = "db2";
+ public static final String TABLE1 = "table1";
+ public static final String TABLE2 = "table2";
- @Override
- public boolean tableExists(QualifiedObjectName name) {
- return name.getDatabaseName().equalsIgnoreCase(DB1) && name.getObjectName().equalsIgnoreCase(TABLE1);
- }
+ @Override
+ public boolean tableExists(QualifiedObjectName name) {
+ return name.getDatabaseName().equalsIgnoreCase(DB1)
+ && name.getObjectName().equalsIgnoreCase(TABLE1);
+ }
- @Override
- public TableSchema getTableSchema(SessionInfo session, TableHandle tableHandle) {
- return null;
- }
+ @Override
+ public TableSchema getTableSchema(SessionInfo session, TableHandle tableHandle) {
+ return null;
+ }
- @Override
- public TableMetadata getTableMetadata(SessionInfo session, TableHandle tableHandle) {
- return null;
- }
+ @Override
+ public TableMetadata getTableMetadata(SessionInfo session, TableHandle tableHandle) {
+ return null;
+ }
- @Override
- public Optional<TableHandle> getTableHandle(SessionInfo session, QualifiedObjectName name) {
- return Optional.empty();
- }
+ @Override
+ public Optional<TableHandle> getTableHandle(SessionInfo session, QualifiedObjectName name) {
+ return Optional.empty();
+ }
- @Override
- public Map<String, ColumnHandle> getColumnHandles(SessionInfo session, TableHandle tableHandle) {
- Map<String, ColumnHandle> map = new HashMap<>();
- TableSchema tableSchema = Mockito.mock(TableSchema.class);
- Mockito.when(tableSchema.getTableName()).thenReturn("table1");
- ColumnSchema column1 =
- ColumnSchema.builder().setName("time").setType(INT64).setHidden(false).build();
- ColumnHandle column1Handle = Mockito.mock(ColumnHandle.class);
- map.put("time", column1Handle);
- ColumnSchema column2 =
- ColumnSchema.builder().setName("s1").setType(INT32).setHidden(false).build();
- ColumnHandle column2Handle = Mockito.mock(ColumnHandle.class);
- map.put("s1", column2Handle);
- ColumnSchema column3 =
- ColumnSchema.builder().setName("s2").setType(INT64).setHidden(false).build();
- ColumnHandle column3Handle = Mockito.mock(ColumnHandle.class);
- map.put("s2", column3Handle);
- List<ColumnSchema> columnSchemaList = Arrays.asList(column1, column2, column3);
- return columnSchemaList;
- }
+ @Override
+ public Map<String, ColumnHandle> getColumnHandles(SessionInfo session, TableHandle tableHandle) {
+ Map<String, ColumnHandle> map = new HashMap<>();
+ TableSchema tableSchema = Mockito.mock(TableSchema.class);
+ Mockito.when(tableSchema.getTableName()).thenReturn("table1");
+ ColumnSchema column1 =
+ ColumnSchema.builder().setName("time").setType(INT64).setHidden(false).build();
+ ColumnHandle column1Handle = Mockito.mock(ColumnHandle.class);
+ map.put("time", column1Handle);
+ ColumnSchema column2 =
+ ColumnSchema.builder().setName("s1").setType(INT32).setHidden(false).build();
+ ColumnHandle column2Handle = Mockito.mock(ColumnHandle.class);
+ map.put("s1", column2Handle);
+ ColumnSchema column3 =
+ ColumnSchema.builder().setName("s2").setType(INT64).setHidden(false).build();
+ ColumnHandle column3Handle = Mockito.mock(ColumnHandle.class);
+ map.put("s2", column3Handle);
+ List<ColumnSchema> columnSchemaList = Arrays.asList(column1, column2, column3);
+ return columnSchemaList;
+ }
- @Override
- public ResolvedFunction resolveOperator(OperatorType operatorType, List<? extends Type> argumentTypes) throws OperatorNotFoundException {
- return null;
- }
+ @Override
+ public ResolvedFunction resolveOperator(
+ OperatorType operatorType, List<? extends Type> argumentTypes)
+ throws OperatorNotFoundException {
+ return null;
+ }
}