This is an automated email from the ASF dual-hosted git repository.

jackietien pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new fe96a5573dd Perfect implicit time process in first, last, first_by, 
last_by of TableModel
fe96a5573dd is described below

commit fe96a5573dde2fccb895d04a039ab786d39e3f4d
Author: Weihao Li <[email protected]>
AuthorDate: Fri Oct 17 16:42:45 2025 +0800

    Perfect implicit time process in first, last, first_by, last_by of 
TableModel
---
 .../db/it/IoTDBMultiTAGsWithAttributesTableIT.java | 39 +++++++++++++++++++-
 .../plan/relational/sql/parser/AstBuilder.java     | 42 ++++++++++++++--------
 2 files changed, 66 insertions(+), 15 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiTAGsWithAttributesTableIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiTAGsWithAttributesTableIT.java
index 8dd8c8a343a..7b959696068 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiTAGsWithAttributesTableIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiTAGsWithAttributesTableIT.java
@@ -167,6 +167,12 @@ public class IoTDBMultiTAGsWithAttributesTableIT {
         "delete from table6 where time >= 1"
       };
 
+  private static final String[] sql7 =
+      new String[] {
+        "create table t1(device_id STRING TAG, code STRING TAG, s1 float)",
+        "create table t2(device_id STRING TAG, s2 float)"
+      };
+
   String[] expectedHeader;
   String[] retArray;
   static String sql;
@@ -191,7 +197,7 @@ public class IoTDBMultiTAGsWithAttributesTableIT {
   private static void insertData() {
     try (Connection connection = EnvFactory.getEnv().getTableConnection();
         Statement statement = connection.createStatement()) {
-      for (String[] sqlList : Arrays.asList(sql1, sql2, sql3, sql4, sql5, 
sql6)) {
+      for (String[] sqlList : Arrays.asList(sql1, sql2, sql3, sql4, sql5, 
sql6, sql7)) {
         for (String sql : sqlList) {
           statement.execute(sql);
         }
@@ -2881,6 +2887,37 @@ public class IoTDBMultiTAGsWithAttributesTableIT {
         DATABASE_NAME);
   }
 
+  @Test
+  public void implicitTimeTest() {
+    expectedHeader = new String[] {"code", "_col1", "device_id", "_col3"};
+    retArray = new String[] {};
+    tableResultSetEqualTest(
+        "select t1.code,date_bin(15m, t2.time),t2.device_id,last(t2.s2) from 
t1, t2 "
+            + "where t1.device_id = t2.device_id group by 
t1.code,date_bin(15m, t2.time),t2.device_id",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+
+    tableResultSetEqualTest(
+        "select t1.code,date_bin(15m, t2.time),t2.device_id,last(t2.s2, 
t2.time) from t1, t2 "
+            + "where t1.device_id = t2.device_id group by 
t1.code,date_bin(15m, t2.time),t2.device_id",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+
+    tableAssertTestFail(
+        "select t1.code,date_bin(15m, t2.time),t2.device_id,last(t2.s2, time) 
from t1, t2 "
+            + "where t1.device_id = t2.device_id group by 
t1.code,date_bin(15m, t2.time),t2.device_id",
+        "Column 'time' is ambiguous",
+        DATABASE_NAME);
+
+    expectedHeader = new String[] {"_col0"};
+    retArray = new String[] {"null,"};
+    tableResultSetEqualTest("select last(1) from t1", expectedHeader, 
retArray, DATABASE_NAME);
+
+    tableResultSetEqualTest("select last(1,time) from t1", expectedHeader, 
retArray, DATABASE_NAME);
+  }
+
   public static void repeatTest(
       String sql, String[] expectedHeader, String[] retArray, String dbName, 
int repeatTimes) {
     for (int i = 0; i < repeatTimes; i++) {
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 431ed9d3d02..76107bfa30e 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
@@ -3340,30 +3340,20 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
     if (name.toString().equalsIgnoreCase(FIRST_AGGREGATION)
         || name.toString().equalsIgnoreCase(LAST_AGGREGATION)) {
       if (arguments.size() == 1) {
-        arguments.add(
-            new Identifier(
-                
TimestampOperand.TIMESTAMP_EXPRESSION_STRING.toLowerCase(Locale.ENGLISH)));
+        appendTimeArgument(arguments);
       } else if (arguments.size() == 2) {
         check(
-            arguments
-                .get(1)
-                .toString()
-                
.equalsIgnoreCase(TimestampOperand.TIMESTAMP_EXPRESSION_STRING),
+            checkArgumentIsTime(arguments.get(1)),
             "The second argument of 'first' or 'last' function must be 'time'",
             ctx);
       }
     } else if (name.toString().equalsIgnoreCase(FIRST_BY_AGGREGATION)
         || name.toString().equalsIgnoreCase(LAST_BY_AGGREGATION)) {
       if (arguments.size() == 2) {
-        arguments.add(
-            new Identifier(
-                
TimestampOperand.TIMESTAMP_EXPRESSION_STRING.toLowerCase(Locale.ENGLISH)));
+        appendTimeArgument(arguments);
       } else if (arguments.size() == 3) {
         check(
-            arguments
-                .get(2)
-                .toString()
-                
.equalsIgnoreCase(TimestampOperand.TIMESTAMP_EXPRESSION_STRING),
+            checkArgumentIsTime(arguments.get(2)),
             "The third argument of 'first_by' or 'last_by' function must be 
'time'",
             ctx);
       }
@@ -3395,6 +3385,30 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
     return new FunctionCall(getLocation(ctx), name, window, nulls, distinct, 
mode, arguments);
   }
 
+  private void appendTimeArgument(List<Expression> arguments) {
+    if (arguments.get(0) instanceof DereferenceExpression) {
+      arguments.add(
+          new DereferenceExpression(
+              ((DereferenceExpression) arguments.get(0)).getBase(),
+              new Identifier(
+                  
TimestampOperand.TIMESTAMP_EXPRESSION_STRING.toLowerCase(Locale.ENGLISH))));
+    } else {
+      arguments.add(
+          new 
Identifier(TimestampOperand.TIMESTAMP_EXPRESSION_STRING.toLowerCase(Locale.ENGLISH)));
+    }
+  }
+
+  private boolean checkArgumentIsTime(Expression argument) {
+    if (argument instanceof DereferenceExpression) {
+      return ((DereferenceExpression) argument)
+          .getField()
+          .get()
+          .toString()
+          .equalsIgnoreCase(TimestampOperand.TIMESTAMP_EXPRESSION_STRING);
+    }
+    return 
argument.toString().equalsIgnoreCase(TimestampOperand.TIMESTAMP_EXPRESSION_STRING);
+  }
+
   @Override
   public Node visitDateBinGapFill(RelationalSqlParser.DateBinGapFillContext 
ctx) {
     TimeDuration timeDuration = 
DateTimeUtils.constructTimeDuration(ctx.timeDuration().getText());

Reply via email to