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);