This is an automated email from the ASF dual-hosted git repository. jackietien pushed a commit to branch ty/groupByTimeParser in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit cde52a00fc23104dfe8e7fa5488e77224a62f54b Author: JackieTien97 <[email protected]> AuthorDate: Fri Jun 14 17:15:30 2024 +0800 Support for group by time sql paser --- .../protocol/thrift/impl/ClientRPCServiceImpl.java | 2 +- .../db/queryengine/plan/parser/ASTVisitor.java | 8 +- .../plan/relational/metadata/MetadataUtil.java | 2 +- .../plan/relational/sql/ast/AstVisitor.java | 4 + .../plan/relational/sql/ast/GroupByTime.java | 145 ++++++++++++++++ .../plan/relational/sql/ast/SimpleGroupBy.java | 2 +- .../sql/ast/{SimpleGroupBy.java => TimeRange.java} | 74 +++++---- .../plan/relational/sql/parser/AstBuilder.java | 183 ++++++++++++++------- .../plan/relational/sql/parser/SqlParser.java | 52 +++--- .../relational/sql/util/ReservedIdentifiers.java | 3 +- .../org/apache/iotdb/db/utils/DateTimeUtils.java | 64 +++++-- .../plan/relational/analyzer/AnalyzerTest.java | 2 +- .../db/relational/grammar/sql/RelationalSql.g4 | 56 +++++-- 13 files changed, 444 insertions(+), 153 deletions(-) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java index b66b2dd4f35..9829d41513b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java @@ -336,7 +336,7 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { req.getTimeout()); } else { org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Statement s = - relationSqlParser.createStatement(statement); + relationSqlParser.createStatement(statement, clientSession.getZoneId()); if (s == null) { return RpcUtils.getTSExecuteStatementResp( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java index 261f3bb7ed9..24413ba8c50 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java @@ -2157,8 +2157,8 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> { } } - public long parseDateTimeFormat(String timestampStr, long currentTime) { - if (timestampStr == null || "".equals(timestampStr.trim())) { + public static long parseDateTimeFormat(String timestampStr, long currentTime, ZoneId zoneId) { + if (timestampStr == null || timestampStr.trim().isEmpty()) { throw new SemanticException("input timestamp cannot be empty"); } if (timestampStr.equalsIgnoreCase(SqlConstant.NOW_FUNC)) { @@ -3137,7 +3137,7 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> { private Long parseDateExpression(IoTDBSqlParser.DateExpressionContext ctx, long currentTime) { long time; - time = parseDateTimeFormat(ctx.getChild(0).getText(), currentTime); + time = parseDateTimeFormat(ctx.getChild(0).getText(), currentTime, zoneId); for (int i = 1; i < ctx.getChildCount(); i = i + 2) { if ("+".equals(ctx.getChild(i).getText())) { time += DateTimeUtils.convertDurationStrToLong(time, ctx.getChild(i + 1).getText(), false); @@ -3162,7 +3162,7 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> { } else if (ctx.dateExpression() != null) { return parseDateExpression(ctx.dateExpression(), currentTime); } else { - return parseDateTimeFormat(ctx.datetimeLiteral().getText(), currentTime); + return parseDateTimeFormat(ctx.datetimeLiteral().getText(), currentTime, zoneId); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/MetadataUtil.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/MetadataUtil.java index d625807b57e..b97141817b0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/MetadataUtil.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/MetadataUtil.java @@ -84,7 +84,7 @@ public class MetadataUtil { .orElseThrow( () -> new SemanticException( - "Catalog must be specified when session catalog is not set")); + "Database must be specified when session database is not set")); return new QualifiedObjectName(databaseName, objectName); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java index d2c1e487458..fa590cfca61 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java @@ -391,6 +391,10 @@ public abstract class AstVisitor<R, C> { return visitNode(node, context); } + protected R visitGroupByTime(GroupByTime node, C context) { + return visitGroupingElement(node, context); + } + protected R visitGroupingSets(GroupingSets node, C context) { return visitGroupingElement(node, context); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/GroupByTime.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/GroupByTime.java new file mode 100644 index 00000000000..2d4068e6c27 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/GroupByTime.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; + +import org.apache.tsfile.utils.TimeDuration; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class GroupByTime extends GroupingElement { + + // [startTime, endTime) + private final long startTime; + private final long endTime; + // time interval + private final TimeDuration interval; + // sliding step + private final TimeDuration slidingStep; + // if it is left close and right open interval + private final boolean leftCRightO; + + public GroupByTime( + long startTime, + long endTime, + TimeDuration interval, + TimeDuration slidingStep, + boolean leftCRightO) { + super(null); + this.startTime = startTime; + this.endTime = endTime; + this.interval = interval; + this.slidingStep = slidingStep; + this.leftCRightO = leftCRightO; + } + + public GroupByTime( + NodeLocation location, + long startTime, + long endTime, + TimeDuration interval, + TimeDuration slidingStep, + boolean leftCRightO) { + super(location); + this.startTime = startTime; + this.endTime = endTime; + this.interval = interval; + this.slidingStep = slidingStep; + this.leftCRightO = leftCRightO; + } + + @Override + public List<Expression> getExpressions() { + return Collections.emptyList(); + } + + @Override + protected <R, C> R accept(AstVisitor<R, C> visitor, C context) { + return visitor.visitGroupByTime(this, context); + } + + @Override + public List<? extends Node> getChildren() { + return Collections.emptyList(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GroupByTime that = (GroupByTime) o; + return startTime == that.startTime + && endTime == that.endTime + && leftCRightO == that.leftCRightO + && Objects.equals(interval, that.interval) + && Objects.equals(slidingStep, that.slidingStep); + } + + @Override + public int hashCode() { + return Objects.hash(startTime, endTime, interval, slidingStep, leftCRightO); + } + + @Override + public String toString() { + return "GroupByTime{" + + "startTime=" + + startTime + + ", endTime=" + + endTime + + ", interval=" + + interval + + ", slidingStep=" + + slidingStep + + ", leftCRightO=" + + leftCRightO + + '}'; + } + + @Override + public boolean shallowEquals(Node other) { + return sameClass(this, other); + } + + public long getStartTime() { + return startTime; + } + + public long getEndTime() { + return endTime; + } + + public TimeDuration getInterval() { + return interval; + } + + public TimeDuration getSlidingStep() { + return slidingStep; + } + + public boolean isLeftCRightO() { + return leftCRightO; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SimpleGroupBy.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SimpleGroupBy.java index 88193960f7d..88b80a6072b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SimpleGroupBy.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SimpleGroupBy.java @@ -57,7 +57,7 @@ public final class SimpleGroupBy extends GroupingElement { @Override public List<? extends Node> getChildren() { - return columns; + return getExpressions(); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SimpleGroupBy.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/TimeRange.java similarity index 50% copy from iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SimpleGroupBy.java copy to iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/TimeRange.java index 88193960f7d..c1fe213ff14 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SimpleGroupBy.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/TimeRange.java @@ -19,45 +19,35 @@ package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; -import com.google.common.collect.ImmutableList; - +import java.util.Collections; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static java.util.Objects.requireNonNull; - -public final class SimpleGroupBy extends GroupingElement { +public class TimeRange extends Node { - private final List<Expression> columns; + // [startTime, endTime) + private final long startTime; + private final long endTime; + // if it is left close and right open interval + private final boolean leftCRightO; - public SimpleGroupBy(List<Expression> simpleGroupByExpressions) { + public TimeRange(long startTime, long endTime, boolean leftCRightO) { super(null); - this.columns = - ImmutableList.copyOf( - requireNonNull(simpleGroupByExpressions, "simpleGroupByExpressions is null")); + this.startTime = startTime; + this.endTime = endTime; + this.leftCRightO = leftCRightO; } - public SimpleGroupBy(NodeLocation location, List<Expression> simpleGroupByExpressions) { - super(requireNonNull(location, "location is null")); - this.columns = - ImmutableList.copyOf( - requireNonNull(simpleGroupByExpressions, "simpleGroupByExpressions is null")); - } - - @Override - public List<Expression> getExpressions() { - return columns; - } - - @Override - protected <R, C> R accept(AstVisitor<R, C> visitor, C context) { - return visitor.visitSimpleGroupBy(this, context); + public TimeRange(NodeLocation location, long startTime, long endTime, boolean leftCRightO) { + super(location); + this.startTime = startTime; + this.endTime = endTime; + this.leftCRightO = leftCRightO; } @Override public List<? extends Node> getChildren() { - return columns; + return Collections.emptyList(); } @Override @@ -68,22 +58,38 @@ public final class SimpleGroupBy extends GroupingElement { if (o == null || getClass() != o.getClass()) { return false; } - SimpleGroupBy that = (SimpleGroupBy) o; - return Objects.equals(columns, that.columns); + TimeRange timeRange = (TimeRange) o; + return startTime == timeRange.startTime + && endTime == timeRange.endTime + && leftCRightO == timeRange.leftCRightO; } @Override public int hashCode() { - return Objects.hash(columns); + return Objects.hash(startTime, endTime, leftCRightO); } @Override public String toString() { - return toStringHelper(this).add("columns", columns).toString(); + return "TimeRange{" + + "startTime=" + + startTime + + ", endTime=" + + endTime + + ", leftCRightO=" + + leftCRightO + + '}'; } - @Override - public boolean shallowEquals(Node other) { - return sameClass(this, other); + public long getStartTime() { + return startTime; + } + + public long getEndTime() { + return endTime; + } + + public boolean isLeftCRightO() { + return leftCRightO; } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java index 52fa55f87be..f50fb72866d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java @@ -20,6 +20,8 @@ package org.apache.iotdb.db.queryengine.plan.relational.sql.parser; import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory; +import org.apache.iotdb.commons.utils.CommonDateTimeUtils; +import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AddColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AliasedRelation; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AllColumns; @@ -56,6 +58,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.FunctionCall; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.GenericDataType; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.GroupBy; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.GroupByTime; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.GroupingElement; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.GroupingSets; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Identifier; @@ -110,6 +113,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StringLiteral; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SubqueryExpression; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Table; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.TableSubquery; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.TimeRange; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Trim; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.TypeParameter; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Union; @@ -123,15 +127,18 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.WithQuery; import org.apache.iotdb.db.relational.grammar.sql.RelationalSqlBaseVisitor; import org.apache.iotdb.db.relational.grammar.sql.RelationalSqlLexer; import org.apache.iotdb.db.relational.grammar.sql.RelationalSqlParser; +import org.apache.iotdb.db.utils.DateTimeUtils; import com.google.common.collect.ImmutableList; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; +import org.apache.tsfile.utils.TimeDuration; import javax.annotation.Nullable; +import java.time.ZoneId; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; @@ -147,6 +154,7 @@ import static org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory import static org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory.ID; import static org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory.MEASUREMENT; import static org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory.TIME; +import static org.apache.iotdb.db.queryengine.plan.parser.ASTVisitor.parseDateTimeFormat; import static org.apache.iotdb.db.queryengine.plan.relational.sql.ast.GroupingSets.Type.CUBE; import static org.apache.iotdb.db.queryengine.plan.relational.sql.ast.GroupingSets.Type.EXPLICIT; import static org.apache.iotdb.db.queryengine.plan.relational.sql.ast.GroupingSets.Type.ROLLUP; @@ -157,8 +165,11 @@ public class AstBuilder extends RelationalSqlBaseVisitor<Node> { @Nullable private final NodeLocation baseLocation; - AstBuilder(@Nullable NodeLocation baseLocation) { + private final ZoneId zoneId; + + AstBuilder(@Nullable NodeLocation baseLocation, ZoneId zoneId) { this.baseLocation = baseLocation; + this.zoneId = zoneId; } @Override @@ -685,6 +696,116 @@ public class AstBuilder extends RelationalSqlBaseVisitor<Node> { visit(ctx.groupingElement(), GroupingElement.class)); } + @Override + public Node visitTimenGrouping(RelationalSqlParser.TimenGroupingContext ctx) { + long startTime = 0; + long endTime = 0; + boolean leftCRightO = true; + if (ctx.timeRange() != null) { + TimeRange timeRange = (TimeRange) visit(ctx.timeRange()); + startTime = timeRange.getStartTime(); + endTime = timeRange.getEndTime(); + leftCRightO = timeRange.isLeftCRightO(); + } + // Parse time interval + TimeDuration interval = DateTimeUtils.constructTimeDuration(ctx.windowInterval.getText()); + TimeDuration slidingStep = interval; + if (ctx.windowStep != null) { + slidingStep = DateTimeUtils.constructTimeDuration(ctx.windowStep.getText()); + } + + if (interval.monthDuration <= 0 && interval.nonMonthDuration <= 0) { + throw new SemanticException( + "The second parameter time interval should be a positive integer."); + } + + if (slidingStep.monthDuration <= 0 && slidingStep.nonMonthDuration <= 0) { + throw new SemanticException( + "The third parameter time slidingStep should be a positive integer."); + } + return new GroupByTime( + getLocation(ctx), startTime, endTime, interval, slidingStep, leftCRightO); + } + + @Override + public Node visitLeftClosedRightOpen(RelationalSqlParser.LeftClosedRightOpenContext ctx) { + return getTimeRange(ctx.timeValue(0), ctx.timeValue(1), true); + } + + @Override + public Node visitLeftOpenRightClosed(RelationalSqlParser.LeftOpenRightClosedContext ctx) { + return getTimeRange(ctx.timeValue(0), ctx.timeValue(1), false); + } + + private TimeRange getTimeRange( + RelationalSqlParser.TimeValueContext left, + RelationalSqlParser.TimeValueContext right, + boolean leftCRightO) { + long currentTime = CommonDateTimeUtils.currentTime(); + long startTime = parseTimeValue(left, currentTime); + long endTime = parseTimeValue(right, currentTime); + if (startTime >= endTime) { + throw new SemanticException("Start time should be smaller than endTime in GroupBy"); + } + return new TimeRange(startTime, endTime, leftCRightO); + } + + private long parseTimeValue(RelationalSqlParser.TimeValueContext ctx, long currentTime) { + if (ctx.DECIMAL_INTEGER_LITERAL() != null) { + try { + if (ctx.MINUS() != null) { + return -Long.parseLong(ctx.DECIMAL_INTEGER_LITERAL().getText()); + } + return Long.parseLong(ctx.DECIMAL_INTEGER_LITERAL().getText()); + } catch (NumberFormatException e) { + throw new SemanticException( + String.format( + "Can not parse %s to long value", ctx.DECIMAL_INTEGER_LITERAL().getText())); + } + } else { + return parseDateExpression(ctx.dateExpression(), currentTime); + } + } + + private Long parseDateExpression( + RelationalSqlParser.DateExpressionContext ctx, long currentTime) { + long time; + time = parseDateTimeFormat(ctx.getChild(0).getText(), currentTime, zoneId); + for (int i = 1; i < ctx.getChildCount(); i = i + 2) { + if ("+".equals(ctx.getChild(i).getText())) { + time += DateTimeUtils.convertDurationStrToLong(time, ctx.getChild(i + 1).getText(), false); + } else { + time -= DateTimeUtils.convertDurationStrToLong(time, ctx.getChild(i + 1).getText(), false); + } + } + return time; + } + + @Override + public Node visitVariationGrouping(RelationalSqlParser.VariationGroupingContext ctx) { + return super.visitVariationGrouping(ctx); + } + + @Override + public Node visitConditionGrouping(RelationalSqlParser.ConditionGroupingContext ctx) { + return super.visitConditionGrouping(ctx); + } + + @Override + public Node visitSessionGrouping(RelationalSqlParser.SessionGroupingContext ctx) { + return super.visitSessionGrouping(ctx); + } + + @Override + public Node visitCountGrouping(RelationalSqlParser.CountGroupingContext ctx) { + return super.visitCountGrouping(ctx); + } + + @Override + public Node visitKeepExpression(RelationalSqlParser.KeepExpressionContext ctx) { + return super.visitKeepExpression(ctx); + } + @Override public Node visitSingleGroupingSet(RelationalSqlParser.SingleGroupingSetContext ctx) { return new SimpleGroupBy( @@ -795,61 +916,6 @@ public class AstBuilder extends RelationalSqlBaseVisitor<Node> { return new Identifier(getLocation(ctx), identifier, true); } - @Override - public Node visitTimenGrouping(RelationalSqlParser.TimenGroupingContext ctx) { - return super.visitTimenGrouping(ctx); - } - - @Override - public Node visitVariationGrouping(RelationalSqlParser.VariationGroupingContext ctx) { - return super.visitVariationGrouping(ctx); - } - - @Override - public Node visitConditionGrouping(RelationalSqlParser.ConditionGroupingContext ctx) { - return super.visitConditionGrouping(ctx); - } - - @Override - public Node visitSessionGrouping(RelationalSqlParser.SessionGroupingContext ctx) { - return super.visitSessionGrouping(ctx); - } - - @Override - public Node visitCountGrouping(RelationalSqlParser.CountGroupingContext ctx) { - return super.visitCountGrouping(ctx); - } - - @Override - public Node visitLeftClosedRightOpen(RelationalSqlParser.LeftClosedRightOpenContext ctx) { - return super.visitLeftClosedRightOpen(ctx); - } - - @Override - public Node visitLeftOpenRightClosed(RelationalSqlParser.LeftOpenRightClosedContext ctx) { - return super.visitLeftOpenRightClosed(ctx); - } - - @Override - public Node visitTimeValue(RelationalSqlParser.TimeValueContext ctx) { - return super.visitTimeValue(ctx); - } - - @Override - public Node visitDateExpression(RelationalSqlParser.DateExpressionContext ctx) { - return super.visitDateExpression(ctx); - } - - @Override - public Node visitDatetimeLiteral(RelationalSqlParser.DatetimeLiteralContext ctx) { - return super.visitDatetimeLiteral(ctx); - } - - @Override - public Node visitKeepExpression(RelationalSqlParser.KeepExpressionContext ctx) { - return super.visitKeepExpression(ctx); - } - // ***************** boolean expressions ****************** @Override public Node visitLogicalNot(RelationalSqlParser.LogicalNotContext ctx) { @@ -1403,11 +1469,6 @@ public class AstBuilder extends RelationalSqlBaseVisitor<Node> { return super.visitIntervalField(ctx); } - @Override - public Node visitTimeDuration(RelationalSqlParser.TimeDurationContext ctx) { - return super.visitTimeDuration(ctx); - } - // ***************** arguments ***************** @Override public Node visitGenericType(RelationalSqlParser.GenericTypeContext ctx) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/SqlParser.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/SqlParser.java index c82e1f8a56f..b20fb829326 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/SqlParser.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/SqlParser.java @@ -44,6 +44,7 @@ import org.antlr.v4.runtime.atn.PredictionMode; import org.antlr.v4.runtime.misc.Pair; import org.antlr.v4.runtime.tree.TerminalNode; +import java.time.ZoneId; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -93,35 +94,43 @@ public class SqlParser { this.initializer = requireNonNull(initializer, "initializer is null"); } - public Statement createStatement(String sql) { - return (Statement) invokeParser("statement", sql, RelationalSqlParser::singleStatement); + public Statement createStatement(String sql, ZoneId zoneId) { + return (Statement) invokeParser("statement", sql, RelationalSqlParser::singleStatement, zoneId); } - public Statement createStatement(String sql, NodeLocation location) { + public Statement createStatement(String sql, NodeLocation location, ZoneId zoneId) { return (Statement) invokeParser( - "statement", sql, Optional.ofNullable(location), RelationalSqlParser::singleStatement); + "statement", + sql, + Optional.ofNullable(location), + RelationalSqlParser::singleStatement, + zoneId); } - public Expression createExpression(String expression) { + public Expression createExpression(String expression, ZoneId zoneId) { return (Expression) - invokeParser("expression", expression, RelationalSqlParser::standaloneExpression); + invokeParser("expression", expression, RelationalSqlParser::standaloneExpression, zoneId); } - public DataType createType(String expression) { - return (DataType) invokeParser("type", expression, RelationalSqlParser::standaloneType); + public DataType createType(String expression, ZoneId zoneId) { + return (DataType) invokeParser("type", expression, RelationalSqlParser::standaloneType, zoneId); } private Node invokeParser( - String name, String sql, Function<RelationalSqlParser, ParserRuleContext> parseFunction) { - return invokeParser(name, sql, Optional.empty(), parseFunction); + String name, + String sql, + Function<RelationalSqlParser, ParserRuleContext> parseFunction, + ZoneId zoneId) { + return invokeParser(name, sql, Optional.empty(), parseFunction, zoneId); } private Node invokeParser( String name, String sql, Optional<NodeLocation> location, - Function<RelationalSqlParser, ParserRuleContext> parseFunction) { + Function<RelationalSqlParser, ParserRuleContext> parseFunction, + ZoneId zoneId) { try { RelationalSqlLexer lexer = new RelationalSqlLexer(new CaseInsensitiveStream(CharStreams.fromString(sql))); @@ -178,7 +187,7 @@ public class SqlParser { throw e; } - return new AstBuilder(location.orElse(null)).visit(tree); + return new AstBuilder(location.orElse(null), zoneId).visit(tree); } catch (StackOverflowError e) { throw new ParsingException(name + " is too large (stack overflow while parsing)"); } @@ -215,15 +224,16 @@ public class SqlParser { token.getCharPositionInLine() + 1); } - @Override - public void exitDigitIdentifier(RelationalSqlParser.DigitIdentifierContext context) { - Token token = context.DIGIT_IDENTIFIER().getSymbol(); - throw new ParsingException( - "identifiers must not start with a digit; surround the identifier with double quotes", - null, - token.getLine(), - token.getCharPositionInLine() + 1); - } + // @Override + // public void exitDigitIdentifier(RelationalSqlParser.DigitIdentifierContext context) { + // Token token = context.DIGIT_IDENTIFIER().getSymbol(); + // throw new ParsingException( + // "identifiers must not start with a digit; surround the identifier with double + // quotes", + // null, + // token.getLine(), + // token.getCharPositionInLine() + 1); + // } @Override public void exitNonReserved(RelationalSqlParser.NonReservedContext context) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/ReservedIdentifiers.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/ReservedIdentifiers.java index d40f6880b9b..2fde6ee0d31 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/ReservedIdentifiers.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/ReservedIdentifiers.java @@ -23,6 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Identifier; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.ParsingException; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; +import java.time.ZoneId; import java.util.Set; import static com.google.common.collect.ImmutableSet.toImmutableSet; @@ -40,7 +41,7 @@ public final class ReservedIdentifiers { public static boolean reserved(String name) { try { - return !(PARSER.createExpression(name) instanceof Identifier); + return !(PARSER.createExpression(name, ZoneId.systemDefault()) instanceof Identifier); } catch (ParsingException ignored) { return true; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DateTimeUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DateTimeUtils.java index 1a3c394af7d..dc24893f32a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DateTimeUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DateTimeUtils.java @@ -61,12 +61,15 @@ public class DateTimeUtils { static { switch (CommonDescriptor.getInstance().getConfig().getTimestampPrecision()) { case "us": + case "microsecond": CAST_TIMESTAMP_TO_MS = timestamp -> timestamp / 1000; break; case "ns": + case "nanosecond": CAST_TIMESTAMP_TO_MS = timestamp -> timestamp / 1000000; break; case "ms": + case "millisecond": default: CAST_TIMESTAMP_TO_MS = timestamp -> timestamp; break; @@ -501,7 +504,7 @@ public class DateTimeUtils { try { ZonedDateTime zonedDateTime = ZonedDateTime.parse(str, formatter); Instant instant = zonedDateTime.toInstant(); - if ("us".equals(timestampPrecision)) { + if ("us".equals(timestampPrecision) || "microsecond".equals(timestampPrecision)) { if (instant.getEpochSecond() < 0 && instant.getNano() > 0) { // adjustment can reduce the loss of the division long millis = Math.multiplyExact(instant.getEpochSecond() + 1, 1000_000L); @@ -511,7 +514,7 @@ public class DateTimeUtils { long millis = Math.multiplyExact(instant.getEpochSecond(), 1000_000L); return Math.addExact(millis, instant.getNano() / 1000); } - } else if ("ns".equals(timestampPrecision)) { + } else if ("ns".equals(timestampPrecision) || "nanosecond".equals(timestampPrecision)) { long millis = Math.multiplyExact(instant.getEpochSecond(), 1000_000_000L); return Math.addExact(millis, instant.getNano()); } @@ -629,9 +632,11 @@ public class DateTimeUtils { long res = value; switch (durationUnit) { case y: + case year: res *= 365 * 86_400_000L; break; case mo: + case month: if (currentTime == -1) { res *= 30 * 86_400_000L; } else { @@ -643,44 +648,55 @@ public class DateTimeUtils { } break; case w: + case week: res *= 7 * 86_400_000L; break; case d: + case day: res *= 86_400_000L; break; case h: + case hour: res *= 3_600_000L; break; case m: + case minute: res *= 60_000L; break; case s: + case second: res *= 1_000L; break; default: break; } - if ("us".equals(timestampPrecision)) { - if (unit.equals(DurationUnit.ns.toString())) { + if ("us".equals(timestampPrecision) || "microsecond".equals(timestampPrecision)) { + if (unit.equals(DurationUnit.ns.toString()) + || unit.equals(DurationUnit.nanosecond.toString())) { return value / 1000; - } else if (unit.equals(DurationUnit.us.toString())) { + } else if (unit.equals(DurationUnit.us.toString()) + || unit.equals(DurationUnit.microsecond.toString())) { return value; } else { return res * 1000; } - } else if ("ns".equals(timestampPrecision)) { - if (unit.equals(DurationUnit.ns.toString())) { + } else if ("ns".equals(timestampPrecision) || "nanosecond".equals(timestampPrecision)) { + if (unit.equals(DurationUnit.ns.toString()) + || unit.equals(DurationUnit.nanosecond.toString())) { return value; - } else if (unit.equals(DurationUnit.us.toString())) { + } else if (unit.equals(DurationUnit.us.toString()) + || unit.equals(DurationUnit.microsecond.toString())) { return value * 1000; } else { return res * 1000_000; } } else { - if (unit.equals(DurationUnit.ns.toString())) { + if (unit.equals(DurationUnit.ns.toString()) + || unit.equals(DurationUnit.nanosecond.toString())) { return value / 1000_000; - } else if (unit.equals(DurationUnit.us.toString())) { + } else if (unit.equals(DurationUnit.us.toString()) + || unit.equals(DurationUnit.microsecond.toString())) { return value / 1000; } else { return res; @@ -689,9 +705,9 @@ public class DateTimeUtils { } public static TimeUnit timestampPrecisionStringToTimeUnit(String timestampPrecision) { - if ("us".equals(timestampPrecision)) { + if ("us".equals(timestampPrecision) || "microsecond".equals(timestampPrecision)) { return TimeUnit.MICROSECONDS; - } else if ("ns".equals(timestampPrecision)) { + } else if ("ns".equals(timestampPrecision) || "nanosecond".equals(timestampPrecision)) { return TimeUnit.NANOSECONDS; } else { return TimeUnit.MILLISECONDS; @@ -706,9 +722,11 @@ public class DateTimeUtils { public static String convertLongToDate(long timestamp, String sourcePrecision) { switch (sourcePrecision) { case "ns": + case "nanosecond": timestamp /= 1000_000; break; case "us": + case "microsecond": timestamp /= 1000; break; } @@ -726,30 +744,46 @@ public class DateTimeUtils { public enum DurationUnit { y, + year, mo, + month, w, + week, d, + day, h, + hour, m, + minute, s, + second, ms, + millisecond, us, - ns + microsecond, + ns, + nanosecond } public static TimeUnit toTimeUnit(String t) { switch (t) { case "h": + case "hour": return TimeUnit.HOURS; case "m": + case "minute": return TimeUnit.MINUTES; case "s": + case "second": return TimeUnit.SECONDS; case "ms": + case "millisecond": return TimeUnit.MILLISECONDS; case "u": + case "microsecond": return TimeUnit.MICROSECONDS; case "n": + case "nanosecond": return TimeUnit.NANOSECONDS; default: throw new IllegalArgumentException("time precision must be one of: h,m,s,ms,u,n"); @@ -793,12 +827,12 @@ public class DateTimeUtils { i++; unit += duration.charAt(i); } - if (unit.equals("y")) { + if ("y".equals(unit) || "year".equals(unit)) { monthDuration += temp * 12; temp = 0; continue; } - if (unit.equals("mo")) { + if ("mo".equals(unit) || "month".equals(unit)) { monthDuration += temp; temp = 0; continue; 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 f59af819f8f..b36e7a784b2 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 @@ -484,7 +484,7 @@ public class AnalyzerTest { public static Analysis analyzeSQL(String sql, Metadata metadata) { try { SqlParser sqlParser = new SqlParser(); - Statement statement = sqlParser.createStatement(sql); + Statement statement = sqlParser.createStatement(sql, ZoneId.systemDefault()); SessionInfo session = new SessionInfo( 0, "test", ZoneId.systemDefault(), "testdb", IClientSession.SqlDialect.TABLE); diff --git a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 index 26cd32456e6..86244da3fe2 100644 --- a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 +++ b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 @@ -375,7 +375,7 @@ propertyValue queryNoWith : queryTerm (ORDER BY sortItem (',' sortItem)*)? - (FILL '(' (LINEAR | PREVIOUS | literalExpression) (',' duration=timeDuration)? ')')? + (FILL '(' (LINEAR | PREVIOUS | literalExpression) (',' duration=timeduration)? ')')? (OFFSET offset=rowCount)? (LIMIT limit=limitRowCount)? ; @@ -419,11 +419,11 @@ groupBy ; groupingElement - : TIME? '(' (timeRange ',')? windowInterval=timeDuration (',' windowStep=timeDuration)?')' #timenGrouping + : TIME '(' (timeRange ',')? windowInterval=timeduration (',' windowStep=timeduration)?')' #timenGrouping | VARIATION '(' expression (',' delta=number)? (',' propertyAssignments)? ')' #variationGrouping | CONDITION '(' expression (',' keepExpression)? (',' propertyAssignments)? ')' #conditionGrouping - | SESSION '(' timeInterval=timeDuration ')' #sessionGrouping - | COUNT '(' expression ',' countNumber=INTEGER_VALUE (',' propertyAssignments)? ')' #countGrouping + | SESSION '(' timeInterval=timeduration ')' #sessionGrouping + | COUNT '(' expression ',' countNumber=DECIMAL_INTEGER_LITERAL (',' propertyAssignments)? ')' #countGrouping | groupingSet #singleGroupingSet // the following three haven't been supported yet | ROLLUP '(' (groupingSet (',' groupingSet)*)? ')' #rollup @@ -438,11 +438,11 @@ timeRange timeValue : dateExpression - | (PLUS | MINUS)? INTEGER_VALUE + | (PLUS | MINUS)? DECIMAL_INTEGER_LITERAL ; dateExpression - : datetimeLiteral ((PLUS | MINUS) timeDuration)* + : datetimeLiteral ((PLUS | MINUS) timeduration)* ; datetimeLiteral @@ -451,7 +451,7 @@ datetimeLiteral ; keepExpression - : (KEEP (EQ | LT | LTE | GT | GTE))? INTEGER_VALUE + : (KEEP (EQ | LT | LTE | GT | GTE))? DECIMAL_INTEGER_LITERAL ; groupingSet @@ -610,8 +610,12 @@ intervalField : YEAR | MONTH | WEEK | DAY | HOUR | MINUTE | SECOND | MILLISECOND | MICROSECOND | NANOSECOND ; -timeDuration - : (INTEGER_VALUE+ (intervalField))+ +timeduration + : (DECIMAL_INTEGER_LITERAL intervalField)+ + ; + +DECIMAL_INTEGER_LITERAL + : DECIMAL_INTEGER ; type @@ -693,7 +697,6 @@ identifier | QUOTED_IDENTIFIER #quotedIdentifier | nonReserved #unquotedIdentifier | BACKQUOTED_IDENTIFIER #backQuotedIdentifier - | DIGIT_IDENTIFIER #digitIdentifier ; number @@ -1133,9 +1136,9 @@ IDENTIFIER : (LETTER | '_') (LETTER | DIGIT | '_')* ; -DIGIT_IDENTIFIER - : DIGIT (LETTER | DIGIT | '_')+ - ; +//DIGIT_IDENTIFIER +// : DIGIT (LETTER | DIGIT | '_')+ +// ; QUOTED_IDENTIFIER : '"' ( ~'"' | '""' )* '"' @@ -1205,3 +1208,30 @@ WS UNRECOGNIZED : . ; + +fragment A: [a]; +fragment B: [b]; +fragment C: [c]; +fragment D: [d]; +fragment E: [e]; +fragment F: [f]; +fragment G: [g]; +fragment H: [h]; +fragment I: [i]; +fragment J: [j]; +fragment K: [k]; +fragment L: [l]; +fragment M: [m]; +fragment N: [n]; +fragment O: [o]; +fragment P: [p]; +fragment Q: [q]; +fragment R: [r]; +fragment S: [s]; +fragment T: [t]; +fragment U: [u]; +fragment V: [v]; +fragment W: [w]; +fragment X: [x]; +fragment Y: [y]; +fragment Z: [z]; \ No newline at end of file
