This is an automated email from the ASF dual-hosted git repository. jackietien pushed a commit to branch object_type in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 0a5f1ebbbb800e0e2b3e8b8a35e715b2ae09dc8d Author: Yongzao <[email protected]> AuthorDate: Sat Jul 12 18:35:19 2025 +0800 [AINode] Support model management for Table (#15923) (cherry picked from commit ba6eed15d29c4302f5e5981785f516cd359fdcdc) --- .../org/apache/iotdb/ainode/it/AINodeBasicIT.java | 268 ------------------- .../iotdb/ainode/it/AINodeClusterConfigIT.java | 76 ++++++ .../iotdb/ainode/it/AINodeInferenceSQLIT.java | 292 +++++++++++++++++++++ .../iotdb/ainode/it/AINodeModelManageIT.java | 215 +++++++++++++++ .../apache/iotdb/ainode/utils/AINodeTestUtils.java | 126 +++++++++ .../src/test/resources/ainode-example/config.yaml | 8 +- .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 | 2 +- .../iotdb/confignode/manager/ModelManager.java | 2 +- .../iotdb/db/queryengine/plan/Coordinator.java | 4 + .../queryengine/plan/analyze/AnalyzeVisitor.java | 8 +- .../execution/config/TableConfigTaskVisitor.java | 23 ++ .../execution/config/TreeConfigTaskVisitor.java | 14 +- .../config/executor/ClusterConfigTaskExecutor.java | 17 +- .../config/executor/IConfigTaskExecutor.java | 8 +- .../config/metadata/ai/CreateModelTask.java | 14 +- .../db/queryengine/plan/parser/ASTVisitor.java | 26 +- .../plan/relational/sql/ast/AstVisitor.java | 8 + .../plan/relational/sql/ast/CreateModel.java | 72 +++++ .../plan/relational/sql/ast/CreateTraining.java | 27 +- .../plan/relational/sql/ast/DropModel.java | 64 +++++ .../plan/relational/sql/parser/AstBuilder.java | 66 +++-- .../plan/statement/crud/QueryStatement.java | 10 +- .../metadata/model/CreateModelStatement.java | 10 +- .../metadata/model/DropModelStatement.java | 10 +- .../metadata/model/ShowModelsStatement.java | 12 +- .../db/relational/grammar/sql/RelationalSql.g4 | 8 +- 26 files changed, 1011 insertions(+), 379 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeBasicIT.java b/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeBasicIT.java deleted file mode 100644 index 84cb3576cee..00000000000 --- a/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeBasicIT.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * 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.ainode.it; - -import org.apache.iotdb.it.env.EnvFactory; - -import org.junit.Test; - -import java.io.File; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Statement; - -import static org.apache.iotdb.db.it.utils.TestUtils.prepareData; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -// @RunWith(IoTDBTestRunner.class) -// @Category({AIClusterIT.class}) -public class AINodeBasicIT { - static final String MODEL_PATH = - System.getProperty("user.dir") - + File.separator - + "src" - + File.separator - + "test" - + File.separator - + "resources" - + File.separator - + "ainode-example"; - - static String[] sqls = - new String[] { - "set configuration \"trusted_uri_pattern\"='.*'", - "create model identity using uri \"" + MODEL_PATH + "\"", - "CREATE DATABASE root.AI.data", - "CREATE TIMESERIES root.AI.data.s0 WITH DATATYPE=FLOAT, ENCODING=RLE", - "CREATE TIMESERIES root.AI.data.s1 WITH DATATYPE=FLOAT, ENCODING=RLE", - "CREATE TIMESERIES root.AI.data.s2 WITH DATATYPE=FLOAT, ENCODING=RLE", - "CREATE TIMESERIES root.AI.data.s3 WITH DATATYPE=DOUBLE, ENCODING=RLE", - "insert into root.AI.data(timestamp,s0,s1,s2,s3) values(1,1.0,2.0,3.0,4.0)", - "insert into root.AI.data(timestamp,s0,s1,s2,s3) values(2,2.0,3.0,4.0,5.0)", - "insert into root.AI.data(timestamp,s0,s1,s2,s3) values(3,3.0,4.0,5.0,6.0)", - "insert into root.AI.data(timestamp,s0,s1,s2,s3) values(4,4.0,5.0,6.0,7.0)", - "insert into root.AI.data(timestamp,s0,s1,s2,s3) values(5,5.0,6.0,7.0,8.0)", - "insert into root.AI.data(timestamp,s0,s1,s2,s3) values(6,6.0,7.0,8.0,9.0)", - "insert into root.AI.data(timestamp,s0,s1,s2,s3) values(7,7.0,8.0,9.0,10.0)", - }; - - // @BeforeClass - public static void setUp() throws Exception { - // Init 1C1D1M cluster environment - EnvFactory.getEnv().initClusterEnvironment(1, 1); - prepareData(sqls); - } - - // @AfterClass - public static void tearDown() throws Exception { - EnvFactory.getEnv().cleanClusterEnvironment(); - } - - private static void checkHeader(ResultSetMetaData resultSetMetaData, String title) - throws SQLException { - String[] headers = title.split(","); - for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) { - assertEquals(headers[i - 1], resultSetMetaData.getColumnName(i)); - } - } - - private void errorTest(String sql, String errorMessage) { - try (Connection connection = EnvFactory.getEnv().getConnection(); - Statement statement = connection.createStatement()) { - - try (ResultSet ignored = statement.executeQuery(sql)) { - fail("There should be an exception"); - } - } catch (SQLException e) { - assertEquals(errorMessage, e.getMessage()); - } - } - - @Test - public void aiNodeConnectionTest() { - String sql = "SHOW AINODES"; - String title = "NodeID,Status,InternalAddress,InternalPort"; - try (Connection connection = EnvFactory.getEnv().getConnection(); - Statement statement = connection.createStatement()) { - - try (ResultSet resultSet = statement.executeQuery(sql)) { - ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); - checkHeader(resultSetMetaData, title); - int count = 0; - while (resultSet.next()) { - assertEquals("2", resultSet.getString(1)); - assertEquals("Running", resultSet.getString(2)); - count++; - } - assertEquals(1, count); - } - } catch (SQLException e) { - e.printStackTrace(); - fail(e.getMessage()); - } - } - - @Test - public void ModelOperationTest() { - String registerSql = "create model operationTest using uri \"" + MODEL_PATH + "\""; - String showSql = "SHOW MODELS operationTest"; - String dropSql = "DROP MODEL operationTest"; - try (Connection connection = EnvFactory.getEnv().getConnection(); - Statement statement = connection.createStatement()) { - statement.execute(registerSql); - boolean loading = true; - int count = 0; - while (loading) { - try (ResultSet resultSet = statement.executeQuery(showSql)) { - ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); - checkHeader(resultSetMetaData, "ModelId,ModelType,State,Configs,Notes"); - while (resultSet.next()) { - String modelName = resultSet.getString(1); - String modelType = resultSet.getString(2); - String status = resultSet.getString(3); - - assertEquals("operationTest", modelName); - assertEquals("USER_DEFINED", modelType); - if (status.equals("ACTIVE")) { - loading = false; - count++; - } else if (status.equals("LOADING")) { - break; - } else { - fail("Unexpected status of model: " + status); - } - } - } - } - assertEquals(1, count); - statement.execute(dropSql); - try (ResultSet resultSet = statement.executeQuery(showSql)) { - ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); - checkHeader(resultSetMetaData, "ModelId,ModelType,State,Configs,Notes"); - count = 0; - while (resultSet.next()) { - count++; - } - assertEquals(0, count); - } - } catch (SQLException e) { - fail(e.getMessage()); - } - } - - @Test - public void callInferenceTest2() { - String sql = - "CALL INFERENCE(_holtwinters, \"select s0 from root.AI.data\", predict_length=6, generateTime=true)"; - try (Connection connection = EnvFactory.getEnv().getConnection(); - Statement statement = connection.createStatement()) { - try (ResultSet resultSet = statement.executeQuery(sql)) { - ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); - checkHeader(resultSetMetaData, "Time,output0"); - int count = 0; - while (resultSet.next()) { - count++; - } - assertEquals(6, count); - } - } catch (SQLException e) { - fail(e.getMessage()); - } - } - - @Test - public void callInferenceTest() { - String sql = - "CALL INFERENCE(identity, \"select s0,s1,s2 from root.AI.data\", generateTime=true)"; - String sql2 = "CALL INFERENCE(identity, \"select s2,s0,s1 from root.AI.data\")"; - String sql3 = - "CALL INFERENCE(_NaiveForecaster, \"select s0 from root.AI.data\", predict_length=3, generateTime=true)"; - try (Connection connection = EnvFactory.getEnv().getConnection(); - Statement statement = connection.createStatement()) { - - try (ResultSet resultSet = statement.executeQuery(sql)) { - ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); - checkHeader(resultSetMetaData, "Time,output0,output1,output2"); - int count = 0; - while (resultSet.next()) { - float s0 = resultSet.getFloat(2); - float s1 = resultSet.getFloat(3); - float s2 = resultSet.getFloat(4); - - assertEquals(s0, count + 1.0, 0.0001); - assertEquals(s1, count + 2.0, 0.0001); - assertEquals(s2, count + 3.0, 0.0001); - count++; - } - assertEquals(7, count); - } - - try (ResultSet resultSet = statement.executeQuery(sql2)) { - ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); - checkHeader(resultSetMetaData, "output0,output1,output2"); - int count = 0; - while (resultSet.next()) { - float s2 = resultSet.getFloat(1); - float s0 = resultSet.getFloat(2); - float s1 = resultSet.getFloat(3); - - assertEquals(s0, count + 1.0, 0.0001); - assertEquals(s1, count + 2.0, 0.0001); - assertEquals(s2, count + 3.0, 0.0001); - count++; - } - assertEquals(7, count); - } - - try (ResultSet resultSet = statement.executeQuery(sql3)) { - ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); - checkHeader(resultSetMetaData, "Time,output0,output1,output2"); - int count = 0; - while (resultSet.next()) { - count++; - } - assertEquals(3, count); - } - - } catch (SQLException e) { - fail(e.getMessage()); - } - } - - @Test - public void errorTest() { - String sql = - "CALL INFERENCE(notFound404, \"select s0,s1,s2 from root.AI.data\", window=head(5))"; - errorTest(sql, "1505: model [notFound404] has not been created."); - sql = "CALL INFERENCE(identity, \"select s0,s1,s2 from root.AI.data\", window=head(2))"; - errorTest(sql, "701: Window output 2 is not equal to input size of model 7"); - sql = "CALL INFERENCE(identity, \"select s0,s1,s2 from root.AI.data limit 5\")"; - errorTest( - sql, - "301: The number of rows 5 in the input data does not match the model input 7. Try to use LIMIT in SQL or WINDOW in CALL INFERENCE"); - sql = "CREATE MODEL 中文 USING URI \"" + MODEL_PATH + "\""; - errorTest(sql, "701: ModelName can only contain letters, numbers, and underscores"); - sql = "DROP MODEL _GaussianHMM"; - errorTest(sql, "1502: Built-in model _GaussianHMM can't be removed"); - } -} diff --git a/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeClusterConfigIT.java b/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeClusterConfigIT.java new file mode 100644 index 00000000000..89bd887b739 --- /dev/null +++ b/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeClusterConfigIT.java @@ -0,0 +1,76 @@ +/* + * 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.ainode.it; + +import org.apache.iotdb.it.env.EnvFactory; +import org.apache.iotdb.it.framework.IoTDBTestRunner; +import org.apache.iotdb.itbase.category.AIClusterIT; + +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.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.apache.iotdb.ainode.utils.AINodeTestUtils.checkHeader; +import static org.junit.Assert.assertEquals; + +@RunWith(IoTDBTestRunner.class) +@Category({AIClusterIT.class}) +public class AINodeClusterConfigIT { + + @BeforeClass + public static void setUp() throws Exception { + // Init 1C1D1A cluster environment + EnvFactory.getEnv().initClusterEnvironment(1, 1); + } + + @AfterClass + public static void tearDown() throws Exception { + EnvFactory.getEnv().cleanClusterEnvironment(); + } + + @Test + public void aiNodeRegisterTest() throws SQLException { + String sql = "SHOW AINODES"; + String title = "NodeID,Status,InternalAddress,InternalPort"; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + + try (ResultSet resultSet = statement.executeQuery(sql)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, title); + int count = 0; + while (resultSet.next()) { + assertEquals("2", resultSet.getString(1)); + assertEquals("Running", resultSet.getString(2)); + count++; + } + assertEquals(1, count); + } + } + } +} diff --git a/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeInferenceSQLIT.java b/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeInferenceSQLIT.java new file mode 100644 index 00000000000..bda34e6ad26 --- /dev/null +++ b/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeInferenceSQLIT.java @@ -0,0 +1,292 @@ +/* + * 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.ainode.it; + +import org.apache.iotdb.it.env.EnvFactory; +import org.apache.iotdb.it.framework.IoTDBTestRunner; +import org.apache.iotdb.itbase.category.AIClusterIT; +import org.apache.iotdb.itbase.env.BaseEnv; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.apache.iotdb.ainode.utils.AINodeTestUtils.EXAMPLE_MODEL_PATH; +import static org.apache.iotdb.ainode.utils.AINodeTestUtils.checkHeader; +import static org.apache.iotdb.ainode.utils.AINodeTestUtils.errorTest; +import static org.apache.iotdb.db.it.utils.TestUtils.prepareData; +import static org.apache.iotdb.db.it.utils.TestUtils.prepareTableData; +import static org.junit.Assert.assertEquals; + +@RunWith(IoTDBTestRunner.class) +@Category({AIClusterIT.class}) +public class AINodeInferenceSQLIT { + + static String[] WRITE_SQL_IN_TREE = + new String[] { + "set configuration \"trusted_uri_pattern\"='.*'", + "create model identity using uri \"" + EXAMPLE_MODEL_PATH + "\"", + "CREATE DATABASE root.AI", + "CREATE TIMESERIES root.AI.s0 WITH DATATYPE=FLOAT, ENCODING=RLE", + "CREATE TIMESERIES root.AI.s1 WITH DATATYPE=DOUBLE, ENCODING=RLE", + "CREATE TIMESERIES root.AI.s2 WITH DATATYPE=INT32, ENCODING=RLE", + "CREATE TIMESERIES root.AI.s3 WITH DATATYPE=INT64, ENCODING=RLE", + "insert into root.AI(timestamp,s0,s1,s2,s3) values(1,1.0,2.0,3,4)", + "insert into root.AI(timestamp,s0,s1,s2,s3) values(2,2.0,3.0,4,5)", + "insert into root.AI(timestamp,s0,s1,s2,s3) values(3,3.0,4.0,5,6)", + "insert into root.AI(timestamp,s0,s1,s2,s3) values(4,4.0,5.0,6,7)", + "insert into root.AI(timestamp,s0,s1,s2,s3) values(5,5.0,6.0,7,8)", + "insert into root.AI(timestamp,s0,s1,s2,s3) values(6,6.0,7.0,8,9)", + "insert into root.AI(timestamp,s0,s1,s2,s3) values(7,7.0,8.0,9,10)", + }; + + static String[] WRITE_SQL_IN_TABLE = + new String[] { + "CREATE DATABASE root", + "CREATE TABLE root.AI (s0 FLOAT FIELD, s1 DOUBLE FIELD, s2 INT32 FIELD, s3 INT64 FIELD)", + "insert into root.AI(time,s0,s1,s2,s3) values(1,1.0,2.0,3,4)", + "insert into root.AI(time,s0,s1,s2,s3) values(2,2.0,3.0,4,5)", + "insert into root.AI(time,s0,s1,s2,s3) values(3,3.0,4.0,5,6)", + "insert into root.AI(time,s0,s1,s2,s3) values(4,4.0,5.0,6,7)", + "insert into root.AI(time,s0,s1,s2,s3) values(5,5.0,6.0,7,8)", + "insert into root.AI(time,s0,s1,s2,s3) values(6,6.0,7.0,8,9)", + "insert into root.AI(time,s0,s1,s2,s3) values(7,7.0,8.0,9,10)", + }; + + @BeforeClass + public static void setUp() throws Exception { + // Init 1C1D1M cluster environment + EnvFactory.getEnv().initClusterEnvironment(1, 1); + prepareData(WRITE_SQL_IN_TREE); + prepareTableData(WRITE_SQL_IN_TABLE); + } + + @AfterClass + public static void tearDown() throws Exception { + EnvFactory.getEnv().cleanClusterEnvironment(); + } + + // TODO: We need reconsider call inference before enable this IT + // @Test + public void callInferenceTestInTree() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TREE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + callInferenceTest(statement); + } + } + + // TODO: Enable this test after the call inference is supported by the table model + // @Test + public void callInferenceTestInTable() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + callInferenceTest(statement); + } + } + + public void callInferenceTest(Statement statement) throws SQLException { + // SQL1: user-defined model inferences multi-columns with generateTime=true + String sql1 = + "CALL INFERENCE(identity, \"select s0,s1,s2,s3 from root.AI\", generateTime=true)"; + // SQL2: user-defined model inferences multi-columns with generateTime=false + String sql2 = + "CALL INFERENCE(identity, \"select s2,s0,s3,s1 from root.AI\", generateTime=false)"; + // SQL3: built-in model inferences single column with given predict_length and multi-outputs + String sql3 = + "CALL INFERENCE(naive_forecaster, \"select s0 from root.AI\", predict_length=3, generateTime=true)"; + // SQL4: built-in model inferences single column with given predict_length + String sql4 = + "CALL INFERENCE(holtwinters, \"select s0 from root.AI\", predict_length=6, generateTime=true)"; + + try (ResultSet resultSet = statement.executeQuery(sql1)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, "Time,output0,output1,output2,output3"); + int count = 0; + while (resultSet.next()) { + float s0 = resultSet.getFloat(2); + float s1 = resultSet.getFloat(3); + float s2 = resultSet.getFloat(4); + float s3 = resultSet.getFloat(5); + + assertEquals(s0, count + 1.0, 0.0001); + assertEquals(s1, count + 2.0, 0.0001); + assertEquals(s2, count + 3.0, 0.0001); + assertEquals(s3, count + 4.0, 0.0001); + count++; + } + assertEquals(7, count); + } + + try (ResultSet resultSet = statement.executeQuery(sql2)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, "output0,output1,output2"); + int count = 0; + while (resultSet.next()) { + float s2 = resultSet.getFloat(1); + float s0 = resultSet.getFloat(2); + float s3 = resultSet.getFloat(3); + float s1 = resultSet.getFloat(4); + + assertEquals(s0, count + 1.0, 0.0001); + assertEquals(s1, count + 2.0, 0.0001); + assertEquals(s2, count + 3.0, 0.0001); + assertEquals(s3, count + 4.0, 0.0001); + count++; + } + assertEquals(7, count); + } + + try (ResultSet resultSet = statement.executeQuery(sql3)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, "Time,output0,output1,output2"); + int count = 0; + while (resultSet.next()) { + count++; + } + assertEquals(3, count); + } + + try (ResultSet resultSet = statement.executeQuery(sql4)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, "Time,output0"); + int count = 0; + while (resultSet.next()) { + count++; + } + assertEquals(6, count); + } + } + + // TODO: We need reconsider call inference before enable this IT + // @Test + public void errorCallInferenceTestInTree() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TREE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + errorCallInferenceTest(statement); + } + } + + // TODO: Enable this test after the call inference is supported by the table model + // @Test + public void errorCallInferenceTestInTable() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + errorCallInferenceTest(statement); + } + } + + public void errorCallInferenceTest(Statement statement) { + String sql = "CALL INFERENCE(notFound404, \"select s0,s1,s2 from root.AI\", window=head(5))"; + errorTest(statement, sql, "1505: model [notFound404] has not been created."); + sql = "CALL INFERENCE(identity, \"select s0,s1,s2,s3 from root.AI\", window=head(2))"; + errorTest(statement, sql, "701: Window output 2 is not equal to input size of model 7"); + sql = "CALL INFERENCE(identity, \"select s0,s1,s2,s3 from root.AI limit 5\")"; + errorTest( + statement, + sql, + "301: The number of rows 5 in the input data does not match the model input 7. Try to use LIMIT in SQL or WINDOW in CALL INFERENCE"); + sql = "CREATE MODEL 中文 USING URI \"" + EXAMPLE_MODEL_PATH + "\""; + errorTest(statement, sql, "701: ModelId can only contain letters, numbers, and underscores"); + } + + // TODO: Our function is too bad currently + // @Test + public void selectForecastTestInTable() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + // SQL1: user-defined model inferences multi-columns with generateTime=true + String sql1 = + "SELECT * FROM FORECAST(model_id=>'identity', input=>(SELECT time,s0,s1,s2,s3 FROM root.AI) ORDER BY time, output_length=>7)"; + // SQL2: user-defined model inferences multi-columns with generateTime=false + String sql2 = + "SELECT * FROM FORECAST(model_id=>'identity', input=>(SELECT time,s2,s0,s3,s1 FROM root.AI) ORDER BY time, output_length=>7)"; + // SQL3: built-in model inferences single column with given predict_length and multi-outputs + String sql3 = + "SELECT * FROM FORECAST(model_id=>'naive_forecaster', input=>(SELECT time,s0 FROM root.AI) ORDER BY time, output_length=>3)"; + // SQL4: built-in model inferences single column with given predict_length + String sql4 = + "SELECT * FROM FORECAST(model_id=>'holtwinters', input=>(SELECT time,s0 FROM root.AI) ORDER BY time, output_length=>6)"; + try (ResultSet resultSet = statement.executeQuery(sql1)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, "time,s0,s1,s2,s3"); + int count = 0; + while (resultSet.next()) { + float s0 = resultSet.getFloat(2); + float s1 = resultSet.getFloat(3); + float s2 = resultSet.getFloat(4); + float s3 = resultSet.getFloat(5); + + assertEquals(s0, count + 1.0, 0.0001); + assertEquals(s1, count + 2.0, 0.0001); + assertEquals(s2, count + 3.0, 0.0001); + assertEquals(s3, count + 4.0, 0.0001); + count++; + } + assertEquals(7, count); + } + + try (ResultSet resultSet = statement.executeQuery(sql2)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, "time,s2,s0,s3,s1"); + int count = 0; + while (resultSet.next()) { + float s2 = resultSet.getFloat(1); + float s0 = resultSet.getFloat(2); + float s3 = resultSet.getFloat(3); + float s1 = resultSet.getFloat(4); + + assertEquals(s0, count + 1.0, 0.0001); + assertEquals(s1, count + 2.0, 0.0001); + assertEquals(s2, count + 3.0, 0.0001); + assertEquals(s3, count + 4.0, 0.0001); + count++; + } + assertEquals(7, count); + } + + try (ResultSet resultSet = statement.executeQuery(sql3)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, "time,output0,output1,output2"); + int count = 0; + while (resultSet.next()) { + count++; + } + assertEquals(3, count); + } + + try (ResultSet resultSet = statement.executeQuery(sql4)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, "time,output0"); + int count = 0; + while (resultSet.next()) { + count++; + } + assertEquals(6, count); + } + } + } +} diff --git a/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeModelManageIT.java b/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeModelManageIT.java new file mode 100644 index 00000000000..f7220f20cbe --- /dev/null +++ b/integration-test/src/test/java/org/apache/iotdb/ainode/it/AINodeModelManageIT.java @@ -0,0 +1,215 @@ +/* + * 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.ainode.it; + +import org.apache.iotdb.ainode.utils.AINodeTestUtils.FakeModelInfo; +import org.apache.iotdb.it.env.EnvFactory; +import org.apache.iotdb.it.framework.IoTDBTestRunner; +import org.apache.iotdb.itbase.category.AIClusterIT; +import org.apache.iotdb.itbase.env.BaseEnv; + +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.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.AbstractMap; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.apache.iotdb.ainode.utils.AINodeTestUtils.EXAMPLE_MODEL_PATH; +import static org.apache.iotdb.ainode.utils.AINodeTestUtils.checkHeader; +import static org.apache.iotdb.ainode.utils.AINodeTestUtils.errorTest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@RunWith(IoTDBTestRunner.class) +@Category({AIClusterIT.class}) +public class AINodeModelManageIT { + + private static final Map<String, FakeModelInfo> BUILT_IN_MACHINE_LEARNING_MODEL_MAP = + Stream.of( + new AbstractMap.SimpleEntry<>( + "arima", new FakeModelInfo("arima", "Arima", "BUILT-IN", "ACTIVE")), + new AbstractMap.SimpleEntry<>( + "holtwinters", + new FakeModelInfo("holtwinters", "HoltWinters", "BUILT-IN", "ACTIVE")), + new AbstractMap.SimpleEntry<>( + "exponential_smoothing", + new FakeModelInfo( + "exponential_smoothing", "ExponentialSmoothing", "BUILT-IN", "ACTIVE")), + new AbstractMap.SimpleEntry<>( + "naive_forecaster", + new FakeModelInfo("naive_forecaster", "NaiveForecaster", "BUILT-IN", "ACTIVE")), + new AbstractMap.SimpleEntry<>( + "stl_forecaster", + new FakeModelInfo("stl_forecaster", "StlForecaster", "BUILT-IN", "ACTIVE")), + new AbstractMap.SimpleEntry<>( + "gaussian_hmm", + new FakeModelInfo("gaussian_hmm", "GaussianHmm", "BUILT-IN", "ACTIVE")), + new AbstractMap.SimpleEntry<>( + "gmm_hmm", new FakeModelInfo("gmm_hmm", "GmmHmm", "BUILT-IN", "ACTIVE")), + new AbstractMap.SimpleEntry<>( + "stray", new FakeModelInfo("stray", "Stray", "BUILT-IN", "ACTIVE"))) + // new AbstractMap.SimpleEntry<>( + // "sundial", new FakeModelInfo("sundial", "Timer-Sundial", "BUILT-IN", + // "ACTIVE")), + // new AbstractMap.SimpleEntry<>( + // "timer_xl", new FakeModelInfo("timer_xl", "Timer-XL", "BUILT-IN", + // "ACTIVE"))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + @BeforeClass + public static void setUp() throws Exception { + // Init 1C1D1A cluster environment + EnvFactory.getEnv().initClusterEnvironment(1, 1); + } + + @AfterClass + public static void tearDown() throws Exception { + EnvFactory.getEnv().cleanClusterEnvironment(); + } + + @Test + public void userDefinedModelManagementTestInTree() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TREE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + userDefinedModelManagementTest(statement); + } + } + + @Test + public void userDefinedModelManagementTestInTable() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + userDefinedModelManagementTest(statement); + } + } + + private void userDefinedModelManagementTest(Statement statement) throws SQLException { + final String alterConfigSQL = "set configuration \"trusted_uri_pattern\"='.*'"; + final String registerSql = + "create model operationTest using uri \"" + EXAMPLE_MODEL_PATH + "\""; + final String showSql = "SHOW MODELS operationTest"; + final String dropSql = "DROP MODEL operationTest"; + + statement.execute(alterConfigSQL); + statement.execute(registerSql); + boolean loading = true; + int count = 0; + while (loading) { + try (ResultSet resultSet = statement.executeQuery(showSql)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, "ModelId,ModelType,Category,State"); + while (resultSet.next()) { + String modelId = resultSet.getString(1); + String category = resultSet.getString(3); + String state = resultSet.getString(4); + + assertEquals("operationTest", modelId); + assertEquals("USER-DEFINED", category); + if (state.equals("ACTIVE")) { + loading = false; + count++; + } else if (state.equals("LOADING")) { + break; + } else { + fail("Unexpected status of model: " + state); + } + } + } + } + assertEquals(1, count); + statement.execute(dropSql); + try (ResultSet resultSet = statement.executeQuery(showSql)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, "ModelId,ModelType,Category,State"); + count = 0; + while (resultSet.next()) { + count++; + } + assertEquals(0, count); + } + } + + @Test + public void dropBuiltInModelErrorTestInTree() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TREE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + errorTest(statement, "drop model sundial", "1501: Built-in model sundial can't be removed"); + } + } + + @Test + public void dropBuiltInModelErrorTestInTable() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + errorTest(statement, "drop model sundial", "1501: Built-in model sundial can't be removed"); + } + } + + @Test + public void showBuiltInModelTestInTree() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TREE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + showBuiltInModelTest(statement); + } + } + + @Test + public void showBuiltInModelTestInTable() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement statement = connection.createStatement(); ) { + showBuiltInModelTest(statement); + } + } + + private void showBuiltInModelTest(Statement statement) throws SQLException { + int built_in_model_count = 0; + final String showSql = "SHOW MODELS"; + try (ResultSet resultSet = statement.executeQuery(showSql)) { + ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); + checkHeader(resultSetMetaData, "ModelId,ModelType,Category,State"); + while (resultSet.next()) { + String modelId = resultSet.getString(1); + if (!modelId.equals("sundial") && !modelId.equals("timer_xl")) { + built_in_model_count++; + FakeModelInfo modelInfo = + new FakeModelInfo( + resultSet.getString(1), + resultSet.getString(2), + resultSet.getString(3), + resultSet.getString(4)); + assertTrue(BUILT_IN_MACHINE_LEARNING_MODEL_MAP.containsKey(modelInfo.getModelId())); + assertEquals(BUILT_IN_MACHINE_LEARNING_MODEL_MAP.get(modelInfo.getModelId()), modelInfo); + } + } + } + assertEquals(BUILT_IN_MACHINE_LEARNING_MODEL_MAP.size(), built_in_model_count); + } +} diff --git a/integration-test/src/test/java/org/apache/iotdb/ainode/utils/AINodeTestUtils.java b/integration-test/src/test/java/org/apache/iotdb/ainode/utils/AINodeTestUtils.java new file mode 100644 index 00000000000..e9464827c79 --- /dev/null +++ b/integration-test/src/test/java/org/apache/iotdb/ainode/utils/AINodeTestUtils.java @@ -0,0 +1,126 @@ +/* + * 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.ainode.utils; + +import java.io.File; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Objects; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class AINodeTestUtils { + + public static final String EXAMPLE_MODEL_PATH = + System.getProperty("user.dir") + + File.separator + + "src" + + File.separator + + "test" + + File.separator + + "resources" + + File.separator + + "ainode-example"; + + public static void checkHeader(ResultSetMetaData resultSetMetaData, String title) + throws SQLException { + String[] headers = title.split(","); + for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) { + assertEquals(headers[i - 1], resultSetMetaData.getColumnName(i)); + } + } + + public static void errorTest(Statement statement, String sql, String errorMessage) { + try (ResultSet ignored = statement.executeQuery(sql)) { + fail("There should be an exception"); + } catch (SQLException e) { + assertEquals(errorMessage, e.getMessage()); + } + } + + public static class FakeModelInfo { + + private final String modelId; + private final String modelType; + private final String category; + private final String state; + + public FakeModelInfo(String modelId, String modelType, String category, String state) { + this.modelId = modelId; + this.modelType = modelType; + this.category = category; + this.state = state; + } + + public String getModelId() { + return modelId; + } + + public String getModelType() { + return modelType; + } + + public String getCategory() { + return category; + } + + public String getState() { + return state; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + FakeModelInfo modelInfo = (FakeModelInfo) o; + return Objects.equals(modelId, modelInfo.modelId) + && Objects.equals(modelType, modelInfo.modelType) + && Objects.equals(category, modelInfo.category) + && Objects.equals(state, modelInfo.state); + } + + @Override + public int hashCode() { + return Objects.hash(modelId, modelType, category, state); + } + + @Override + public String toString() { + return "FakeModelInfo{" + + "modelId='" + + modelId + + '\'' + + ", modelType='" + + modelType + + '\'' + + ", category='" + + category + + '\'' + + ", state='" + + state + + '\'' + + '}'; + } + } +} diff --git a/integration-test/src/test/resources/ainode-example/config.yaml b/integration-test/src/test/resources/ainode-example/config.yaml index 996e572cf39..665acb8704e 100644 --- a/integration-test/src/test/resources/ainode-example/config.yaml +++ b/integration-test/src/test/resources/ainode-example/config.yaml @@ -1,5 +1,5 @@ configs: - input_shape: [7, 3] - output_shape: [7, 3] - input_type: ["float32", "float32","float32"] - output_type: ["float32", "float32","float32"] + input_shape: [7, 4] + output_shape: [7, 4] + input_type: ["float32", "float32", "float32", "float32"] + output_type: ["float32", "float32", "float32", "float32"] 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 01c7de75ede..a30619f6762 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 @@ -698,7 +698,7 @@ dropSubscription // AI Model ========================================================================================= // ---- Create Model createModel - : CREATE MODEL modelName=identifier uriClause + : CREATE MODEL modelId=identifier uriClause | CREATE MODEL modelId=identifier (WITH HYPERPARAMETERS LR_BRACKET hparamPair (COMMA hparamPair)* RR_BRACKET)? FROM MODEL existingModelId=identifier ON DATASET LR_BRACKET trainingData RR_BRACKET ; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ModelManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ModelManager.java index ad1a6bed4ed..e9d9ff97510 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ModelManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ModelManager.java @@ -82,7 +82,7 @@ public class ModelManager { public TSStatus dropModel(TDropModelReq req) { if (modelInfo.checkModelType(req.getModelId()) != ModelType.USER_DEFINED) { - return new TSStatus(TSStatusCode.MODEL_EXIST_ERROR.getStatusCode()) + return new TSStatus(TSStatusCode.DROP_MODEL_ERROR.getStatusCode()) .setMessage(String.format("Built-in model %s can't be removed", req.modelId)); } if (!modelInfo.contain(req.modelId)) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java index 8e22af456f6..0c7af068d4b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java @@ -63,6 +63,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ClearCache; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateModel; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTable; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTraining; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DeleteDevice; @@ -70,6 +71,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeTable; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropFunction; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropModel; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropTable; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ExtendRegion; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Flush; @@ -454,8 +456,10 @@ public class Coordinator { || statement instanceof MigrateRegion || statement instanceof ReconstructRegion || statement instanceof ExtendRegion + || statement instanceof CreateModel || statement instanceof CreateTraining || statement instanceof ShowModels + || statement instanceof DropModel || statement instanceof RemoveRegion) { return new ConfigExecution( queryContext, diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java index 434419bf596..6009b07011e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java @@ -428,7 +428,7 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> } // Get model metadata from configNode and do some check - String modelId = queryStatement.getModelName(); + String modelId = queryStatement.getModelId(); TSStatus status = modelFetcher.fetchModel(modelId, analysis); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { throw new GetModelInfoException(status.getMessage()); @@ -1813,6 +1813,12 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> for (int i = 0; i < inputType.length; i++) { Expression inputExpression = outputExpressions.get(i).left; TSDataType inputDataType = analysis.getType(inputExpression); + boolean isExpressionNumeric = inputDataType.isNumeric(); + boolean isModelNumeric = inputType[i].isNumeric(); + if (isExpressionNumeric && isModelNumeric) { + // every model supports numeric by default + continue; + } if (inputDataType != inputType[i]) { throw new SemanticException( String.format( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java index b3b10606d3e..a9282a88b43 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java @@ -52,7 +52,9 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ShowFuncti import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ShowPipePluginsTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ShowRegionTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ShowVariablesTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ai.CreateModelTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ai.CreateTrainingTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ai.DropModelTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ai.ShowModelsTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.ExtendRegionTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.MigrateRegionTask; @@ -123,6 +125,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ClearCache; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ColumnDefinition; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateModel; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipePlugin; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTable; @@ -136,6 +139,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeTable; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropFunction; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropModel; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropPipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropPipePlugin; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropSubscription; @@ -1362,8 +1366,27 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont node.getModelId(), node.getParameters(), node.getExistingModelId(), node.getTargetSql()); } + @Override + protected IConfigTask visitCreateModel(CreateModel node, MPPQueryContext context) { + context.setQueryType(QueryType.WRITE); + String uri = node.getUri(); + if (uri != null && ExecutableManager.isUriTrusted(uri)) { + // user specified uri and that uri is trusted + return new CreateModelTask(node.getModelId(), uri); + } + // user specified uri and that uri is not trusted + throw new SemanticException(getUnTrustedUriErrorMsg(uri)); + } + @Override protected IConfigTask visitShowModels(ShowModels node, MPPQueryContext context) { + context.setQueryType(QueryType.READ); return new ShowModelsTask(node.getModelId()); } + + @Override + protected IConfigTask visitDropModel(DropModel node, MPPQueryContext context) { + context.setQueryType(QueryType.WRITE); + return new DropModelTask(node.getModelId()); + } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java index 6bd1d0392d5..3aa4217cd8e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java @@ -765,26 +765,26 @@ public class TreeConfigTaskVisitor extends StatementVisitor<IConfigTask, MPPQuer @Override public IConfigTask visitCreateModel( CreateModelStatement createModelStatement, MPPQueryContext context) { - if (createModelStatement.getUri() != null && isUriTrusted(createModelStatement.getUri())) { - // 1. user specified uri and that uri is trusted - // 2. user doesn't specify uri - return new CreateModelTask(createModelStatement, context); + String uri = createModelStatement.getUri(); + if (uri != null && isUriTrusted(uri)) { + // user specified uri and that uri is trusted + return new CreateModelTask(createModelStatement.getModelId(), uri); } else { // user specified uri and that uri is not trusted - throw new SemanticException(getUnTrustedUriErrorMsg(createModelStatement.getUri())); + throw new SemanticException(getUnTrustedUriErrorMsg(uri)); } } @Override public IConfigTask visitDropModel( DropModelStatement dropModelStatement, MPPQueryContext context) { - return new DropModelTask(dropModelStatement.getModelName()); + return new DropModelTask(dropModelStatement.getModelId()); } @Override public IConfigTask visitShowModels( ShowModelsStatement showModelsStatement, MPPQueryContext context) { - return new ShowModelsTask(showModelsStatement.getModelName()); + return new ShowModelsTask(showModelsStatement.getModelId()); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java index 86e42530e11..e3c82de71ea 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java @@ -250,7 +250,6 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowClusterStatem import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowDatabaseStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowRegionStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowTTLStatement; -import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.CreateModelStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.AlterPipeStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.CreatePipePluginStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.CreatePipeStatement; @@ -3300,13 +3299,11 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor { } @Override - public SettableFuture<ConfigTaskResult> createModel( - final CreateModelStatement createModelStatement, final MPPQueryContext context) { + public SettableFuture<ConfigTaskResult> createModel(String modelId, String uri) { final SettableFuture<ConfigTaskResult> future = SettableFuture.create(); try (final ConfigNodeClient client = CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { - final TCreateModelReq req = - new TCreateModelReq(createModelStatement.getModelName(), createModelStatement.getUri()); + final TCreateModelReq req = new TCreateModelReq(modelId, uri); final TSStatus status = client.createModel(req); if (TSStatusCode.SUCCESS_STATUS.getStatusCode() != status.getCode()) { future.setException(new IoTDBException(status.message, status.code)); @@ -3320,11 +3317,11 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor { } @Override - public SettableFuture<ConfigTaskResult> dropModel(final String modelName) { + public SettableFuture<ConfigTaskResult> dropModel(final String modelId) { final SettableFuture<ConfigTaskResult> future = SettableFuture.create(); try (final ConfigNodeClient client = CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { - final TSStatus executionStatus = client.dropModel(new TDropModelReq(modelName)); + final TSStatus executionStatus = client.dropModel(new TDropModelReq(modelId)); if (TSStatusCode.SUCCESS_STATUS.getStatusCode() != executionStatus.getCode()) { future.setException(new IoTDBException(executionStatus.message, executionStatus.code)); } else { @@ -3337,13 +3334,13 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor { } @Override - public SettableFuture<ConfigTaskResult> showModels(final String modelName) { + public SettableFuture<ConfigTaskResult> showModels(final String modelId) { final SettableFuture<ConfigTaskResult> future = SettableFuture.create(); try (final ConfigNodeClient client = CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { final TShowModelReq req = new TShowModelReq(); - if (modelName != null) { - req.setModelId(modelName); + if (modelId != null) { + req.setModelId(modelId); } final TShowModelResp showModelResp = client.showModel(req); if (showModelResp.getStatus().getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java index 11df7b6f689..fe1f36b1b5d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java @@ -62,7 +62,6 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowClusterStatem import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowDatabaseStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowRegionStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowTTLStatement; -import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.CreateModelStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.AlterPipeStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.CreatePipePluginStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.CreatePipeStatement; @@ -415,12 +414,11 @@ public interface IConfigTaskExecutor { SettableFuture<ConfigTaskResult> showCurrentTimestamp(); // =============================== AI ========================================= - SettableFuture<ConfigTaskResult> createModel( - CreateModelStatement createModelStatement, MPPQueryContext context); + SettableFuture<ConfigTaskResult> createModel(String modelId, String uri); - SettableFuture<ConfigTaskResult> dropModel(String modelName); + SettableFuture<ConfigTaskResult> dropModel(String modelId); - SettableFuture<ConfigTaskResult> showModels(String modelName); + SettableFuture<ConfigTaskResult> showModels(String modelId); SettableFuture<ConfigTaskResult> createTraining( String modelId, diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateModelTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateModelTask.java index 875455529b5..6f488a0e7bf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateModelTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateModelTask.java @@ -19,27 +19,25 @@ package org.apache.iotdb.db.queryengine.plan.execution.config.metadata.ai; -import org.apache.iotdb.db.queryengine.common.MPPQueryContext; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; -import org.apache.iotdb.db.queryengine.plan.statement.metadata.model.CreateModelStatement; import com.google.common.util.concurrent.ListenableFuture; public class CreateModelTask implements IConfigTask { - private final CreateModelStatement createModelStatement; - private final MPPQueryContext context; + private final String modelId; + private final String uri; - public CreateModelTask(CreateModelStatement createModelStatement, MPPQueryContext context) { - this.createModelStatement = createModelStatement; - this.context = context; + public CreateModelTask(String modelId, String uri) { + this.modelId = modelId; + this.uri = uri; } @Override public ListenableFuture<ConfigTaskResult> execute(IConfigTaskExecutor configTaskExecutor) throws InterruptedException { - return configTaskExecutor.createModel(createModelStatement, context); + return configTaskExecutor.createModel(modelId, uri); } } 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 ad84c41b262..b30dd297153 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 @@ -1354,19 +1354,19 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> { } // Create Model ===================================================================== - public static void validateModelName(String modelName) { - if (modelName.length() < 2 || modelName.length() > 64) { - throw new SemanticException("Model name should be 2-64 characters"); - } else if (modelName.startsWith("_")) { - throw new SemanticException("Model name should not start with '_'"); - } else if (!modelName.matches("^[-\\w]*$")) { - throw new SemanticException("ModelName can only contain letters, numbers, and underscores"); + public static void validateModelId(String modelId) { + if (modelId.length() < 2 || modelId.length() > 64) { + throw new SemanticException("ModelId should be 2-64 characters"); + } else if (modelId.startsWith("_")) { + throw new SemanticException("ModelId should not start with '_'"); + } else if (!modelId.matches("^[-\\w]*$")) { + throw new SemanticException("ModelId can only contain letters, numbers, and underscores"); } } @Override public Statement visitCreateModel(IoTDBSqlParser.CreateModelContext ctx) { - if (ctx.modelName == null) { + if (ctx.uriClause() == null) { String modelId = ctx.modelId.getText(); CreateTrainingStatement createTrainingStatement = new CreateTrainingStatement(modelId); if (ctx.hparamPair() != null) { @@ -1404,10 +1404,10 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> { createTrainingStatement.setTargetPathPatterns(targetPath); return createTrainingStatement; } - String modelName = ctx.modelName.getText(); - validateModelName(modelName); + String modelId = ctx.modelId.getText(); + validateModelId(modelId); CreateModelStatement createModelStatement = new CreateModelStatement(); - createModelStatement.setModelName(parseIdentifier(modelName)); + createModelStatement.setModelId(parseIdentifier(modelId)); createModelStatement.setUri(parseAndValidateURI(ctx.uriClause())); return createModelStatement; } @@ -1423,7 +1423,7 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> { public Statement visitShowModels(IoTDBSqlParser.ShowModelsContext ctx) { ShowModelsStatement statement = new ShowModelsStatement(); if (ctx.modelId != null) { - statement.setModelName(parseIdentifier(ctx.modelId.getText())); + statement.setModelId(parseIdentifier(ctx.modelId.getText())); } return statement; } @@ -4571,7 +4571,7 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> { (QueryStatement) StatementGenerator.createStatement(sql.substring(1, sql.length() - 1), zoneId); - statement.setModelName(parseIdentifier(ctx.modelId.getText())); + statement.setModelId(parseIdentifier(ctx.modelId.getText())); statement.setHasModelInference(true); if (ctx.hparamPair() != null) { 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 fd3eda8e6f0..a7a4fdbc305 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 @@ -725,10 +725,18 @@ public abstract class AstVisitor<R, C> { return visitStatement(node, context); } + protected R visitCreateModel(CreateModel node, C context) { + return visitStatement(node, context); + } + protected R visitShowModels(ShowModels node, C context) { return visitStatement(node, context); } + protected R visitDropModel(DropModel node, C context) { + return visitStatement(node, context); + } + public R visitTableArgument(TableFunctionTableArgument tableFunctionTableArgument, C context) { return visitNode(tableFunctionTableArgument, context); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateModel.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateModel.java new file mode 100644 index 00000000000..f2132aab440 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateModel.java @@ -0,0 +1,72 @@ +/* + * 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 java.util.List; +import java.util.Objects; + +public class CreateModel extends Statement { + + private final String modelId; + private final String uri; + + public CreateModel(String modelId, String uri) { + super(null); + this.modelId = modelId; + this.uri = uri; + } + + public String getModelId() { + return modelId; + } + + public String getUri() { + return uri; + } + + @Override + public <R, C> R accept(AstVisitor<R, C> visitor, C context) { + return visitor.visitCreateModel(this, context); + } + + @Override + public List<? extends Node> getChildren() { + return null; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + CreateModel that = (CreateModel) o; + return Objects.equals(modelId, that.modelId) && Objects.equals(uri, that.uri); + } + + @Override + public int hashCode() { + return Objects.hash(modelId, uri); + } + + @Override + public String toString() { + return "CreateModel{" + "modelId='" + modelId + '\'' + ", uri='" + uri + '\'' + '}'; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTraining.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTraining.java index eb8f63df9fb..a62e4305d40 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTraining.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTraining.java @@ -72,20 +72,20 @@ public class CreateTraining extends Statement { } @Override - public int hashCode() { - return Objects.hash(modelId, targetSql, existingModelId, parameters); + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + CreateTraining that = (CreateTraining) o; + return Objects.equals(modelId, that.modelId) + && Objects.equals(targetSql, that.targetSql) + && Objects.equals(parameters, that.parameters) + && Objects.equals(existingModelId, that.existingModelId); } @Override - public boolean equals(Object obj) { - if (!(obj instanceof CreateTraining)) { - return false; - } - CreateTraining createTraining = (CreateTraining) obj; - return modelId.equals(createTraining.modelId) - && Objects.equals(existingModelId, createTraining.existingModelId) - && Objects.equals(parameters, createTraining.parameters) - && Objects.equals(targetSql, createTraining.targetSql); + public int hashCode() { + return Objects.hash(modelId, targetSql, parameters, existingModelId); } @Override @@ -94,13 +94,14 @@ public class CreateTraining extends Statement { + "modelId='" + modelId + '\'' + + ", targetSql='" + + targetSql + + '\'' + ", parameters=" + parameters + ", existingModelId='" + existingModelId + '\'' - + ", targetSql='" - + targetSql + '}'; } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropModel.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropModel.java new file mode 100644 index 00000000000..780b7c38f9a --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropModel.java @@ -0,0 +1,64 @@ +/* + * 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 java.util.List; +import java.util.Objects; + +public class DropModel extends Statement { + + private final String modelId; + + public DropModel(String modelId) { + super(null); + this.modelId = modelId; + } + + public String getModelId() { + return modelId; + } + + @Override + public <R, C> R accept(AstVisitor<R, C> visitor, C context) { + return visitor.visitDropModel(this, context); + } + + @Override + public List<? extends Node> getChildren() { + return null; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + DropModel dropModel = (DropModel) o; + return Objects.equals(modelId, dropModel.modelId); + } + + @Override + public int hashCode() { + return Objects.hashCode(modelId); + } + + @Override + public String toString() { + return "DropModel{" + "modelId='" + modelId + '\'' + '}'; + } +} 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 26fd39070fd..76c22001c40 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 @@ -58,6 +58,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountStatement; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateIndex; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateModel; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipePlugin; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTable; @@ -78,6 +79,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropFunction; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropIndex; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropModel; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropPipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropPipePlugin; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropSubscription; @@ -3544,51 +3546,63 @@ public class AstBuilder extends RelationalSqlBaseVisitor<Node> { } // ***************** AI ***************** - public static void validateModelName(String modelName) { - if (modelName.length() < 2 || modelName.length() > 64) { - throw new SemanticException("Model name should be 2-64 characters"); - } else if (modelName.startsWith("_")) { - throw new SemanticException("Model name should not start with '_'"); - } else if (!modelName.matches("^[-\\w]*$")) { - throw new SemanticException("ModelName can only contain letters, numbers, and underscores"); + public static void validateModelId(String modelId) { + if (modelId.length() < 2 || modelId.length() > 64) { + throw new SemanticException("ModelId should be 2-64 characters"); + } else if (modelId.startsWith("_")) { + throw new SemanticException("ModelId should not start with '_'"); + } else if (!modelId.matches("^[-\\w]*$")) { + throw new SemanticException("ModelId can only contain letters, numbers, and underscores"); } } @Override public Node visitCreateModelStatement(RelationalSqlParser.CreateModelStatementContext ctx) { String modelId = ctx.modelId.getText(); - validateModelName(modelId); - - if (ctx.targetData == null) { - throw new SemanticException("Target data in sql should be set in CREATE MODEL"); - } - String targetData = ((StringLiteral) visit(ctx.targetData)).getValue(); - CreateTraining createTraining = new CreateTraining(modelId, targetData); - if (ctx.HYPERPARAMETERS() != null) { - Map<String, String> parameters = new HashMap<>(); - for (RelationalSqlParser.HparamPairContext hparamPairContext : ctx.hparamPair()) { - parameters.put( - hparamPairContext.hparamKey.getText(), hparamPairContext.hyparamValue.getText()); + validateModelId(modelId); + if (ctx.uriClause() == null) { + if (ctx.targetData == null) { + throw new SemanticException("Target data in sql should be set in CREATE MODEL"); + } + String targetData = ((StringLiteral) visit(ctx.targetData)).getValue(); + CreateTraining createTraining = new CreateTraining(modelId, targetData); + if (ctx.HYPERPARAMETERS() != null) { + Map<String, String> parameters = new HashMap<>(); + for (RelationalSqlParser.HparamPairContext hparamPairContext : ctx.hparamPair()) { + parameters.put( + hparamPairContext.hparamKey.getText(), hparamPairContext.hyparamValue.getText()); + } + createTraining.setParameters(parameters); } - createTraining.setParameters(parameters); - } - if (ctx.existingModelId != null) { - createTraining.setExistingModelId(ctx.existingModelId.getText()); - } + if (ctx.existingModelId != null) { + createTraining.setExistingModelId(ctx.existingModelId.getText()); + } - return createTraining; + return createTraining; + } + String uri = ((Identifier) visit(ctx.uriClause().uri)).getValue(); + return new CreateModel(modelId, uri); } @Override public Node visitShowModelsStatement(RelationalSqlParser.ShowModelsStatementContext ctx) { ShowModels showModels = new ShowModels(); if (ctx.modelId != null) { - showModels.setModelId(ctx.modelId.getText()); + String modelId = ctx.modelId.getText(); + validateModelId(modelId); + showModels.setModelId(modelId); } return showModels; } + @Override + public Node visitDropModelStatement(RelationalSqlParser.DropModelStatementContext ctx) { + String modelId = ctx.modelId.getText(); + validateModelId(modelId); + return new DropModel(modelId); + } + // ***************** arguments ***************** @Override public Node visitGenericType(RelationalSqlParser.GenericTypeContext ctx) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/QueryStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/QueryStatement.java index 1993def2326..ba4ac07ae87 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/QueryStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/QueryStatement.java @@ -137,7 +137,7 @@ public class QueryStatement extends AuthorityInformationStatement { private boolean isResultSetEmpty = false; // [IoTDB-AI] used for model inference, which will be removed in the future - private String modelName; + private String modelId; private boolean hasModelInference = false; private boolean generateTime = false; private InferenceWindow inferenceWindow = null; @@ -151,12 +151,12 @@ public class QueryStatement extends AuthorityInformationStatement { return generateTime; } - public void setModelName(String modelName) { - this.modelName = modelName; + public void setModelId(String modelId) { + this.modelId = modelId; } - public String getModelName() { - return modelName; + public String getModelId() { + return modelId; } public void setHasModelInference(boolean hasModelInference) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/CreateModelStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/CreateModelStatement.java index e54792ac831..c99462bc3d1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/CreateModelStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/CreateModelStatement.java @@ -34,7 +34,7 @@ import java.util.List; public class CreateModelStatement extends Statement implements IConfigStatement { - private String modelName; + private String modelId; private String uri; @@ -46,16 +46,16 @@ public class CreateModelStatement extends Statement implements IConfigStatement return uri; } - public String getModelName() { - return modelName; + public String getModelId() { + return modelId; } public void setUri(String uri) { this.uri = uri; } - public void setModelName(String modelName) { - this.modelName = modelName; + public void setModelId(String modelId) { + this.modelId = modelId; } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/DropModelStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/DropModelStatement.java index 21871b58751..1273cde4fac 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/DropModelStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/DropModelStatement.java @@ -34,14 +34,14 @@ import java.util.List; public class DropModelStatement extends Statement implements IConfigStatement { - private final String modelName; + private final String modelId; - public DropModelStatement(String modelName) { - this.modelName = modelName; + public DropModelStatement(String modelId) { + this.modelId = modelId; } - public String getModelName() { - return modelName; + public String getModelId() { + return modelId; } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/ShowModelsStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/ShowModelsStatement.java index 50241dad114..4675ed8980c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/ShowModelsStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/ShowModelsStatement.java @@ -34,22 +34,22 @@ import java.util.List; public class ShowModelsStatement extends Statement implements IConfigStatement { - private String modelName; + private String modelId; public ShowModelsStatement() { // do nothing } - public void setModelName(String modelName) { - this.modelName = modelName; + public void setModelId(String modelId) { + this.modelId = modelId; } public boolean isSetModelName() { - return modelName != null; + return modelId != null; } - public String getModelName() { - return modelName; + public String getModelId() { + return modelId; } @Override 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 91bb547c27f..de9d62dcd21 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 @@ -162,6 +162,7 @@ statement // AI | createModelStatement + | dropModelStatement | showModelsStatement // View, Trigger, CQ, Quota are not supported yet @@ -782,13 +783,18 @@ revokeGrantOpt // ------------------------------------------- AI --------------------------------------------------------- createModelStatement - : CREATE MODEL modelId=identifier (WITH HYPERPARAMETERS '(' hparamPair (',' hparamPair)* ')')? FROM MODEL existingModelId=identifier ON DATASET '(' targetData=string ')' + : CREATE MODEL modelId=identifier uriClause + | CREATE MODEL modelId=identifier (WITH HYPERPARAMETERS '(' hparamPair (',' hparamPair)* ')')? FROM MODEL existingModelId=identifier ON DATASET '(' targetData=string ')' ; hparamPair : hparamKey=identifier '=' hyparamValue=primaryExpression ; +dropModelStatement + : DROP MODEL modelId=identifier + ; + showModelsStatement : SHOW MODELS | SHOW MODELS modelId=identifier
