This is an automated email from the ASF dual-hosted git repository. haonan pushed a commit to branch rc/1.3.1 in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 6252a973c499d9ad8809f1e8f275a036181cecd4 Author: Jackie Tien <[email protected]> AuthorDate: Wed Feb 28 08:38:11 2024 +0800 [IOTDB-6300] Support place time column at any column index in insert statement --- .../db/it/IOTDBInsertWithTimeAtAnyIndexIT.java | 99 ++++++++++++++++++++ .../iotdb/db/it/IoTDBInsertWithoutTimeIT.java | 8 +- .../it/IoTDBSyntaxConventionStringLiteralIT.java | 8 +- .../aggregation/IoTDBCountTimeAlignedDeviceIT.java | 2 +- .../org/apache/iotdb/db/it/cq/IoTDBCQExecIT.java | 10 +- .../apache/iotdb/db/it/cq/IoTDBCQExecInNsIT.java | 10 +- .../apache/iotdb/db/it/cq/IoTDBCQExecInUsIT.java | 10 +- .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 | 18 ++-- .../db/queryengine/plan/parser/ASTVisitor.java | 104 ++++++++++++--------- 9 files changed, 195 insertions(+), 74 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IOTDBInsertWithTimeAtAnyIndexIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IOTDBInsertWithTimeAtAnyIndexIT.java new file mode 100644 index 00000000000..3db755bb547 --- /dev/null +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IOTDBInsertWithTimeAtAnyIndexIT.java @@ -0,0 +1,99 @@ +/* + * 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.it; + +import org.apache.iotdb.it.env.EnvFactory; +import org.apache.iotdb.it.framework.IoTDBTestRunner; +import org.apache.iotdb.itbase.category.ClusterIT; +import org.apache.iotdb.itbase.category.LocalStandaloneIT; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@RunWith(IoTDBTestRunner.class) +@Category({LocalStandaloneIT.class, ClusterIT.class}) +public class IOTDBInsertWithTimeAtAnyIndexIT { + + @BeforeClass + public static void setUp() throws Exception { + EnvFactory.getEnv().getConfig().getCommonConfig().setAutoCreateSchemaEnabled(true); + EnvFactory.getEnv().initClusterEnvironment(); + } + + @AfterClass + public static void tearDown() throws Exception { + EnvFactory.getEnv().cleanClusterEnvironment(); + } + + @Test + public void testInsertTimeAtAnyIndex() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + statement.addBatch("insert into root.db.d1(s1, s2, time) aligned values (2, 3, 1)"); + statement.addBatch("insert into root.db.d1(s1, time, s2) aligned values (20, 10, 30)"); + statement.addBatch("insert into root.db.d1(`time`, s1, s2) aligned values (100, 200, 300)"); + statement.executeBatch(); + + try (ResultSet resultSet = statement.executeQuery("select s1 from root.db.d1")) { + assertTrue(resultSet.next()); + assertEquals(1, resultSet.getLong(1)); + assertEquals(2, resultSet.getFloat(2), 0.00001); + assertTrue(resultSet.next()); + assertEquals(10, resultSet.getLong(1)); + assertEquals(20, resultSet.getFloat(2), 0.00001); + assertTrue(resultSet.next()); + assertEquals(100, resultSet.getLong(1)); + assertEquals(200, resultSet.getFloat(2), 0.00001); + assertFalse(resultSet.next()); + } + } + } + + @Test + public void testInsertMultiTime() { + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + try { + statement.addBatch( + "insert into root.db.d1(s1, s2, time, time) aligned values (2, 3, 1, 1)"); + statement.executeBatch(); + fail(); + } catch (SQLException e) { + // expected + } + + } catch (SQLException e) { + fail(); + } + } +} diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBInsertWithoutTimeIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBInsertWithoutTimeIT.java index d9d576ea20a..62d04de083a 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBInsertWithoutTimeIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBInsertWithoutTimeIT.java @@ -101,7 +101,8 @@ public class IoTDBInsertWithoutTimeIT { @Test public void testInsertWithoutValueColumns() { assertNonQueryTestFail( - "insert into root.sg1.d1(time) values (1)", "Error occurred while parsing SQL"); + "insert into root.sg1.d1(time) values (1)", + "InsertStatement should contain at least one measurement"); } @Test @@ -117,9 +118,10 @@ public class IoTDBInsertWithoutTimeIT { @Test public void testInsertWithMultiTimesColumns() { assertNonQueryTestFail( - "insert into root.sg1.d1(time, time) values (1, 1)", "Error occurred while parsing SQL"); + "insert into root.sg1.d1(time, time) values (1, 1)", + "One row should only have one time value"); assertNonQueryTestFail( "insert into root.sg1.d1(time, s1, time) values (1, 1, 1)", - "Error occurred while parsing SQL"); + "One row should only have one time value"); } } diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSyntaxConventionStringLiteralIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSyntaxConventionStringLiteralIT.java index ceadf96ab3e..27a88f44736 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSyntaxConventionStringLiteralIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSyntaxConventionStringLiteralIT.java @@ -179,7 +179,7 @@ public class IoTDBSyntaxConventionStringLiteralIT { String errorMsg = TSStatusCode.SQL_PARSE_ERROR.getStatusCode() + ": Error occurred while parsing SQL to physical plan: " - + "line 1:45 no viable alternative at input '(1, string'"; + + "line 1:45 mismatched input 'string' expecting {FALSE, NAN, NOW, NULL, TRUE, '-', '+', '/', '.', STRING_LITERAL, DATETIME_LITERAL, INTEGER_LITERAL, EXPONENT_NUM_PART}"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { statement.execute("CREATE TIMESERIES root.sg1.d1.s1 TEXT"); @@ -198,7 +198,7 @@ public class IoTDBSyntaxConventionStringLiteralIT { String errorMsg1 = TSStatusCode.SQL_PARSE_ERROR.getStatusCode() + ": Error occurred while parsing SQL to physical plan: " - + "line 1:45 no viable alternative at input '(1, `string`'"; + + "line 1:45 mismatched input '`string`' expecting {FALSE, NAN, NOW, NULL, TRUE, '-', '+', '/', '.', STRING_LITERAL, DATETIME_LITERAL, INTEGER_LITERAL, EXPONENT_NUM_PART}"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { // wrap STRING_LITERAL with `` @@ -211,7 +211,7 @@ public class IoTDBSyntaxConventionStringLiteralIT { String errorMsg2 = TSStatusCode.SQL_PARSE_ERROR.getStatusCode() + ": Error occurred while parsing SQL to physical plan: " - + "line 1:53 token recognition error at: '')'"; + + "line 1:47 extraneous input 'string' expecting {',', ')'}"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { // single ' in '' @@ -224,7 +224,7 @@ public class IoTDBSyntaxConventionStringLiteralIT { String errorMsg3 = TSStatusCode.SQL_PARSE_ERROR.getStatusCode() + ": Error occurred while parsing SQL to physical plan: " - + "line 1:53 token recognition error at: '\")'"; + + "line 1:47 extraneous input 'string' expecting {',', ')'}"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { // single " in "" diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBCountTimeAlignedDeviceIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBCountTimeAlignedDeviceIT.java index d1647cd3ef5..621a3867a26 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBCountTimeAlignedDeviceIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBCountTimeAlignedDeviceIT.java @@ -50,7 +50,7 @@ public class IoTDBCountTimeAlignedDeviceIT { "CREATE ALIGNED TIMESERIES root.aligned.downsampling.d2(s1 INT32, s2 INT32);", "INSERT INTO root.aligned.downsampling.d1(time, s1, s2) ALIGNED VALUES(0, 0, null), (1, null, 1), " + "(2, null, 2), (4,4,null), (5,5,5), (7,null,7), (8,8,8), (9,null,9);", - "INSERT INTO root.aligned.downsampling.d2(time, s1, s2) ALIGNED VALUES(0,null,0) (1, 1, null), (2,2,null), " + "INSERT INTO root.aligned.downsampling.d2(time, s1, s2) ALIGNED VALUES(0,null,0), (1, 1, null), (2,2,null), " + "(4,null,4), (5,5,5), (7,7,null), (8,8,8);", // test group by variation diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecIT.java index c83510beacf..ea00bbfa646 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecIT.java @@ -55,7 +55,7 @@ public class IoTDBCQExecIT { @Test public void testCQExecution1() { String insertTemplate = - "INSERT INTO root.sg.d1(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d1(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis(); @@ -142,7 +142,7 @@ public class IoTDBCQExecIT { @Test public void testCQExecution2() { String insertTemplate = - "INSERT INTO root.sg.d2(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d2(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis(); @@ -230,7 +230,7 @@ public class IoTDBCQExecIT { @Test public void testCQExecution3() { String insertTemplate = - "INSERT INTO root.sg.d3(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d3(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis(); @@ -329,7 +329,7 @@ public class IoTDBCQExecIT { @Test public void testCQExecution4() { String insertTemplate = - "INSERT INTO root.sg.d4(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d4(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis(); @@ -415,7 +415,7 @@ public class IoTDBCQExecIT { @Test public void testCQExecution5() { String insertTemplate = - "INSERT INTO root.sg.d5(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d5(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis(); diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecInNsIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecInNsIT.java index 453cd73cbd7..80acdb14087 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecInNsIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecInNsIT.java @@ -57,7 +57,7 @@ public class IoTDBCQExecInNsIT { @Test public void testCQExecution1() { String insertTemplate = - "INSERT INTO root.sg.d1(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d1(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis() * 1_000_000L; @@ -147,7 +147,7 @@ public class IoTDBCQExecInNsIT { @Test public void testCQExecution2() { String insertTemplate = - "INSERT INTO root.sg.d2(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d2(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis() * 1_000_000L; @@ -239,7 +239,7 @@ public class IoTDBCQExecInNsIT { @Test public void testCQExecution3() { String insertTemplate = - "INSERT INTO root.sg.d3(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d3(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis() * 1_000_000L; @@ -338,7 +338,7 @@ public class IoTDBCQExecInNsIT { @Test public void testCQExecution4() { String insertTemplate = - "INSERT INTO root.sg.d4(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d4(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis() * 1_000_000L; @@ -424,7 +424,7 @@ public class IoTDBCQExecInNsIT { @Test public void testCQExecution5() { String insertTemplate = - "INSERT INTO root.sg.d5(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d5(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis() * 1_000_000L; diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecInUsIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecInUsIT.java index 14a03e7e66f..c9971af3afb 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecInUsIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecInUsIT.java @@ -57,7 +57,7 @@ public class IoTDBCQExecInUsIT { @Test public void testCQExecution1() { String insertTemplate = - "INSERT INTO root.sg.d1(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d1(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis() * 1_000L; @@ -147,7 +147,7 @@ public class IoTDBCQExecInUsIT { @Test public void testCQExecution2() { String insertTemplate = - "INSERT INTO root.sg.d2(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d2(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis() * 1_000L; @@ -239,7 +239,7 @@ public class IoTDBCQExecInUsIT { @Test public void testCQExecution3() { String insertTemplate = - "INSERT INTO root.sg.d3(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d3(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis() * 1_000L; @@ -338,7 +338,7 @@ public class IoTDBCQExecInUsIT { @Test public void testCQExecution4() { String insertTemplate = - "INSERT INTO root.sg.d4(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d4(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis() * 1_000L; @@ -424,7 +424,7 @@ public class IoTDBCQExecInUsIT { @Test public void testCQExecution5() { String insertTemplate = - "INSERT INTO root.sg.d5(time, s1) VALUES (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d) (%d, %d)"; + "INSERT INTO root.sg.d5(time, s1) VALUES (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d), (%d, %d)"; try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { long now = System.currentTimeMillis() * 1_000L; diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 index 7c4ae5ebe0c..56568d653da 100644 --- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 +++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 @@ -787,21 +787,21 @@ insertStatement ; insertColumnsSpec - : LR_BRACKET (TIMESTAMP|TIME)? (COMMA? nodeNameWithoutWildcard)+ RR_BRACKET + : LR_BRACKET insertColumn (COMMA insertColumn)* RR_BRACKET ; -insertValuesSpec - : (COMMA? insertMultiValue)* +insertColumn + : identifier + | TIME + | TIMESTAMP ; -insertMultiValue - : LR_BRACKET timeValue (COMMA measurementValue)+ RR_BRACKET - | LR_BRACKET (measurementValue COMMA?)+ RR_BRACKET +insertValuesSpec + : row (COMMA row)* ; -measurementValue - : constant - | LR_BRACKET constant (COMMA constant)+ RR_BRACKET +row + : LR_BRACKET constant (COMMA constant)* RR_BRACKET ; // Delete Statement 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 fff5c53ef66..b3307e2ad2f 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 @@ -1800,78 +1800,98 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> { public Statement visitInsertStatement(IoTDBSqlParser.InsertStatementContext ctx) { InsertStatement insertStatement = new InsertStatement(); insertStatement.setDevice(parsePrefixPath(ctx.prefixPath())); - boolean isTimeDefault = parseInsertColumnSpec(ctx.insertColumnsSpec(), insertStatement); - parseInsertValuesSpec(ctx.insertValuesSpec(), insertStatement, isTimeDefault); + int timeIndex = parseInsertColumnSpec(ctx.insertColumnsSpec(), insertStatement); + parseInsertValuesSpec(ctx.insertValuesSpec(), insertStatement, timeIndex); insertStatement.setAligned(ctx.ALIGNED() != null); return insertStatement; } - private boolean parseInsertColumnSpec( + private int parseInsertColumnSpec( IoTDBSqlParser.InsertColumnsSpecContext ctx, InsertStatement insertStatement) { List<String> measurementList = new ArrayList<>(); - for (IoTDBSqlParser.NodeNameWithoutWildcardContext measurementName : - ctx.nodeNameWithoutWildcard()) { - measurementList.add(parseNodeNameWithoutWildCard(measurementName)); + int timeIndex = -1; + + for (int i = 0, size = ctx.insertColumn().size(); i < size; i++) { + String measurement = parseInsertColumn(ctx.insertColumn(i)); + if ("time".equalsIgnoreCase(measurement) || "timestamp".equalsIgnoreCase(measurement)) { + if (timeIndex != -1) { + throw new SemanticException("One row should only have one time value"); + } else { + timeIndex = i; + } + } else { + measurementList.add(measurement); + } + } + if (measurementList.isEmpty()) { + throw new SemanticException("InsertStatement should contain at least one measurement"); } insertStatement.setMeasurementList(measurementList.toArray(new String[0])); - return (ctx.TIME() == null && ctx.TIMESTAMP() == null); + return timeIndex; + } + + private String parseInsertColumn(IoTDBSqlParser.InsertColumnContext columnContext) { + return parseNodeString(columnContext.getText()); } private void parseInsertValuesSpec( - IoTDBSqlParser.InsertValuesSpecContext ctx, - InsertStatement insertStatement, - boolean isTimeDefault) { - List<IoTDBSqlParser.InsertMultiValueContext> insertMultiValues = ctx.insertMultiValue(); + IoTDBSqlParser.InsertValuesSpecContext ctx, InsertStatement insertStatement, int timeIndex) { + List<IoTDBSqlParser.RowContext> rows = ctx.row(); + if (timeIndex == -1 && rows.size() != 1) { + throw new SemanticException("need timestamps when insert multi rows"); + } List<String[]> valuesList = new ArrayList<>(); - long[] timeArray = new long[insertMultiValues.size()]; - for (int i = 0; i < insertMultiValues.size(); i++) { + long[] timeArray = new long[rows.size()]; + for (int i = 0, size = rows.size(); i < size; i++) { + IoTDBSqlParser.RowContext row = rows.get(i); // parse timestamp long timestamp; List<String> valueList = new ArrayList<>(); - - if (insertMultiValues.get(i).timeValue() != null) { - if (isTimeDefault) { - if (insertMultiValues.size() != 1) { - throw new SemanticException("need timestamps when insert multi rows"); - } - valueList.add(insertMultiValues.get(i).timeValue().getText()); - timestamp = CommonDateTimeUtils.currentTime(); - } else { - timestamp = - parseTimeValue( - insertMultiValues.get(i).timeValue(), CommonDateTimeUtils.currentTime()); - TimestampPrecisionUtils.checkTimestampPrecision(timestamp); - } + // using now() instead + if (timeIndex == -1) { + timestamp = CommonDateTimeUtils.currentTime(); } else { - if (!isTimeDefault) { - throw new SemanticException( - "the measurementList's size is not consistent with the valueList's size"); - } - if (insertMultiValues.size() != 1) { - throw new SemanticException("need timestamps when insert multi rows"); - } - timestamp = parseDateFormat(SqlConstant.NOW_FUNC); + timestamp = parseTimeValue(row.constant(timeIndex)); + TimestampPrecisionUtils.checkTimestampPrecision(timestamp); } timeArray[i] = timestamp; // parse values - List<IoTDBSqlParser.MeasurementValueContext> values = - insertMultiValues.get(i).measurementValue(); - for (IoTDBSqlParser.MeasurementValueContext value : values) { - for (IoTDBSqlParser.ConstantContext constant : value.constant()) { - if (constant.STRING_LITERAL() != null) { - valueList.add(parseStringLiteralInInsertValue(constant.getText())); + List<ConstantContext> values = row.constant(); + for (int j = 0, columnCount = values.size(); j < columnCount; j++) { + if (j != timeIndex) { + if (values.get(j).STRING_LITERAL() != null) { + valueList.add(parseStringLiteralInInsertValue(values.get(j).getText())); } else { - valueList.add(constant.getText()); + valueList.add(values.get(j).getText()); } } } + valuesList.add(valueList.toArray(new String[0])); } insertStatement.setTimes(timeArray); insertStatement.setValuesList(valuesList); } + private long parseTimeValue(ConstantContext constant) { + if (constant.INTEGER_LITERAL() != null) { + try { + if (constant.MINUS() != null) { + return -Long.parseLong(constant.INTEGER_LITERAL().getText()); + } + return Long.parseLong(constant.INTEGER_LITERAL().getText()); + } catch (NumberFormatException e) { + throw new SemanticException( + String.format("Can not parse %s to long value", constant.INTEGER_LITERAL().getText())); + } + } else if (constant.dateExpression() != null) { + return parseDateExpression(constant.dateExpression(), CommonDateTimeUtils.currentTime()); + } else { + throw new SemanticException(String.format("Can not parse %s to time", constant)); + } + } + // Load File @Override
