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

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


The following commit(s) were added to refs/heads/master by this push:
     new 4e9679da812 feat: support automatic schema creation in Session (#18890)
4e9679da812 is described below

commit 4e9679da812934268918f5388c3885ecc3672c37
Author: shizy <[email protected]>
AuthorDate: Fri Oct 16 11:33:18 2026 +0800

    feat: support automatic schema creation in Session (#18890)
---
 .../it/session/IoTDBSessionRelationalIT.java       | 39 ++++++++++++++++++++++
 .../iotdb/session/AbstractSessionBuilder.java      |  2 ++
 .../java/org/apache/iotdb/session/Session.java     |  8 +++++
 .../apache/iotdb/session/SessionConnection.java    |  4 +++
 .../apache/iotdb/session/TableSessionBuilder.java  | 12 +++++++
 .../org/apache/iotdb/session/pool/SessionPool.java |  5 +++
 .../session/pool/TableSessionPoolBuilder.java      | 12 +++++++
 .../iotdb/db/protocol/session/IClientSession.java  | 10 ++++++
 .../iotdb/db/protocol/session/SessionManager.java  |  3 +-
 .../protocol/thrift/impl/ClientRPCServiceImpl.java | 15 +++++++--
 .../fetcher/TableHeaderSchemaValidator.java        |  3 +-
 .../commons/queryengine/common/SessionInfo.java    | 23 ++++++++++++-
 12 files changed, 131 insertions(+), 5 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBSessionRelationalIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBSessionRelationalIT.java
index d6f7aef603d..0fae395cc2d 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBSessionRelationalIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/session/IoTDBSessionRelationalIT.java
@@ -471,6 +471,45 @@ public class IoTDBSessionRelationalIT {
     }
   }
 
+  @Test
+  public void disableAutoCreateSchemaForTableSessionTest()
+      throws IoTDBConnectionException, StatementExecutionException {
+    final DataNodeWrapper dataNode = EnvFactory.getEnv().getDataNodeWrapper(0);
+    try (ITableSession session =
+        new TableSessionBuilder()
+            .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString()))
+            .database("db1")
+            .enableAutoCreateSchema(false)
+            .build()) {
+      final List<IMeasurementSchema> schemaList = new ArrayList<>();
+      schemaList.add(new MeasurementSchema("tag1", TSDataType.STRING));
+      schemaList.add(new MeasurementSchema("s1", TSDataType.INT64));
+      schemaList.add(new MeasurementSchema("s2", TSDataType.INT64));
+      final Tablet tablet =
+          new Tablet(
+              "session_auto_create",
+              IMeasurementSchema.getMeasurementNameList(schemaList),
+              IMeasurementSchema.getDataTypeList(schemaList),
+              Arrays.asList(ColumnCategory.TAG, ColumnCategory.FIELD, 
ColumnCategory.FIELD),
+              1);
+      tablet.addTimestamp(0, 1);
+      tablet.addValue("tag1", 0, "d1");
+      tablet.addValue("s1", 0, 1L);
+      tablet.addValue("s2", 0, 2L);
+
+      assertThrows(StatementExecutionException.class, () -> 
session.insert(tablet));
+
+      session.executeNonQueryStatement(
+          "CREATE TABLE session_auto_create (tag1 string tag, s1 int64 
field)");
+      assertThrows(StatementExecutionException.class, () -> 
session.insert(tablet));
+
+      try (SessionDataSet dataSet =
+          session.executeQueryStatement("SELECT * FROM session_auto_create")) {
+        assertEquals(Arrays.asList("time", "tag1", "s1"), 
dataSet.getColumnNames());
+      }
+    }
+  }
+
   @Test
   public void partialInsertRelationalTabletTest()
       throws IoTDBConnectionException, StatementExecutionException {
diff --git 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/AbstractSessionBuilder.java
 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/AbstractSessionBuilder.java
index 44500ed3f10..c41185e1bec 100644
--- 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/AbstractSessionBuilder.java
+++ 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/AbstractSessionBuilder.java
@@ -80,6 +80,8 @@ public abstract class AbstractSessionBuilder {
 
   public String database;
 
+  public boolean enableAutoCreateSchema = true;
+
   public Boolean isIoTDBRpcCompressionEnabled = true;
 
   public Boolean isThriftRpcCompactionEnabled = false;
diff --git 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java
index 6d31f81885a..a6deda17491 100644
--- a/iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java
+++ b/iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java
@@ -204,6 +204,8 @@ public class Session implements ISession {
   // may be null
   protected volatile String database;
 
+  protected volatile boolean enableAutoCreateSchema = true;
+
   private static final String VALUES_SIZE_SHOULD_BE_EQUAL =
       "times, measurementsList and valuesList's size should be equal";
 
@@ -484,6 +486,7 @@ public class Session implements ISession {
     this.sqlDialect = builder.sqlDialect;
     this.queryTimeoutInMs = builder.timeOut;
     this.database = builder.database;
+    this.enableAutoCreateSchema = builder.enableAutoCreateSchema;
   }
 
   @Override
@@ -4528,6 +4531,11 @@ public class Session implements ISession {
       return this;
     }
 
+    public Builder enableAutoCreateSchema(boolean enableAutoCreateSchema) {
+      this.enableAutoCreateSchema = enableAutoCreateSchema;
+      return this;
+    }
+
     public Builder useSSL(boolean useSSL) {
       this.useSSL = useSSL;
       return this;
diff --git 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/SessionConnection.java
 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/SessionConnection.java
index 89c9113b988..7b6ec3b03df 100644
--- 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/SessionConnection.java
+++ 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/SessionConnection.java
@@ -99,6 +99,7 @@ public class SessionConnection {
 
   private static final Logger logger = 
LoggerFactory.getLogger(SessionConnection.class);
   private static final String USE_ENCRYPTED_PASSWORD_KEY = 
"use_encrypted_password";
+  private static final String ENABLE_AUTO_CREATE_SCHEMA_KEY = 
"enable_auto_create_schema";
   public static final String MSG_RECONNECTION_FAIL =
       "Fail to reconnect to server. Please check server status.";
   protected Session session;
@@ -244,6 +245,9 @@ public class SessionConnection {
     if (session.useEncryptedPassword) {
       openReq.putToConfiguration(USE_ENCRYPTED_PASSWORD_KEY, 
Boolean.TRUE.toString());
     }
+    if (!session.enableAutoCreateSchema) {
+      openReq.putToConfiguration(ENABLE_AUTO_CREATE_SCHEMA_KEY, 
Boolean.FALSE.toString());
+    }
     if (database != null) {
       openReq.putToConfiguration("db", database);
     }
diff --git 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSessionBuilder.java
 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSessionBuilder.java
index 2dca0d351dc..20147e1cfcf 100644
--- 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSessionBuilder.java
+++ 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSessionBuilder.java
@@ -179,6 +179,18 @@ public class TableSessionBuilder extends 
AbstractSessionBuilder {
     return this;
   }
 
+  /**
+   * Enables or disables schema auto-creation for this session's write 
requests.
+   *
+   * @param enableAutoCreateSchema whether to allow auto creating tables and 
columns.
+   * @return the current {@link TableSessionBuilder} instance.
+   * @defaultValue true
+   */
+  public TableSessionBuilder enableAutoCreateSchema(boolean 
enableAutoCreateSchema) {
+    this.enableAutoCreateSchema = enableAutoCreateSchema;
+    return this;
+  }
+
   /**
    * Sets the maximum number of retries for connection attempts.
    *
diff --git 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/SessionPool.java
 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/SessionPool.java
index b3de497df39..32feac2cea5 100644
--- 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/SessionPool.java
+++ 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/SessionPool.java
@@ -188,6 +188,8 @@ public class SessionPool implements ISessionPool {
   // may be null
   protected String database;
 
+  protected boolean enableAutoCreateSchema = true;
+
   private static final String INSERT_RECORD_FAIL = 
SessionMessages.INSERT_RECORD_FAILED;
 
   private static final String INSERT_RECORD_ERROR_MSG =
@@ -553,6 +555,7 @@ public class SessionPool implements ISessionPool {
     this.sqlDialect = builder.sqlDialect;
     this.database = builder.database;
     this.queryTimeoutInMs = builder.timeOut;
+    this.enableAutoCreateSchema = builder.enableAutoCreateSchema;
 
     if (enableAutoFetch) {
       initThreadPool();
@@ -612,6 +615,7 @@ public class SessionPool implements ISessionPool {
               .retryIntervalInMs(retryIntervalInMs)
               .sqlDialect(sqlDialect)
               .database(database)
+              .enableAutoCreateSchema(enableAutoCreateSchema)
               .timeOut(queryTimeoutInMs)
               .enableIoTDBRpcCompression(enableIoTDBRpcCompression)
               .enableThriftRpcCompression(enableThriftCompression)
@@ -640,6 +644,7 @@ public class SessionPool implements ISessionPool {
               .retryIntervalInMs(retryIntervalInMs)
               .sqlDialect(sqlDialect)
               .database(database)
+              .enableAutoCreateSchema(enableAutoCreateSchema)
               .timeOut(queryTimeoutInMs)
               .enableIoTDBRpcCompression(enableIoTDBRpcCompression)
               .enableThriftRpcCompression(enableThriftCompression)
diff --git 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/TableSessionPoolBuilder.java
 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/TableSessionPoolBuilder.java
index ab3e1f1c6a6..790e7816288 100644
--- 
a/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/TableSessionPoolBuilder.java
+++ 
b/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/TableSessionPoolBuilder.java
@@ -317,6 +317,18 @@ public class TableSessionPoolBuilder extends 
AbstractSessionPoolBuilder {
     return this;
   }
 
+  /**
+   * Enables or disables schema auto-creation for this pool's write requests.
+   *
+   * @param enableAutoCreateSchema whether to allow auto creating tables and 
columns.
+   * @return the current {@link TableSessionPoolBuilder} instance.
+   * @defaultValue true
+   */
+  public TableSessionPoolBuilder enableAutoCreateSchema(boolean 
enableAutoCreateSchema) {
+    this.enableAutoCreateSchema = enableAutoCreateSchema;
+    return this;
+  }
+
   /**
    * Builds and returns a configured {@link ITableSessionPool} instance.
    *
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/IClientSession.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/IClientSession.java
index 5114ecb0d78..aaf73db5c4d 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/IClientSession.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/IClientSession.java
@@ -59,6 +59,8 @@ public abstract class IClientSession {
 
   @Nullable private String databaseName;
 
+  private boolean enableAutoCreateSchema = true;
+
   private long lastActiveTime = CommonDateTimeUtils.currentTime();
 
   public abstract String getClientAddress();
@@ -201,6 +203,14 @@ public abstract class IClientSession {
     }
   }
 
+  public boolean isEnableAutoCreateSchema() {
+    return enableAutoCreateSchema;
+  }
+
+  public void setEnableAutoCreateSchema(boolean enableAutoCreateSchema) {
+    this.enableAutoCreateSchema = enableAutoCreateSchema;
+  }
+
   /**
    * Add a prepared statement to this session.
    *
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/SessionManager.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/SessionManager.java
index 21dbffb606e..3acfc299b9e 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/SessionManager.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/SessionManager.java
@@ -510,7 +510,8 @@ public class SessionManager implements SessionManagerMBean {
         session.getZoneId(),
         session.getClientVersion(),
         session.getDatabaseName(),
-        session.getSqlDialect());
+        session.getSqlDialect(),
+        session.isEnableAutoCreateSchema());
   }
 
   // Sometimes we need to switch from table model to tree model,
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
index 692b5bedd2d..ad2a85db152 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
@@ -270,6 +270,7 @@ public class ClientRPCServiceImpl implements 
IClientRPCServiceWithHandler {
 
   public static final String ERROR_CODE = DataNodeMiscMessages.ERROR_CODE;
   private static final String USE_ENCRYPTED_PASSWORD_KEY = 
"use_encrypted_password";
+  private static final String ENABLE_AUTO_CREATE_SCHEMA_KEY = 
"enable_auto_create_schema";
 
   private static final TSProtocolVersion CURRENT_RPC_VERSION =
       TSProtocolVersion.IOTDB_SERVICE_PROTOCOL_V3;
@@ -1633,8 +1634,11 @@ public class ClientRPCServiceImpl implements 
IClientRPCServiceWithHandler {
             parseUseEncryptedPassword(req));
     TSStatus tsStatus = RpcUtils.getStatus(openSessionResp.getCode(), 
openSessionResp.getMessage());
 
-    if (tsStatus.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() && 
database.isPresent()) {
-      
clientSession.setDatabaseName(database.get().toLowerCase(Locale.ENGLISH));
+    if (tsStatus.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+      if (database.isPresent()) {
+        
clientSession.setDatabaseName(database.get().toLowerCase(Locale.ENGLISH));
+      }
+      
clientSession.setEnableAutoCreateSchema(parseEnableAutoCreateSchema(req));
     }
     TSOpenSessionResp resp = new TSOpenSessionResp(tsStatus, 
CURRENT_RPC_VERSION);
     Map<String, String> configuration = new HashMap<>();
@@ -1678,6 +1682,13 @@ public class ClientRPCServiceImpl implements 
IClientRPCServiceWithHandler {
         && Boolean.parseBoolean(configuration.get(USE_ENCRYPTED_PASSWORD_KEY));
   }
 
+  private boolean parseEnableAutoCreateSchema(TSOpenSessionReq req) {
+    Map<String, String> configuration = req.configuration;
+    return configuration == null
+        || !configuration.containsKey(ENABLE_AUTO_CREATE_SCHEMA_KEY)
+        || 
Boolean.parseBoolean(configuration.get(ENABLE_AUTO_CREATE_SCHEMA_KEY));
+  }
+
   @Override
   public TSStatus closeSession(TSCloseSessionReq req) {
     return new TSStatus(
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
index 6a08427d82b..01c2555608a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
@@ -368,7 +368,8 @@ public class TableHeaderSchemaValidator {
     final List<Integer> missingMeasurementIndices = new ArrayList<>();
 
     final boolean isAutoCreateSchemaEnabled =
-        IoTDBDescriptor.getInstance().getConfig().isAutoCreateSchemaEnabled();
+        IoTDBDescriptor.getInstance().getConfig().isAutoCreateSchemaEnabled()
+            && context.getSession().isEnableAutoCreateSchema();
 
     // First round validate, check existing schema
     if (table == null) {
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/common/SessionInfo.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/common/SessionInfo.java
index 824ea18ce7d..de681067c93 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/common/SessionInfo.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/common/SessionInfo.java
@@ -46,12 +46,17 @@ public class SessionInfo {
 
   private ClientVersion version = ClientVersion.V_1_0;
 
+  // Only used during statement analysis on the coordinator. Analysis is 
complete before the
+  // SessionInfo is sent to execution nodes, so this field does not need 
serialization.
+  private final transient boolean enableAutoCreateSchema;
+
   public SessionInfo(long sessionId, UserEntity userEntity, ZoneId zoneId) {
     this.sessionId = sessionId;
     this.userEntity = userEntity;
     this.zoneId = zoneId;
     this.databaseName = null;
     this.sqlDialect = SqlDialect.TREE;
+    this.enableAutoCreateSchema = true;
   }
 
   public SessionInfo(
@@ -60,7 +65,7 @@ public class SessionInfo {
       ZoneId zoneId,
       @Nullable String databaseName,
       SqlDialect sqlDialect) {
-    this(sessionId, userEntity, zoneId, ClientVersion.V_1_0, databaseName, 
sqlDialect);
+    this(sessionId, userEntity, zoneId, ClientVersion.V_1_0, databaseName, 
sqlDialect, true);
   }
 
   public SessionInfo(
@@ -70,12 +75,24 @@ public class SessionInfo {
       ClientVersion version,
       @Nullable String databaseName,
       SqlDialect sqlDialect) {
+    this(sessionId, userEntity, zoneId, version, databaseName, sqlDialect, 
true);
+  }
+
+  public SessionInfo(
+      long sessionId,
+      UserEntity userEntity,
+      ZoneId zoneId,
+      ClientVersion version,
+      @Nullable String databaseName,
+      SqlDialect sqlDialect,
+      boolean enableAutoCreateSchema) {
     this.sessionId = sessionId;
     this.userEntity = userEntity;
     this.zoneId = zoneId;
     this.version = version;
     this.databaseName = databaseName;
     this.sqlDialect = sqlDialect;
+    this.enableAutoCreateSchema = enableAutoCreateSchema;
   }
 
   @TestOnly
@@ -144,6 +161,10 @@ public class SessionInfo {
     return sqlDialect;
   }
 
+  public boolean isEnableAutoCreateSchema() {
+    return enableAutoCreateSchema;
+  }
+
   public static SessionInfo deserializeFrom(final ByteBuffer buffer) {
     final long sessionId = ReadWriteIOUtils.readLong(buffer);
     final long userId = ReadWriteIOUtils.readLong(buffer);

Reply via email to