You are viewing a plain text version of this content. The canonical link for it is here.
Posted to reviews@iotdb.apache.org by "hongzhi-gao (via GitHub)" <gi...@apache.org> on 2026/03/24 06:47:18 UTC

[PR] session c [iotdb]

hongzhi-gao opened a new pull request, #17347:
URL: https://github.com/apache/iotdb/pull/17347

   ## **Description**
   ### What
   Adds a C API (SessionC.h / SessionC.cpp) around the existing C++ Session / TableSession, for C/FFI callers without exposing C++ types.
   
   ### Design
   
   Opaque handles (CSession, CTableSession, CTablet, CSessionDataSet, CRowRecord); public symbols prefixed with ts_*.
   TsStatus return codes + thread-local last error via ts_get_last_error().
   Thin wrappers: convert C args → C++ types, map exceptions to status codes; reuse existing client logic (no duplicate Thrift layer).
   ### Scope
   Tree + table model: connect, metadata, inserts (record/tablet), queries, result iteration, deletes—aligned with current Session behavior.
   
   ### This PR has:
   
   self-reviewed
   tests added or updated
   
   (optional) tested on a cluster
   ### Key files
   client-cpp/src/main/SessionC.h, SessionC.cpp
   client-cpp/src/test/main_c*.cpp, sessionC*IT.cpp


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "hongzhi-gao (via GitHub)" <gi...@apache.org>.
hongzhi-gao commented on code in PR #17347:
URL: https://github.com/apache/iotdb/pull/17347#discussion_r3070892476


##########
iotdb-client/client-cpp/src/test/main.cpp:
##########
@@ -23,30 +23,35 @@
 #include "Session.h"
 #include "SessionBuilder.h"
 
-auto builder = std::unique_ptr<SessionBuilder>(new SessionBuilder());
-std::shared_ptr<Session> session =
-    std::shared_ptr<Session>(
-        builder
-        ->host("127.0.0.1")
-        ->rpcPort(6667)
-        ->username("root")
-        ->password("root")
-        ->useSSL(false)
-        ->build()
-    );
+std::shared_ptr<Session> session;
 
 struct SessionListener : Catch::TestEventListenerBase {
 
     using TestEventListenerBase::TestEventListenerBase;
 
     void testCaseStarting(Catch::TestCaseInfo const &testInfo) override {
-        // Perform some setup before a test case is run
-        session->open(false);
+        if (!session) {
+            SessionBuilder builder;
+            session = builder.host("127.0.0.1")
+                          ->rpcPort(6667)
+                          ->username("root")
+                          ->password("root")
+                          ->useSSL(false)
+                          ->build();
+        } else {
+            session->open(false);
+        }

Review Comment:
   SessionBuilder::build() already calls open(false) before returning the Session (see SessionBuilder.h), so the first test case starts with an open session. Subsequent cases rely on the else branch after testCaseEnded closes the session.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "hongzhi-gao (via GitHub)" <gi...@apache.org>.
hongzhi-gao commented on code in PR #17347:
URL: https://github.com/apache/iotdb/pull/17347#discussion_r3070827883


##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_with_zone(const char* host, int rpcPort,
+                                    const char* username, const char* password,
+                                    const char* zoneId, int fetchSize) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password),
+            std::string(zoneId), fetchSize);
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                     const char* username, const char* password) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(urls, std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_session_destroy(CSession* session) {
+    delete session;
+}
+
+TsStatus ts_session_open(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open(enableRPCCompression);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_close(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Table Model
+ * ============================================================ */
+
+CTableSession* ts_table_session_new(const char* host, int rpcPort,
+                                     const char* username, const char* password,
+                                     const char* database) {
+    clearError();
+    try {
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->host(std::string(host))
+            ->rpcPort(rpcPort)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                                const char* username, const char* password,
+                                                const char* database) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->nodeUrls(urls)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_table_session_destroy(CTableSession* session) {
+    delete session;
+}
+
+TsStatus ts_table_session_open(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_close(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timezone
+ * ============================================================ */
+
+TsStatus ts_session_set_timezone(CSession* session, const char* zoneId) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->setTimeZone(std::string(zoneId));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!buf || bufLen <= 0) return setError(TS_ERR_INVALID_PARAM, "invalid buffer");
+    try {
+        std::string tz = session->cpp->getTimeZone();
+        if ((int)tz.size() >= bufLen) {
+            return setError(TS_ERR_INVALID_PARAM, "buffer too small");
+        }
+        strncpy(buf, tz.c_str(), bufLen);
+        buf[bufLen - 1] = '\0';
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Database Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_databases(CSession* session, const char* const* databases, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto dbs = toStringVec(databases, count);
+        session->cpp->deleteDatabases(dbs);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timeseries Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_timeseries(CSession* session, const char* path,
+                                       TSDataType_C dataType, TSEncoding_C encoding,
+                                       TSCompressionType_C compressor) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_timeseries_ex(CSession* session, const char* path,
+                                          TSDataType_C dataType, TSEncoding_C encoding,
+                                          TSCompressionType_C compressor,
+                                          int propsCount,
+                                          const char* const* propKeys,
+                                          const char* const* propValues,
+                                          int tagsCount,
+                                          const char* const* tagKeys,
+                                          const char* const* tagValues,
+                                          int attrsCount,
+                                          const char* const* attrKeys,
+                                          const char* const* attrValues,
+                                          const char* measurementAlias) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::map<std::string, std::string> props = propsCount > 0 ? toStringMap(propsCount, propKeys, propValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> tags = tagsCount > 0 ? toStringMap(tagsCount, tagKeys, tagValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> attrs = attrsCount > 0 ? toStringMap(attrsCount, attrKeys, attrValues) : std::map<std::string, std::string>();

Review Comment:
   When initializing a `std::map` from a return value, it will use move semantics.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "hongzhi-gao (via GitHub)" <gi...@apache.org>.
hongzhi-gao closed pull request #17347: Session:  session-c interface
URL: https://github.com/apache/iotdb/pull/17347


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "jt2594838 (via GitHub)" <gi...@apache.org>.
jt2594838 merged PR #17347:
URL: https://github.com/apache/iotdb/pull/17347


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "jt2594838 (via GitHub)" <gi...@apache.org>.
jt2594838 commented on code in PR #17347:
URL: https://github.com/apache/iotdb/pull/17347#discussion_r3042962465


##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_with_zone(const char* host, int rpcPort,
+                                    const char* username, const char* password,
+                                    const char* zoneId, int fetchSize) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password),
+            std::string(zoneId), fetchSize);
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                     const char* username, const char* password) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(urls, std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_session_destroy(CSession* session) {
+    delete session;
+}
+
+TsStatus ts_session_open(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open(enableRPCCompression);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_close(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Table Model
+ * ============================================================ */
+
+CTableSession* ts_table_session_new(const char* host, int rpcPort,
+                                     const char* username, const char* password,
+                                     const char* database) {
+    clearError();
+    try {
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->host(std::string(host))
+            ->rpcPort(rpcPort)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                                const char* username, const char* password,
+                                                const char* database) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->nodeUrls(urls)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_table_session_destroy(CTableSession* session) {
+    delete session;
+}

Review Comment:
   Check if it is nullptr?



##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_with_zone(const char* host, int rpcPort,
+                                    const char* username, const char* password,
+                                    const char* zoneId, int fetchSize) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password),
+            std::string(zoneId), fetchSize);
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                     const char* username, const char* password) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(urls, std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_session_destroy(CSession* session) {
+    delete session;
+}
+
+TsStatus ts_session_open(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open(enableRPCCompression);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_close(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Table Model
+ * ============================================================ */
+
+CTableSession* ts_table_session_new(const char* host, int rpcPort,
+                                     const char* username, const char* password,
+                                     const char* database) {
+    clearError();
+    try {
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->host(std::string(host))
+            ->rpcPort(rpcPort)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                                const char* username, const char* password,
+                                                const char* database) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->nodeUrls(urls)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_table_session_destroy(CTableSession* session) {
+    delete session;
+}
+
+TsStatus ts_table_session_open(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_close(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timezone
+ * ============================================================ */
+
+TsStatus ts_session_set_timezone(CSession* session, const char* zoneId) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->setTimeZone(std::string(zoneId));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!buf || bufLen <= 0) return setError(TS_ERR_INVALID_PARAM, "invalid buffer");
+    try {
+        std::string tz = session->cpp->getTimeZone();
+        if ((int)tz.size() >= bufLen) {
+            return setError(TS_ERR_INVALID_PARAM, "buffer too small");
+        }
+        strncpy(buf, tz.c_str(), bufLen);
+        buf[bufLen - 1] = '\0';
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Database Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_databases(CSession* session, const char* const* databases, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto dbs = toStringVec(databases, count);
+        session->cpp->deleteDatabases(dbs);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timeseries Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_timeseries(CSession* session, const char* path,
+                                       TSDataType_C dataType, TSEncoding_C encoding,
+                                       TSCompressionType_C compressor) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_timeseries_ex(CSession* session, const char* path,
+                                          TSDataType_C dataType, TSEncoding_C encoding,
+                                          TSCompressionType_C compressor,
+                                          int propsCount,
+                                          const char* const* propKeys,
+                                          const char* const* propValues,
+                                          int tagsCount,
+                                          const char* const* tagKeys,
+                                          const char* const* tagValues,
+                                          int attrsCount,
+                                          const char* const* attrKeys,
+                                          const char* const* attrValues,
+                                          const char* measurementAlias) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::map<std::string, std::string> props = propsCount > 0 ? toStringMap(propsCount, propKeys, propValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> tags = tagsCount > 0 ? toStringMap(tagsCount, tagKeys, tagValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> attrs = attrsCount > 0 ? toStringMap(attrsCount, attrKeys, attrValues) : std::map<std::string, std::string>();
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor),
+            &props, &tags, &attrs,
+            std::string(measurementAlias ? measurementAlias : ""));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_multi_timeseries(CSession* session, int count,
+                                             const char* const* paths,
+                                             const TSDataType_C* dataTypes,
+                                             const TSEncoding_C* encodings,
+                                             const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createMultiTimeseries(
+            pathsVec, typesVec, encVec, compVec,
+            nullptr, nullptr, nullptr, nullptr);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_aligned_timeseries(CSession* session, const char* deviceId,
+                                               int count,
+                                               const char* const* measurements,
+                                               const TSDataType_C* dataTypes,
+                                               const TSEncoding_C* encodings,
+                                               const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createAlignedTimeseries(
+            std::string(deviceId), measurementsVec, typesVec, encVec, compVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_check_timeseries_exists(CSession* session, const char* path, bool* exists) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!exists) return setError(TS_ERR_INVALID_PARAM, "exists pointer is null");
+    try {
+        *exists = session->cpp->checkTimeseriesExists(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries(CSession* session, const char* path) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteTimeseries(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries_batch(CSession* session, const char* const* paths, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        session->cpp->deleteTimeseries(pathsVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Tablet Operations
+ * ============================================================ */
+
+CTablet* ts_tablet_new(const char* deviceId, int columnCount,
+                        const char* const* columnNames,
+                        const TSDataType_C* dataTypes,
+                        int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        schemas.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTablet* ts_tablet_new_with_category(const char* deviceId, int columnCount,
+                                      const char* const* columnNames,
+                                      const TSDataType_C* dataTypes,
+                                      const TSColumnCategory_C* columnCategories,
+                                      int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        std::vector<ColumnCategory> colTypes;
+        schemas.reserve(columnCount);
+        colTypes.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+            colTypes.push_back(static_cast<ColumnCategory>(columnCategories[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, colTypes, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_tablet_destroy(CTablet* tablet) {
+    delete tablet;
+}
+
+void ts_tablet_reset(CTablet* tablet) {
+    if (tablet) {
+        tablet->cpp.reset();
+    }
+}
+
+int ts_tablet_get_row_count(CTablet* tablet) {
+    if (!tablet) return 0;
+    return static_cast<int>(tablet->cpp.rowSize);
+}
+
+TsStatus ts_tablet_set_row_count(CTablet* tablet, int rowCount) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    tablet->cpp.rowSize = rowCount;
+    return TS_OK;
+}
+
+TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addTimestamp(static_cast<size_t>(rowIndex), timestamp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        std::string str(value);
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), str);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Record, string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_record_str(CSession* session, const char* deviceId,
+                                       int64_t time, int count,
+                                       const char* const* measurements,
+                                       const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_record(CSession* session, const char* deviceId,
+                                   int64_t time, int count,
+                                   const char* const* measurements,
+                                   const TSDataType_C* types,
+                                   const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record_str(CSession* session, const char* deviceId,
+                                               int64_t time, int count,
+                                               const char* const* measurements,
+                                               const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record(CSession* session, const char* deviceId,
+                                           int64_t time, int count,
+                                           const char* const* measurements,
+                                           const TSDataType_C* types,
+                                           const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_str(CSession* session, int deviceCount,
+                                        const char* const* deviceIds,
+                                        const int64_t* times,
+                                        const int* measurementCounts,
+                                        const char* const* const* measurementsList,
+                                        const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_str(CSession* session, int deviceCount,
+                                                const char* const* deviceIds,
+                                                const int64_t* times,
+                                                const int* measurementCounts,
+                                                const char* const* const* measurementsList,
+                                                const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records(CSession* session, int deviceCount,
+                                    const char* const* deviceIds,
+                                    const int64_t* times,
+                                    const int* measurementCounts,
+                                    const char* const* const* measurementsList,
+                                    const TSDataType_C* const* typesList,
+                                    const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records(CSession* session, int deviceCount,
+                                            const char* const* deviceIds,
+                                            const int64_t* times,
+                                            const int* measurementCounts,
+                                            const char* const* const* measurementsList,
+                                            const TSDataType_C* const* typesList,
+                                            const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, single device (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_of_one_device(CSession* session, const char* deviceId,
+                                                  int rowCount, const int64_t* times,
+                                                  const int* measurementCounts,
+                                                  const char* const* const* measurementsList,
+                                                  const TSDataType_C* const* typesList,
+                                                  const void* const* const* valuesList,
+                                                  bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_of_one_device(CSession* session, const char* deviceId,
+                                                          int rowCount, const int64_t* times,
+                                                          const int* measurementCounts,
+                                                          const char* const* const* measurementsList,
+                                                          const TSDataType_C* const* typesList,
+                                                          const void* const* const* valuesList,
+                                                          bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_session_insert_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertAlignedTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_tablets(CSession* session, int tabletCount,
+                                    const char* const* deviceIds,
+                                    CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablets(CSession* session, int tabletCount,
+                                            const char* const* deviceIds,
+                                            CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertAlignedTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Table Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_table_session_insert(CTableSession* session, CTablet* tablet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insert(tablet->cpp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Tree Model
+ * ============================================================ */
+
+TsStatus ts_session_execute_query(CSession* session, const char* sql,
+                                   CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_query_with_timeout(CSession* session, const char* sql,
+                                                int64_t timeoutInMs,
+                                                CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_non_query(CSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_raw_data_query(CSession* session,
+                                            int pathCount, const char* const* paths,
+                                            int64_t startTime, int64_t endTime,
+                                            CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeRawDataQuery(pathsVec, startTime, endTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query(CSession* session,
+                                             int pathCount, const char* const* paths,
+                                             CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query_with_time(CSession* session,
+                                                       int pathCount, const char* const* paths,
+                                                       int64_t lastTime,
+                                                       CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec, lastTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Table Model
+ * ============================================================ */
+
+TsStatus ts_table_session_execute_query(CTableSession* session, const char* sql,
+                                         CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_query_with_timeout(CTableSession* session, const char* sql,
+                                                      int64_t timeoutInMs,
+                                                      CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_non_query(CTableSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  SessionDataSet & RowRecord  —  Result Iteration
+ * ============================================================ */
+
+void ts_dataset_destroy(CSessionDataSet* dataSet) {
+    if (dataSet) {
+        if (dataSet->cpp) {
+            dataSet->cpp->closeOperationHandle();
+        }
+        delete dataSet;
+    }
+}
+
+bool ts_dataset_has_next(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return false;
+    try {
+        return dataSet->cpp->hasNext();
+    } catch (...) {
+        return false;
+    }
+}
+
+CRowRecord* ts_dataset_next(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return nullptr;
+    try {
+        auto row = dataSet->cpp->next();
+        if (!row) return nullptr;
+        auto* crr = new CRowRecord_();
+        crr->cpp = row;
+        return crr;
+    } catch (...) {
+        return nullptr;
+    }
+}
+
+int ts_dataset_get_column_count(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return 0;
+    return static_cast<int>(dataSet->cpp->getColumnNames().size());
+}
+
+static thread_local std::string g_colNameBuf;
+
+const char* ts_dataset_get_column_name(CSessionDataSet* dataSet, int index) {
+    if (!dataSet || !dataSet->cpp) return "";
+    const auto& names = dataSet->cpp->getColumnNames();
+    if (index < 0 || index >= (int)names.size()) return "";
+    g_colNameBuf = names[index];
+    return g_colNameBuf.c_str();
+}
+
+static thread_local std::string g_colTypeBuf;
+
+const char* ts_dataset_get_column_type(CSessionDataSet* dataSet, int index) {
+    if (!dataSet || !dataSet->cpp) return "";
+    const auto& types = dataSet->cpp->getColumnTypeList();
+    if (index < 0 || index >= (int)types.size()) return "";
+    g_colTypeBuf = types[index];
+    return g_colTypeBuf.c_str();
+}
+
+void ts_dataset_set_fetch_size(CSessionDataSet* dataSet, int fetchSize) {
+    if (dataSet && dataSet->cpp) {
+        dataSet->cpp->setFetchSize(fetchSize);
+    }
+}
+
+void ts_row_record_destroy(CRowRecord* record) {
+    delete record;
+}
+
+int64_t ts_row_record_get_timestamp(CRowRecord* record) {
+    if (!record || !record->cpp) return -1;
+    return record->cpp->timestamp;
+}
+
+int ts_row_record_get_field_count(CRowRecord* record) {
+    if (!record || !record->cpp) return 0;
+    return static_cast<int>(record->cpp->fields.size());
+}
+
+bool ts_row_record_is_null(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return true;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return true;
+    const Field& f = record->cpp->fields[index];
+    switch (f.dataType) {
+        case TSDataType::BOOLEAN:   return !f.boolV.is_initialized();
+        case TSDataType::INT32:     return !f.intV.is_initialized();
+        case TSDataType::INT64:
+        case TSDataType::TIMESTAMP: return !f.longV.is_initialized();
+        case TSDataType::FLOAT:     return !f.floatV.is_initialized();
+        case TSDataType::DOUBLE:    return !f.doubleV.is_initialized();
+        case TSDataType::TEXT:
+        case TSDataType::STRING:
+        case TSDataType::BLOB:      return !f.stringV.is_initialized();
+        case TSDataType::DATE:      return !f.dateV.is_initialized();
+        default:                    return true;
+    }
+}

Review Comment:
   No such a method in Cpp?



##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_with_zone(const char* host, int rpcPort,
+                                    const char* username, const char* password,
+                                    const char* zoneId, int fetchSize) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password),
+            std::string(zoneId), fetchSize);
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                     const char* username, const char* password) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(urls, std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_session_destroy(CSession* session) {
+    delete session;
+}
+
+TsStatus ts_session_open(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open(enableRPCCompression);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_close(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Table Model
+ * ============================================================ */
+
+CTableSession* ts_table_session_new(const char* host, int rpcPort,
+                                     const char* username, const char* password,
+                                     const char* database) {
+    clearError();
+    try {
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->host(std::string(host))
+            ->rpcPort(rpcPort)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                                const char* username, const char* password,
+                                                const char* database) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->nodeUrls(urls)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_table_session_destroy(CTableSession* session) {
+    delete session;
+}
+
+TsStatus ts_table_session_open(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_close(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timezone
+ * ============================================================ */
+
+TsStatus ts_session_set_timezone(CSession* session, const char* zoneId) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->setTimeZone(std::string(zoneId));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!buf || bufLen <= 0) return setError(TS_ERR_INVALID_PARAM, "invalid buffer");
+    try {
+        std::string tz = session->cpp->getTimeZone();
+        if ((int)tz.size() >= bufLen) {
+            return setError(TS_ERR_INVALID_PARAM, "buffer too small");
+        }
+        strncpy(buf, tz.c_str(), bufLen);
+        buf[bufLen - 1] = '\0';
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Database Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_databases(CSession* session, const char* const* databases, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto dbs = toStringVec(databases, count);
+        session->cpp->deleteDatabases(dbs);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timeseries Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_timeseries(CSession* session, const char* path,
+                                       TSDataType_C dataType, TSEncoding_C encoding,
+                                       TSCompressionType_C compressor) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_timeseries_ex(CSession* session, const char* path,
+                                          TSDataType_C dataType, TSEncoding_C encoding,
+                                          TSCompressionType_C compressor,
+                                          int propsCount,
+                                          const char* const* propKeys,
+                                          const char* const* propValues,
+                                          int tagsCount,
+                                          const char* const* tagKeys,
+                                          const char* const* tagValues,
+                                          int attrsCount,
+                                          const char* const* attrKeys,
+                                          const char* const* attrValues,
+                                          const char* measurementAlias) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::map<std::string, std::string> props = propsCount > 0 ? toStringMap(propsCount, propKeys, propValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> tags = tagsCount > 0 ? toStringMap(tagsCount, tagKeys, tagValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> attrs = attrsCount > 0 ? toStringMap(attrsCount, attrKeys, attrValues) : std::map<std::string, std::string>();

Review Comment:
   Will this result in unnecessary copying?



##########
iotdb-client/client-cpp/src/test/main.cpp:
##########
@@ -23,30 +23,35 @@
 #include "Session.h"
 #include "SessionBuilder.h"
 
-auto builder = std::unique_ptr<SessionBuilder>(new SessionBuilder());
-std::shared_ptr<Session> session =
-    std::shared_ptr<Session>(
-        builder
-        ->host("127.0.0.1")
-        ->rpcPort(6667)
-        ->username("root")
-        ->password("root")
-        ->useSSL(false)
-        ->build()
-    );
+std::shared_ptr<Session> session;
 
 struct SessionListener : Catch::TestEventListenerBase {
 
     using TestEventListenerBase::TestEventListenerBase;
 
     void testCaseStarting(Catch::TestCaseInfo const &testInfo) override {
-        // Perform some setup before a test case is run
-        session->open(false);
+        if (!session) {
+            SessionBuilder builder;
+            session = builder.host("127.0.0.1")
+                          ->rpcPort(6667)
+                          ->username("root")
+                          ->password("root")
+                          ->useSSL(false)
+                          ->build();
+        } else {
+            session->open(false);
+        }

Review Comment:
   Session is not open in the first test?



##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }

Review Comment:
   Will cs be freed when an exception occurs?
   The same below.



##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_with_zone(const char* host, int rpcPort,
+                                    const char* username, const char* password,
+                                    const char* zoneId, int fetchSize) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password),
+            std::string(zoneId), fetchSize);
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                     const char* username, const char* password) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(urls, std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_session_destroy(CSession* session) {
+    delete session;
+}
+
+TsStatus ts_session_open(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open(enableRPCCompression);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_close(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Table Model
+ * ============================================================ */
+
+CTableSession* ts_table_session_new(const char* host, int rpcPort,
+                                     const char* username, const char* password,
+                                     const char* database) {
+    clearError();
+    try {
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->host(std::string(host))
+            ->rpcPort(rpcPort)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                                const char* username, const char* password,
+                                                const char* database) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->nodeUrls(urls)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_table_session_destroy(CTableSession* session) {
+    delete session;
+}
+
+TsStatus ts_table_session_open(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_close(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timezone
+ * ============================================================ */
+
+TsStatus ts_session_set_timezone(CSession* session, const char* zoneId) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->setTimeZone(std::string(zoneId));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!buf || bufLen <= 0) return setError(TS_ERR_INVALID_PARAM, "invalid buffer");
+    try {
+        std::string tz = session->cpp->getTimeZone();
+        if ((int)tz.size() >= bufLen) {
+            return setError(TS_ERR_INVALID_PARAM, "buffer too small");
+        }
+        strncpy(buf, tz.c_str(), bufLen);
+        buf[bufLen - 1] = '\0';
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Database Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_databases(CSession* session, const char* const* databases, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto dbs = toStringVec(databases, count);
+        session->cpp->deleteDatabases(dbs);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timeseries Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_timeseries(CSession* session, const char* path,
+                                       TSDataType_C dataType, TSEncoding_C encoding,
+                                       TSCompressionType_C compressor) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_timeseries_ex(CSession* session, const char* path,
+                                          TSDataType_C dataType, TSEncoding_C encoding,
+                                          TSCompressionType_C compressor,
+                                          int propsCount,
+                                          const char* const* propKeys,
+                                          const char* const* propValues,
+                                          int tagsCount,
+                                          const char* const* tagKeys,
+                                          const char* const* tagValues,
+                                          int attrsCount,
+                                          const char* const* attrKeys,
+                                          const char* const* attrValues,
+                                          const char* measurementAlias) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::map<std::string, std::string> props = propsCount > 0 ? toStringMap(propsCount, propKeys, propValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> tags = tagsCount > 0 ? toStringMap(tagsCount, tagKeys, tagValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> attrs = attrsCount > 0 ? toStringMap(attrsCount, attrKeys, attrValues) : std::map<std::string, std::string>();
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor),
+            &props, &tags, &attrs,
+            std::string(measurementAlias ? measurementAlias : ""));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_multi_timeseries(CSession* session, int count,
+                                             const char* const* paths,
+                                             const TSDataType_C* dataTypes,
+                                             const TSEncoding_C* encodings,
+                                             const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createMultiTimeseries(
+            pathsVec, typesVec, encVec, compVec,
+            nullptr, nullptr, nullptr, nullptr);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_aligned_timeseries(CSession* session, const char* deviceId,
+                                               int count,
+                                               const char* const* measurements,
+                                               const TSDataType_C* dataTypes,
+                                               const TSEncoding_C* encodings,
+                                               const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createAlignedTimeseries(
+            std::string(deviceId), measurementsVec, typesVec, encVec, compVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_check_timeseries_exists(CSession* session, const char* path, bool* exists) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!exists) return setError(TS_ERR_INVALID_PARAM, "exists pointer is null");
+    try {
+        *exists = session->cpp->checkTimeseriesExists(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries(CSession* session, const char* path) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteTimeseries(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries_batch(CSession* session, const char* const* paths, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        session->cpp->deleteTimeseries(pathsVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Tablet Operations
+ * ============================================================ */
+
+CTablet* ts_tablet_new(const char* deviceId, int columnCount,
+                        const char* const* columnNames,
+                        const TSDataType_C* dataTypes,
+                        int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        schemas.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTablet* ts_tablet_new_with_category(const char* deviceId, int columnCount,
+                                      const char* const* columnNames,
+                                      const TSDataType_C* dataTypes,
+                                      const TSColumnCategory_C* columnCategories,
+                                      int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        std::vector<ColumnCategory> colTypes;
+        schemas.reserve(columnCount);
+        colTypes.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+            colTypes.push_back(static_cast<ColumnCategory>(columnCategories[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, colTypes, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_tablet_destroy(CTablet* tablet) {
+    delete tablet;
+}
+
+void ts_tablet_reset(CTablet* tablet) {
+    if (tablet) {
+        tablet->cpp.reset();
+    }
+}
+
+int ts_tablet_get_row_count(CTablet* tablet) {
+    if (!tablet) return 0;
+    return static_cast<int>(tablet->cpp.rowSize);
+}
+
+TsStatus ts_tablet_set_row_count(CTablet* tablet, int rowCount) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    tablet->cpp.rowSize = rowCount;
+    return TS_OK;
+}
+
+TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addTimestamp(static_cast<size_t>(rowIndex), timestamp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        std::string str(value);
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), str);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Record, string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_record_str(CSession* session, const char* deviceId,
+                                       int64_t time, int count,
+                                       const char* const* measurements,
+                                       const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_record(CSession* session, const char* deviceId,
+                                   int64_t time, int count,
+                                   const char* const* measurements,
+                                   const TSDataType_C* types,
+                                   const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record_str(CSession* session, const char* deviceId,
+                                               int64_t time, int count,
+                                               const char* const* measurements,
+                                               const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record(CSession* session, const char* deviceId,
+                                           int64_t time, int count,
+                                           const char* const* measurements,
+                                           const TSDataType_C* types,
+                                           const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_str(CSession* session, int deviceCount,
+                                        const char* const* deviceIds,
+                                        const int64_t* times,
+                                        const int* measurementCounts,
+                                        const char* const* const* measurementsList,
+                                        const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_str(CSession* session, int deviceCount,
+                                                const char* const* deviceIds,
+                                                const int64_t* times,
+                                                const int* measurementCounts,
+                                                const char* const* const* measurementsList,
+                                                const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records(CSession* session, int deviceCount,
+                                    const char* const* deviceIds,
+                                    const int64_t* times,
+                                    const int* measurementCounts,
+                                    const char* const* const* measurementsList,
+                                    const TSDataType_C* const* typesList,
+                                    const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records(CSession* session, int deviceCount,
+                                            const char* const* deviceIds,
+                                            const int64_t* times,
+                                            const int* measurementCounts,
+                                            const char* const* const* measurementsList,
+                                            const TSDataType_C* const* typesList,
+                                            const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, single device (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_of_one_device(CSession* session, const char* deviceId,
+                                                  int rowCount, const int64_t* times,
+                                                  const int* measurementCounts,
+                                                  const char* const* const* measurementsList,
+                                                  const TSDataType_C* const* typesList,
+                                                  const void* const* const* valuesList,
+                                                  bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_of_one_device(CSession* session, const char* deviceId,
+                                                          int rowCount, const int64_t* times,
+                                                          const int* measurementCounts,
+                                                          const char* const* const* measurementsList,
+                                                          const TSDataType_C* const* typesList,
+                                                          const void* const* const* valuesList,
+                                                          bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_session_insert_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertAlignedTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_tablets(CSession* session, int tabletCount,
+                                    const char* const* deviceIds,
+                                    CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablets(CSession* session, int tabletCount,
+                                            const char* const* deviceIds,
+                                            CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertAlignedTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Table Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_table_session_insert(CTableSession* session, CTablet* tablet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insert(tablet->cpp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Tree Model
+ * ============================================================ */
+
+TsStatus ts_session_execute_query(CSession* session, const char* sql,
+                                   CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_query_with_timeout(CSession* session, const char* sql,
+                                                int64_t timeoutInMs,
+                                                CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_non_query(CSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_raw_data_query(CSession* session,
+                                            int pathCount, const char* const* paths,
+                                            int64_t startTime, int64_t endTime,
+                                            CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeRawDataQuery(pathsVec, startTime, endTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query(CSession* session,
+                                             int pathCount, const char* const* paths,
+                                             CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query_with_time(CSession* session,
+                                                       int pathCount, const char* const* paths,
+                                                       int64_t lastTime,
+                                                       CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec, lastTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Table Model
+ * ============================================================ */
+
+TsStatus ts_table_session_execute_query(CTableSession* session, const char* sql,
+                                         CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_query_with_timeout(CTableSession* session, const char* sql,
+                                                      int64_t timeoutInMs,
+                                                      CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_non_query(CTableSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  SessionDataSet & RowRecord  —  Result Iteration
+ * ============================================================ */
+
+void ts_dataset_destroy(CSessionDataSet* dataSet) {
+    if (dataSet) {
+        if (dataSet->cpp) {
+            dataSet->cpp->closeOperationHandle();
+        }
+        delete dataSet;
+    }
+}
+
+bool ts_dataset_has_next(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return false;
+    try {
+        return dataSet->cpp->hasNext();
+    } catch (...) {
+        return false;
+    }
+}
+
+CRowRecord* ts_dataset_next(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return nullptr;
+    try {
+        auto row = dataSet->cpp->next();
+        if (!row) return nullptr;
+        auto* crr = new CRowRecord_();
+        crr->cpp = row;
+        return crr;
+    } catch (...) {
+        return nullptr;
+    }
+}
+
+int ts_dataset_get_column_count(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return 0;
+    return static_cast<int>(dataSet->cpp->getColumnNames().size());
+}
+
+static thread_local std::string g_colNameBuf;
+
+const char* ts_dataset_get_column_name(CSessionDataSet* dataSet, int index) {
+    if (!dataSet || !dataSet->cpp) return "";
+    const auto& names = dataSet->cpp->getColumnNames();
+    if (index < 0 || index >= (int)names.size()) return "";
+    g_colNameBuf = names[index];
+    return g_colNameBuf.c_str();
+}
+
+static thread_local std::string g_colTypeBuf;
+
+const char* ts_dataset_get_column_type(CSessionDataSet* dataSet, int index) {
+    if (!dataSet || !dataSet->cpp) return "";
+    const auto& types = dataSet->cpp->getColumnTypeList();
+    if (index < 0 || index >= (int)types.size()) return "";
+    g_colTypeBuf = types[index];
+    return g_colTypeBuf.c_str();
+}
+
+void ts_dataset_set_fetch_size(CSessionDataSet* dataSet, int fetchSize) {
+    if (dataSet && dataSet->cpp) {
+        dataSet->cpp->setFetchSize(fetchSize);
+    }
+}
+
+void ts_row_record_destroy(CRowRecord* record) {
+    delete record;
+}
+
+int64_t ts_row_record_get_timestamp(CRowRecord* record) {
+    if (!record || !record->cpp) return -1;
+    return record->cpp->timestamp;
+}
+
+int ts_row_record_get_field_count(CRowRecord* record) {
+    if (!record || !record->cpp) return 0;
+    return static_cast<int>(record->cpp->fields.size());
+}
+
+bool ts_row_record_is_null(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return true;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return true;
+    const Field& f = record->cpp->fields[index];
+    switch (f.dataType) {
+        case TSDataType::BOOLEAN:   return !f.boolV.is_initialized();
+        case TSDataType::INT32:     return !f.intV.is_initialized();
+        case TSDataType::INT64:
+        case TSDataType::TIMESTAMP: return !f.longV.is_initialized();
+        case TSDataType::FLOAT:     return !f.floatV.is_initialized();
+        case TSDataType::DOUBLE:    return !f.doubleV.is_initialized();
+        case TSDataType::TEXT:
+        case TSDataType::STRING:
+        case TSDataType::BLOB:      return !f.stringV.is_initialized();
+        case TSDataType::DATE:      return !f.dateV.is_initialized();
+        default:                    return true;
+    }
+}
+
+bool ts_row_record_get_bool(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return false;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return false;
+    const Field& f = record->cpp->fields[index];
+    return f.boolV.is_initialized() ? f.boolV.value() : false;
+}
+
+int32_t ts_row_record_get_int32(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return 0;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return 0;
+    const Field& f = record->cpp->fields[index];
+    return f.intV.is_initialized() ? f.intV.value() : 0;
+}
+
+int64_t ts_row_record_get_int64(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return 0;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return 0;
+    const Field& f = record->cpp->fields[index];
+    return f.longV.is_initialized() ? f.longV.value() : 0;
+}
+
+float ts_row_record_get_float(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return 0.0f;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return 0.0f;
+    const Field& f = record->cpp->fields[index];
+    return f.floatV.is_initialized() ? f.floatV.value() : 0.0f;
+}
+
+double ts_row_record_get_double(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return 0.0;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return 0.0;
+    const Field& f = record->cpp->fields[index];
+    return f.doubleV.is_initialized() ? f.doubleV.value() : 0.0;
+}
+
+static thread_local std::string g_stringBuf;
+
+const char* ts_row_record_get_string(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return "";
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return "";
+    const Field& f = record->cpp->fields[index];
+    if (f.stringV.is_initialized()) {
+        g_stringBuf = f.stringV.value();
+        return g_stringBuf.c_str();
+    }
+    return "";
+}
+
+TSDataType_C ts_row_record_get_data_type(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return TS_TYPE_TEXT;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return TS_TYPE_TEXT;

Review Comment:
   TS_TYPE_INVALID?



##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_with_zone(const char* host, int rpcPort,
+                                    const char* username, const char* password,
+                                    const char* zoneId, int fetchSize) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password),
+            std::string(zoneId), fetchSize);
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                     const char* username, const char* password) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(urls, std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_session_destroy(CSession* session) {
+    delete session;
+}
+
+TsStatus ts_session_open(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open(enableRPCCompression);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_close(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Table Model
+ * ============================================================ */
+
+CTableSession* ts_table_session_new(const char* host, int rpcPort,
+                                     const char* username, const char* password,
+                                     const char* database) {
+    clearError();
+    try {
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->host(std::string(host))
+            ->rpcPort(rpcPort)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                                const char* username, const char* password,
+                                                const char* database) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->nodeUrls(urls)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_table_session_destroy(CTableSession* session) {
+    delete session;
+}
+
+TsStatus ts_table_session_open(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_close(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timezone
+ * ============================================================ */
+
+TsStatus ts_session_set_timezone(CSession* session, const char* zoneId) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->setTimeZone(std::string(zoneId));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!buf || bufLen <= 0) return setError(TS_ERR_INVALID_PARAM, "invalid buffer");
+    try {
+        std::string tz = session->cpp->getTimeZone();
+        if ((int)tz.size() >= bufLen) {
+            return setError(TS_ERR_INVALID_PARAM, "buffer too small");
+        }
+        strncpy(buf, tz.c_str(), bufLen);
+        buf[bufLen - 1] = '\0';
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Database Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_databases(CSession* session, const char* const* databases, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto dbs = toStringVec(databases, count);
+        session->cpp->deleteDatabases(dbs);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timeseries Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_timeseries(CSession* session, const char* path,
+                                       TSDataType_C dataType, TSEncoding_C encoding,
+                                       TSCompressionType_C compressor) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_timeseries_ex(CSession* session, const char* path,
+                                          TSDataType_C dataType, TSEncoding_C encoding,
+                                          TSCompressionType_C compressor,
+                                          int propsCount,
+                                          const char* const* propKeys,
+                                          const char* const* propValues,
+                                          int tagsCount,
+                                          const char* const* tagKeys,
+                                          const char* const* tagValues,
+                                          int attrsCount,
+                                          const char* const* attrKeys,
+                                          const char* const* attrValues,
+                                          const char* measurementAlias) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::map<std::string, std::string> props = propsCount > 0 ? toStringMap(propsCount, propKeys, propValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> tags = tagsCount > 0 ? toStringMap(tagsCount, tagKeys, tagValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> attrs = attrsCount > 0 ? toStringMap(attrsCount, attrKeys, attrValues) : std::map<std::string, std::string>();
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor),
+            &props, &tags, &attrs,
+            std::string(measurementAlias ? measurementAlias : ""));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_multi_timeseries(CSession* session, int count,
+                                             const char* const* paths,
+                                             const TSDataType_C* dataTypes,
+                                             const TSEncoding_C* encodings,
+                                             const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createMultiTimeseries(
+            pathsVec, typesVec, encVec, compVec,
+            nullptr, nullptr, nullptr, nullptr);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_aligned_timeseries(CSession* session, const char* deviceId,
+                                               int count,
+                                               const char* const* measurements,
+                                               const TSDataType_C* dataTypes,
+                                               const TSEncoding_C* encodings,
+                                               const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createAlignedTimeseries(
+            std::string(deviceId), measurementsVec, typesVec, encVec, compVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_check_timeseries_exists(CSession* session, const char* path, bool* exists) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!exists) return setError(TS_ERR_INVALID_PARAM, "exists pointer is null");
+    try {
+        *exists = session->cpp->checkTimeseriesExists(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries(CSession* session, const char* path) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteTimeseries(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries_batch(CSession* session, const char* const* paths, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        session->cpp->deleteTimeseries(pathsVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Tablet Operations
+ * ============================================================ */
+
+CTablet* ts_tablet_new(const char* deviceId, int columnCount,
+                        const char* const* columnNames,
+                        const TSDataType_C* dataTypes,
+                        int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        schemas.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTablet* ts_tablet_new_with_category(const char* deviceId, int columnCount,
+                                      const char* const* columnNames,
+                                      const TSDataType_C* dataTypes,
+                                      const TSColumnCategory_C* columnCategories,
+                                      int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        std::vector<ColumnCategory> colTypes;
+        schemas.reserve(columnCount);
+        colTypes.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+            colTypes.push_back(static_cast<ColumnCategory>(columnCategories[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, colTypes, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_tablet_destroy(CTablet* tablet) {
+    delete tablet;
+}
+
+void ts_tablet_reset(CTablet* tablet) {
+    if (tablet) {
+        tablet->cpp.reset();
+    }
+}
+
+int ts_tablet_get_row_count(CTablet* tablet) {
+    if (!tablet) return 0;
+    return static_cast<int>(tablet->cpp.rowSize);
+}
+
+TsStatus ts_tablet_set_row_count(CTablet* tablet, int rowCount) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    tablet->cpp.rowSize = rowCount;
+    return TS_OK;
+}
+
+TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addTimestamp(static_cast<size_t>(rowIndex), timestamp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        std::string str(value);
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), str);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Record, string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_record_str(CSession* session, const char* deviceId,
+                                       int64_t time, int count,
+                                       const char* const* measurements,
+                                       const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_record(CSession* session, const char* deviceId,
+                                   int64_t time, int count,
+                                   const char* const* measurements,
+                                   const TSDataType_C* types,
+                                   const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record_str(CSession* session, const char* deviceId,
+                                               int64_t time, int count,
+                                               const char* const* measurements,
+                                               const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record(CSession* session, const char* deviceId,
+                                           int64_t time, int count,
+                                           const char* const* measurements,
+                                           const TSDataType_C* types,
+                                           const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_str(CSession* session, int deviceCount,
+                                        const char* const* deviceIds,
+                                        const int64_t* times,
+                                        const int* measurementCounts,
+                                        const char* const* const* measurementsList,
+                                        const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_str(CSession* session, int deviceCount,
+                                                const char* const* deviceIds,
+                                                const int64_t* times,
+                                                const int* measurementCounts,
+                                                const char* const* const* measurementsList,
+                                                const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records(CSession* session, int deviceCount,
+                                    const char* const* deviceIds,
+                                    const int64_t* times,
+                                    const int* measurementCounts,
+                                    const char* const* const* measurementsList,
+                                    const TSDataType_C* const* typesList,
+                                    const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records(CSession* session, int deviceCount,
+                                            const char* const* deviceIds,
+                                            const int64_t* times,
+                                            const int* measurementCounts,
+                                            const char* const* const* measurementsList,
+                                            const TSDataType_C* const* typesList,
+                                            const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, single device (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_of_one_device(CSession* session, const char* deviceId,
+                                                  int rowCount, const int64_t* times,
+                                                  const int* measurementCounts,
+                                                  const char* const* const* measurementsList,
+                                                  const TSDataType_C* const* typesList,
+                                                  const void* const* const* valuesList,
+                                                  bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_of_one_device(CSession* session, const char* deviceId,
+                                                          int rowCount, const int64_t* times,
+                                                          const int* measurementCounts,
+                                                          const char* const* const* measurementsList,
+                                                          const TSDataType_C* const* typesList,
+                                                          const void* const* const* valuesList,
+                                                          bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_session_insert_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertAlignedTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_tablets(CSession* session, int tabletCount,
+                                    const char* const* deviceIds,
+                                    CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablets(CSession* session, int tabletCount,
+                                            const char* const* deviceIds,
+                                            CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertAlignedTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Table Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_table_session_insert(CTableSession* session, CTablet* tablet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insert(tablet->cpp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Tree Model
+ * ============================================================ */
+
+TsStatus ts_session_execute_query(CSession* session, const char* sql,
+                                   CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_query_with_timeout(CSession* session, const char* sql,
+                                                int64_t timeoutInMs,
+                                                CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_non_query(CSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_raw_data_query(CSession* session,
+                                            int pathCount, const char* const* paths,
+                                            int64_t startTime, int64_t endTime,
+                                            CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeRawDataQuery(pathsVec, startTime, endTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query(CSession* session,
+                                             int pathCount, const char* const* paths,
+                                             CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query_with_time(CSession* session,
+                                                       int pathCount, const char* const* paths,
+                                                       int64_t lastTime,
+                                                       CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec, lastTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Table Model
+ * ============================================================ */
+
+TsStatus ts_table_session_execute_query(CTableSession* session, const char* sql,
+                                         CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_query_with_timeout(CTableSession* session, const char* sql,
+                                                      int64_t timeoutInMs,
+                                                      CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_non_query(CTableSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  SessionDataSet & RowRecord  —  Result Iteration
+ * ============================================================ */
+
+void ts_dataset_destroy(CSessionDataSet* dataSet) {
+    if (dataSet) {
+        if (dataSet->cpp) {
+            dataSet->cpp->closeOperationHandle();
+        }
+        delete dataSet;
+    }
+}
+
+bool ts_dataset_has_next(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return false;
+    try {
+        return dataSet->cpp->hasNext();
+    } catch (...) {
+        return false;
+    }
+}

Review Comment:
   global error?
   The same below.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "hongzhi-gao (via GitHub)" <gi...@apache.org>.
hongzhi-gao commented on code in PR #17347:
URL: https://github.com/apache/iotdb/pull/17347#discussion_r3070878361


##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_with_zone(const char* host, int rpcPort,
+                                    const char* username, const char* password,
+                                    const char* zoneId, int fetchSize) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password),
+            std::string(zoneId), fetchSize);
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                     const char* username, const char* password) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(urls, std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_session_destroy(CSession* session) {
+    delete session;
+}
+
+TsStatus ts_session_open(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open(enableRPCCompression);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_close(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Table Model
+ * ============================================================ */
+
+CTableSession* ts_table_session_new(const char* host, int rpcPort,
+                                     const char* username, const char* password,
+                                     const char* database) {
+    clearError();
+    try {
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->host(std::string(host))
+            ->rpcPort(rpcPort)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                                const char* username, const char* password,
+                                                const char* database) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->nodeUrls(urls)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_table_session_destroy(CTableSession* session) {
+    delete session;
+}
+
+TsStatus ts_table_session_open(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_close(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timezone
+ * ============================================================ */
+
+TsStatus ts_session_set_timezone(CSession* session, const char* zoneId) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->setTimeZone(std::string(zoneId));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!buf || bufLen <= 0) return setError(TS_ERR_INVALID_PARAM, "invalid buffer");
+    try {
+        std::string tz = session->cpp->getTimeZone();
+        if ((int)tz.size() >= bufLen) {
+            return setError(TS_ERR_INVALID_PARAM, "buffer too small");
+        }
+        strncpy(buf, tz.c_str(), bufLen);
+        buf[bufLen - 1] = '\0';
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Database Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_databases(CSession* session, const char* const* databases, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto dbs = toStringVec(databases, count);
+        session->cpp->deleteDatabases(dbs);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timeseries Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_timeseries(CSession* session, const char* path,
+                                       TSDataType_C dataType, TSEncoding_C encoding,
+                                       TSCompressionType_C compressor) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_timeseries_ex(CSession* session, const char* path,
+                                          TSDataType_C dataType, TSEncoding_C encoding,
+                                          TSCompressionType_C compressor,
+                                          int propsCount,
+                                          const char* const* propKeys,
+                                          const char* const* propValues,
+                                          int tagsCount,
+                                          const char* const* tagKeys,
+                                          const char* const* tagValues,
+                                          int attrsCount,
+                                          const char* const* attrKeys,
+                                          const char* const* attrValues,
+                                          const char* measurementAlias) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::map<std::string, std::string> props = propsCount > 0 ? toStringMap(propsCount, propKeys, propValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> tags = tagsCount > 0 ? toStringMap(tagsCount, tagKeys, tagValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> attrs = attrsCount > 0 ? toStringMap(attrsCount, attrKeys, attrValues) : std::map<std::string, std::string>();
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor),
+            &props, &tags, &attrs,
+            std::string(measurementAlias ? measurementAlias : ""));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_multi_timeseries(CSession* session, int count,
+                                             const char* const* paths,
+                                             const TSDataType_C* dataTypes,
+                                             const TSEncoding_C* encodings,
+                                             const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createMultiTimeseries(
+            pathsVec, typesVec, encVec, compVec,
+            nullptr, nullptr, nullptr, nullptr);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_aligned_timeseries(CSession* session, const char* deviceId,
+                                               int count,
+                                               const char* const* measurements,
+                                               const TSDataType_C* dataTypes,
+                                               const TSEncoding_C* encodings,
+                                               const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createAlignedTimeseries(
+            std::string(deviceId), measurementsVec, typesVec, encVec, compVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_check_timeseries_exists(CSession* session, const char* path, bool* exists) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!exists) return setError(TS_ERR_INVALID_PARAM, "exists pointer is null");
+    try {
+        *exists = session->cpp->checkTimeseriesExists(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries(CSession* session, const char* path) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteTimeseries(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries_batch(CSession* session, const char* const* paths, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        session->cpp->deleteTimeseries(pathsVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Tablet Operations
+ * ============================================================ */
+
+CTablet* ts_tablet_new(const char* deviceId, int columnCount,
+                        const char* const* columnNames,
+                        const TSDataType_C* dataTypes,
+                        int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        schemas.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTablet* ts_tablet_new_with_category(const char* deviceId, int columnCount,
+                                      const char* const* columnNames,
+                                      const TSDataType_C* dataTypes,
+                                      const TSColumnCategory_C* columnCategories,
+                                      int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        std::vector<ColumnCategory> colTypes;
+        schemas.reserve(columnCount);
+        colTypes.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+            colTypes.push_back(static_cast<ColumnCategory>(columnCategories[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, colTypes, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_tablet_destroy(CTablet* tablet) {
+    delete tablet;
+}
+
+void ts_tablet_reset(CTablet* tablet) {
+    if (tablet) {
+        tablet->cpp.reset();
+    }
+}
+
+int ts_tablet_get_row_count(CTablet* tablet) {
+    if (!tablet) return 0;
+    return static_cast<int>(tablet->cpp.rowSize);
+}
+
+TsStatus ts_tablet_set_row_count(CTablet* tablet, int rowCount) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    tablet->cpp.rowSize = rowCount;
+    return TS_OK;
+}
+
+TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addTimestamp(static_cast<size_t>(rowIndex), timestamp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        std::string str(value);
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), str);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Record, string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_record_str(CSession* session, const char* deviceId,
+                                       int64_t time, int count,
+                                       const char* const* measurements,
+                                       const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_record(CSession* session, const char* deviceId,
+                                   int64_t time, int count,
+                                   const char* const* measurements,
+                                   const TSDataType_C* types,
+                                   const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record_str(CSession* session, const char* deviceId,
+                                               int64_t time, int count,
+                                               const char* const* measurements,
+                                               const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record(CSession* session, const char* deviceId,
+                                           int64_t time, int count,
+                                           const char* const* measurements,
+                                           const TSDataType_C* types,
+                                           const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_str(CSession* session, int deviceCount,
+                                        const char* const* deviceIds,
+                                        const int64_t* times,
+                                        const int* measurementCounts,
+                                        const char* const* const* measurementsList,
+                                        const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_str(CSession* session, int deviceCount,
+                                                const char* const* deviceIds,
+                                                const int64_t* times,
+                                                const int* measurementCounts,
+                                                const char* const* const* measurementsList,
+                                                const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records(CSession* session, int deviceCount,
+                                    const char* const* deviceIds,
+                                    const int64_t* times,
+                                    const int* measurementCounts,
+                                    const char* const* const* measurementsList,
+                                    const TSDataType_C* const* typesList,
+                                    const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records(CSession* session, int deviceCount,
+                                            const char* const* deviceIds,
+                                            const int64_t* times,
+                                            const int* measurementCounts,
+                                            const char* const* const* measurementsList,
+                                            const TSDataType_C* const* typesList,
+                                            const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, single device (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_of_one_device(CSession* session, const char* deviceId,
+                                                  int rowCount, const int64_t* times,
+                                                  const int* measurementCounts,
+                                                  const char* const* const* measurementsList,
+                                                  const TSDataType_C* const* typesList,
+                                                  const void* const* const* valuesList,
+                                                  bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_of_one_device(CSession* session, const char* deviceId,
+                                                          int rowCount, const int64_t* times,
+                                                          const int* measurementCounts,
+                                                          const char* const* const* measurementsList,
+                                                          const TSDataType_C* const* typesList,
+                                                          const void* const* const* valuesList,
+                                                          bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_session_insert_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertAlignedTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_tablets(CSession* session, int tabletCount,
+                                    const char* const* deviceIds,
+                                    CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablets(CSession* session, int tabletCount,
+                                            const char* const* deviceIds,
+                                            CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertAlignedTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Table Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_table_session_insert(CTableSession* session, CTablet* tablet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insert(tablet->cpp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Tree Model
+ * ============================================================ */
+
+TsStatus ts_session_execute_query(CSession* session, const char* sql,
+                                   CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_query_with_timeout(CSession* session, const char* sql,
+                                                int64_t timeoutInMs,
+                                                CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_non_query(CSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_raw_data_query(CSession* session,
+                                            int pathCount, const char* const* paths,
+                                            int64_t startTime, int64_t endTime,
+                                            CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeRawDataQuery(pathsVec, startTime, endTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query(CSession* session,
+                                             int pathCount, const char* const* paths,
+                                             CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query_with_time(CSession* session,
+                                                       int pathCount, const char* const* paths,
+                                                       int64_t lastTime,
+                                                       CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec, lastTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Table Model
+ * ============================================================ */
+
+TsStatus ts_table_session_execute_query(CTableSession* session, const char* sql,
+                                         CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_query_with_timeout(CTableSession* session, const char* sql,
+                                                      int64_t timeoutInMs,
+                                                      CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_non_query(CTableSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  SessionDataSet & RowRecord  —  Result Iteration
+ * ============================================================ */
+
+void ts_dataset_destroy(CSessionDataSet* dataSet) {
+    if (dataSet) {
+        if (dataSet->cpp) {
+            dataSet->cpp->closeOperationHandle();
+        }
+        delete dataSet;
+    }
+}
+
+bool ts_dataset_has_next(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return false;
+    try {
+        return dataSet->cpp->hasNext();
+    } catch (...) {
+        return false;
+    }
+}
+
+CRowRecord* ts_dataset_next(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return nullptr;
+    try {
+        auto row = dataSet->cpp->next();
+        if (!row) return nullptr;
+        auto* crr = new CRowRecord_();
+        crr->cpp = row;
+        return crr;
+    } catch (...) {
+        return nullptr;
+    }
+}
+
+int ts_dataset_get_column_count(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return 0;
+    return static_cast<int>(dataSet->cpp->getColumnNames().size());
+}
+
+static thread_local std::string g_colNameBuf;
+
+const char* ts_dataset_get_column_name(CSessionDataSet* dataSet, int index) {
+    if (!dataSet || !dataSet->cpp) return "";
+    const auto& names = dataSet->cpp->getColumnNames();
+    if (index < 0 || index >= (int)names.size()) return "";
+    g_colNameBuf = names[index];
+    return g_colNameBuf.c_str();
+}
+
+static thread_local std::string g_colTypeBuf;
+
+const char* ts_dataset_get_column_type(CSessionDataSet* dataSet, int index) {
+    if (!dataSet || !dataSet->cpp) return "";
+    const auto& types = dataSet->cpp->getColumnTypeList();
+    if (index < 0 || index >= (int)types.size()) return "";
+    g_colTypeBuf = types[index];
+    return g_colTypeBuf.c_str();
+}
+
+void ts_dataset_set_fetch_size(CSessionDataSet* dataSet, int fetchSize) {
+    if (dataSet && dataSet->cpp) {
+        dataSet->cpp->setFetchSize(fetchSize);
+    }
+}
+
+void ts_row_record_destroy(CRowRecord* record) {
+    delete record;
+}
+
+int64_t ts_row_record_get_timestamp(CRowRecord* record) {
+    if (!record || !record->cpp) return -1;
+    return record->cpp->timestamp;
+}
+
+int ts_row_record_get_field_count(CRowRecord* record) {
+    if (!record || !record->cpp) return 0;
+    return static_cast<int>(record->cpp->fields.size());
+}
+
+bool ts_row_record_is_null(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return true;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return true;
+    const Field& f = record->cpp->fields[index];
+    switch (f.dataType) {
+        case TSDataType::BOOLEAN:   return !f.boolV.is_initialized();
+        case TSDataType::INT32:     return !f.intV.is_initialized();
+        case TSDataType::INT64:
+        case TSDataType::TIMESTAMP: return !f.longV.is_initialized();
+        case TSDataType::FLOAT:     return !f.floatV.is_initialized();
+        case TSDataType::DOUBLE:    return !f.doubleV.is_initialized();
+        case TSDataType::TEXT:
+        case TSDataType::STRING:
+        case TSDataType::BLOB:      return !f.stringV.is_initialized();
+        case TSDataType::DATE:      return !f.dateV.is_initialized();
+        default:                    return true;
+    }
+}
+
+bool ts_row_record_get_bool(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return false;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return false;
+    const Field& f = record->cpp->fields[index];
+    return f.boolV.is_initialized() ? f.boolV.value() : false;
+}
+
+int32_t ts_row_record_get_int32(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return 0;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return 0;
+    const Field& f = record->cpp->fields[index];
+    return f.intV.is_initialized() ? f.intV.value() : 0;
+}
+
+int64_t ts_row_record_get_int64(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return 0;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return 0;
+    const Field& f = record->cpp->fields[index];
+    return f.longV.is_initialized() ? f.longV.value() : 0;
+}
+
+float ts_row_record_get_float(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return 0.0f;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return 0.0f;
+    const Field& f = record->cpp->fields[index];
+    return f.floatV.is_initialized() ? f.floatV.value() : 0.0f;
+}
+
+double ts_row_record_get_double(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return 0.0;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return 0.0;
+    const Field& f = record->cpp->fields[index];
+    return f.doubleV.is_initialized() ? f.doubleV.value() : 0.0;
+}
+
+static thread_local std::string g_stringBuf;
+
+const char* ts_row_record_get_string(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return "";
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return "";
+    const Field& f = record->cpp->fields[index];
+    if (f.stringV.is_initialized()) {
+        g_stringBuf = f.stringV.value();
+        return g_stringBuf.c_str();
+    }
+    return "";
+}
+
+TSDataType_C ts_row_record_get_data_type(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return TS_TYPE_TEXT;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return TS_TYPE_TEXT;

Review Comment:
   Done. Added TS_TYPE_INVALID (255) to TSDataType_C



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "hongzhi-gao (via GitHub)" <gi...@apache.org>.
hongzhi-gao commented on code in PR #17347:
URL: https://github.com/apache/iotdb/pull/17347#discussion_r3070874023


##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_with_zone(const char* host, int rpcPort,
+                                    const char* username, const char* password,
+                                    const char* zoneId, int fetchSize) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password),
+            std::string(zoneId), fetchSize);
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                     const char* username, const char* password) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(urls, std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_session_destroy(CSession* session) {
+    delete session;
+}
+
+TsStatus ts_session_open(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open(enableRPCCompression);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_close(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Table Model
+ * ============================================================ */
+
+CTableSession* ts_table_session_new(const char* host, int rpcPort,
+                                     const char* username, const char* password,
+                                     const char* database) {
+    clearError();
+    try {
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->host(std::string(host))
+            ->rpcPort(rpcPort)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                                const char* username, const char* password,
+                                                const char* database) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->nodeUrls(urls)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_table_session_destroy(CTableSession* session) {
+    delete session;
+}
+
+TsStatus ts_table_session_open(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_close(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timezone
+ * ============================================================ */
+
+TsStatus ts_session_set_timezone(CSession* session, const char* zoneId) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->setTimeZone(std::string(zoneId));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!buf || bufLen <= 0) return setError(TS_ERR_INVALID_PARAM, "invalid buffer");
+    try {
+        std::string tz = session->cpp->getTimeZone();
+        if ((int)tz.size() >= bufLen) {
+            return setError(TS_ERR_INVALID_PARAM, "buffer too small");
+        }
+        strncpy(buf, tz.c_str(), bufLen);
+        buf[bufLen - 1] = '\0';
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Database Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_databases(CSession* session, const char* const* databases, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto dbs = toStringVec(databases, count);
+        session->cpp->deleteDatabases(dbs);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timeseries Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_timeseries(CSession* session, const char* path,
+                                       TSDataType_C dataType, TSEncoding_C encoding,
+                                       TSCompressionType_C compressor) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_timeseries_ex(CSession* session, const char* path,
+                                          TSDataType_C dataType, TSEncoding_C encoding,
+                                          TSCompressionType_C compressor,
+                                          int propsCount,
+                                          const char* const* propKeys,
+                                          const char* const* propValues,
+                                          int tagsCount,
+                                          const char* const* tagKeys,
+                                          const char* const* tagValues,
+                                          int attrsCount,
+                                          const char* const* attrKeys,
+                                          const char* const* attrValues,
+                                          const char* measurementAlias) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::map<std::string, std::string> props = propsCount > 0 ? toStringMap(propsCount, propKeys, propValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> tags = tagsCount > 0 ? toStringMap(tagsCount, tagKeys, tagValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> attrs = attrsCount > 0 ? toStringMap(attrsCount, attrKeys, attrValues) : std::map<std::string, std::string>();
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor),
+            &props, &tags, &attrs,
+            std::string(measurementAlias ? measurementAlias : ""));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_multi_timeseries(CSession* session, int count,
+                                             const char* const* paths,
+                                             const TSDataType_C* dataTypes,
+                                             const TSEncoding_C* encodings,
+                                             const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createMultiTimeseries(
+            pathsVec, typesVec, encVec, compVec,
+            nullptr, nullptr, nullptr, nullptr);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_aligned_timeseries(CSession* session, const char* deviceId,
+                                               int count,
+                                               const char* const* measurements,
+                                               const TSDataType_C* dataTypes,
+                                               const TSEncoding_C* encodings,
+                                               const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createAlignedTimeseries(
+            std::string(deviceId), measurementsVec, typesVec, encVec, compVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_check_timeseries_exists(CSession* session, const char* path, bool* exists) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!exists) return setError(TS_ERR_INVALID_PARAM, "exists pointer is null");
+    try {
+        *exists = session->cpp->checkTimeseriesExists(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries(CSession* session, const char* path) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteTimeseries(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries_batch(CSession* session, const char* const* paths, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        session->cpp->deleteTimeseries(pathsVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Tablet Operations
+ * ============================================================ */
+
+CTablet* ts_tablet_new(const char* deviceId, int columnCount,
+                        const char* const* columnNames,
+                        const TSDataType_C* dataTypes,
+                        int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        schemas.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTablet* ts_tablet_new_with_category(const char* deviceId, int columnCount,
+                                      const char* const* columnNames,
+                                      const TSDataType_C* dataTypes,
+                                      const TSColumnCategory_C* columnCategories,
+                                      int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        std::vector<ColumnCategory> colTypes;
+        schemas.reserve(columnCount);
+        colTypes.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+            colTypes.push_back(static_cast<ColumnCategory>(columnCategories[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, colTypes, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_tablet_destroy(CTablet* tablet) {
+    delete tablet;
+}
+
+void ts_tablet_reset(CTablet* tablet) {
+    if (tablet) {
+        tablet->cpp.reset();
+    }
+}
+
+int ts_tablet_get_row_count(CTablet* tablet) {
+    if (!tablet) return 0;
+    return static_cast<int>(tablet->cpp.rowSize);
+}
+
+TsStatus ts_tablet_set_row_count(CTablet* tablet, int rowCount) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    tablet->cpp.rowSize = rowCount;
+    return TS_OK;
+}
+
+TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addTimestamp(static_cast<size_t>(rowIndex), timestamp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        std::string str(value);
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), str);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Record, string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_record_str(CSession* session, const char* deviceId,
+                                       int64_t time, int count,
+                                       const char* const* measurements,
+                                       const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_record(CSession* session, const char* deviceId,
+                                   int64_t time, int count,
+                                   const char* const* measurements,
+                                   const TSDataType_C* types,
+                                   const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record_str(CSession* session, const char* deviceId,
+                                               int64_t time, int count,
+                                               const char* const* measurements,
+                                               const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record(CSession* session, const char* deviceId,
+                                           int64_t time, int count,
+                                           const char* const* measurements,
+                                           const TSDataType_C* types,
+                                           const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_str(CSession* session, int deviceCount,
+                                        const char* const* deviceIds,
+                                        const int64_t* times,
+                                        const int* measurementCounts,
+                                        const char* const* const* measurementsList,
+                                        const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_str(CSession* session, int deviceCount,
+                                                const char* const* deviceIds,
+                                                const int64_t* times,
+                                                const int* measurementCounts,
+                                                const char* const* const* measurementsList,
+                                                const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records(CSession* session, int deviceCount,
+                                    const char* const* deviceIds,
+                                    const int64_t* times,
+                                    const int* measurementCounts,
+                                    const char* const* const* measurementsList,
+                                    const TSDataType_C* const* typesList,
+                                    const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records(CSession* session, int deviceCount,
+                                            const char* const* deviceIds,
+                                            const int64_t* times,
+                                            const int* measurementCounts,
+                                            const char* const* const* measurementsList,
+                                            const TSDataType_C* const* typesList,
+                                            const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, single device (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_of_one_device(CSession* session, const char* deviceId,
+                                                  int rowCount, const int64_t* times,
+                                                  const int* measurementCounts,
+                                                  const char* const* const* measurementsList,
+                                                  const TSDataType_C* const* typesList,
+                                                  const void* const* const* valuesList,
+                                                  bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_of_one_device(CSession* session, const char* deviceId,
+                                                          int rowCount, const int64_t* times,
+                                                          const int* measurementCounts,
+                                                          const char* const* const* measurementsList,
+                                                          const TSDataType_C* const* typesList,
+                                                          const void* const* const* valuesList,
+                                                          bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_session_insert_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertAlignedTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_tablets(CSession* session, int tabletCount,
+                                    const char* const* deviceIds,
+                                    CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablets(CSession* session, int tabletCount,
+                                            const char* const* deviceIds,
+                                            CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertAlignedTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Table Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_table_session_insert(CTableSession* session, CTablet* tablet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insert(tablet->cpp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Tree Model
+ * ============================================================ */
+
+TsStatus ts_session_execute_query(CSession* session, const char* sql,
+                                   CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_query_with_timeout(CSession* session, const char* sql,
+                                                int64_t timeoutInMs,
+                                                CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_non_query(CSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_raw_data_query(CSession* session,
+                                            int pathCount, const char* const* paths,
+                                            int64_t startTime, int64_t endTime,
+                                            CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeRawDataQuery(pathsVec, startTime, endTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query(CSession* session,
+                                             int pathCount, const char* const* paths,
+                                             CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query_with_time(CSession* session,
+                                                       int pathCount, const char* const* paths,
+                                                       int64_t lastTime,
+                                                       CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec, lastTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Table Model
+ * ============================================================ */
+
+TsStatus ts_table_session_execute_query(CTableSession* session, const char* sql,
+                                         CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_query_with_timeout(CTableSession* session, const char* sql,
+                                                      int64_t timeoutInMs,
+                                                      CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_non_query(CTableSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  SessionDataSet & RowRecord  —  Result Iteration
+ * ============================================================ */
+
+void ts_dataset_destroy(CSessionDataSet* dataSet) {
+    if (dataSet) {
+        if (dataSet->cpp) {
+            dataSet->cpp->closeOperationHandle();
+        }
+        delete dataSet;
+    }
+}
+
+bool ts_dataset_has_next(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return false;
+    try {
+        return dataSet->cpp->hasNext();
+    } catch (...) {
+        return false;
+    }
+}
+
+CRowRecord* ts_dataset_next(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return nullptr;
+    try {
+        auto row = dataSet->cpp->next();
+        if (!row) return nullptr;
+        auto* crr = new CRowRecord_();
+        crr->cpp = row;
+        return crr;
+    } catch (...) {
+        return nullptr;
+    }
+}
+
+int ts_dataset_get_column_count(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return 0;
+    return static_cast<int>(dataSet->cpp->getColumnNames().size());
+}
+
+static thread_local std::string g_colNameBuf;
+
+const char* ts_dataset_get_column_name(CSessionDataSet* dataSet, int index) {
+    if (!dataSet || !dataSet->cpp) return "";
+    const auto& names = dataSet->cpp->getColumnNames();
+    if (index < 0 || index >= (int)names.size()) return "";
+    g_colNameBuf = names[index];
+    return g_colNameBuf.c_str();
+}
+
+static thread_local std::string g_colTypeBuf;
+
+const char* ts_dataset_get_column_type(CSessionDataSet* dataSet, int index) {
+    if (!dataSet || !dataSet->cpp) return "";
+    const auto& types = dataSet->cpp->getColumnTypeList();
+    if (index < 0 || index >= (int)types.size()) return "";
+    g_colTypeBuf = types[index];
+    return g_colTypeBuf.c_str();
+}
+
+void ts_dataset_set_fetch_size(CSessionDataSet* dataSet, int fetchSize) {
+    if (dataSet && dataSet->cpp) {
+        dataSet->cpp->setFetchSize(fetchSize);
+    }
+}
+
+void ts_row_record_destroy(CRowRecord* record) {
+    delete record;
+}
+
+int64_t ts_row_record_get_timestamp(CRowRecord* record) {
+    if (!record || !record->cpp) return -1;
+    return record->cpp->timestamp;
+}
+
+int ts_row_record_get_field_count(CRowRecord* record) {
+    if (!record || !record->cpp) return 0;
+    return static_cast<int>(record->cpp->fields.size());
+}
+
+bool ts_row_record_is_null(CRowRecord* record, int index) {
+    if (!record || !record->cpp) return true;
+    if (index < 0 || index >= (int)record->cpp->fields.size()) return true;
+    const Field& f = record->cpp->fields[index];
+    switch (f.dataType) {
+        case TSDataType::BOOLEAN:   return !f.boolV.is_initialized();
+        case TSDataType::INT32:     return !f.intV.is_initialized();
+        case TSDataType::INT64:
+        case TSDataType::TIMESTAMP: return !f.longV.is_initialized();
+        case TSDataType::FLOAT:     return !f.floatV.is_initialized();
+        case TSDataType::DOUBLE:    return !f.doubleV.is_initialized();
+        case TSDataType::TEXT:
+        case TSDataType::STRING:
+        case TSDataType::BLOB:      return !f.stringV.is_initialized();
+        case TSDataType::DATE:      return !f.dateV.is_initialized();
+        default:                    return true;
+    }
+}

Review Comment:
   Added Field::isNull() in Common.h (same logic as the previous C helper), ts_row_record_is_null now delegates to it. RowRecord::toString uses isNull() where it matches; 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "jt2594838 (via GitHub)" <gi...@apache.org>.
jt2594838 commented on code in PR #17347:
URL: https://github.com/apache/iotdb/pull/17347#discussion_r3071537275


##########
example/client-c-example/src/table_example.c:
##########
@@ -0,0 +1,151 @@
+/**
+ * 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.
+ */
+
+/*
+ * Table model: CREATE DATABASE/TABLE, insert rows via Tablet, SELECT, DROP DATABASE.
+ * Requires IoTDB table-SQL support. Edit HOST / PORT / credentials below.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "SessionC.h"
+
+#define HOST "127.0.0.1"
+#define PORT 6667
+#define USER "root"
+#define PASS "root"
+
+#define DB_NAME "cdemo_db"
+#define TABLE_NAME "cdemo_t0"
+
+static void fail(const char* ctx, CTableSession* s) {
+    fprintf(stderr, "[table_example] %s failed: %s\n", ctx, ts_get_last_error());
+    if (s) {
+        ts_table_session_close(s);
+        ts_table_session_destroy(s);
+    }
+    exit(1);
+}
+
+int main(void) {
+    CTableSession* session = ts_table_session_new(HOST, PORT, USER, PASS, "");

Review Comment:
   Add a comment to explain what the last "" is.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "hongzhi-gao (via GitHub)" <gi...@apache.org>.
hongzhi-gao commented on code in PR #17347:
URL: https://github.com/apache/iotdb/pull/17347#discussion_r3070819832


##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_with_zone(const char* host, int rpcPort,
+                                    const char* username, const char* password,
+                                    const char* zoneId, int fetchSize) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password),
+            std::string(zoneId), fetchSize);
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                     const char* username, const char* password) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(urls, std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_session_destroy(CSession* session) {
+    delete session;
+}
+
+TsStatus ts_session_open(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open(enableRPCCompression);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_close(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Table Model
+ * ============================================================ */
+
+CTableSession* ts_table_session_new(const char* host, int rpcPort,
+                                     const char* username, const char* password,
+                                     const char* database) {
+    clearError();
+    try {
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->host(std::string(host))
+            ->rpcPort(rpcPort)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                                const char* username, const char* password,
+                                                const char* database) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->nodeUrls(urls)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_table_session_destroy(CTableSession* session) {
+    delete session;
+}

Review Comment:
   delete applied to a nullptr has no effect and is well-defined behavior according to the C++ standard.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "hongzhi-gao (via GitHub)" <gi...@apache.org>.
hongzhi-gao commented on code in PR #17347:
URL: https://github.com/apache/iotdb/pull/17347#discussion_r3070815578


##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }

Review Comment:
   If `new CSession_()` succeeds but the `make_shared/Session` construction throws an exception, the old code’s `catch` block would not delete `cs`, resulting in a memory leak. **This has now been fixed** by first completing all potentially failing C++ constructions, and only then allocating the C-side wrapper (similar patterns for datasets/row records and other cases have already been aligned using stack-based temporary objects).



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "hongzhi-gao (via GitHub)" <gi...@apache.org>.
hongzhi-gao commented on code in PR #17347:
URL: https://github.com/apache/iotdb/pull/17347#discussion_r3070855193


##########
iotdb-client/client-cpp/src/main/SessionC.cpp:
##########
@@ -0,0 +1,1421 @@
+/**
+ * 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.
+ */
+
+#include "SessionC.h"
+#include "Session.h"
+#include "TableSession.h"
+#include "TableSessionBuilder.h"
+#include "SessionBuilder.h"
+#include "SessionDataSet.h"
+
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <memory>
+
+/* ============================================================
+ *  Internal wrapper structs — the opaque handles point to these
+ * ============================================================ */
+
+struct CSession_ {
+    std::shared_ptr<Session> cpp;
+};
+
+struct CTableSession_ {
+    std::shared_ptr<TableSession> cpp;
+};
+
+struct CTablet_ {
+    Tablet cpp;
+};
+
+struct CSessionDataSet_ {
+    std::unique_ptr<SessionDataSet> cpp;
+};
+
+struct CRowRecord_ {
+    std::shared_ptr<RowRecord> cpp;
+};
+
+/* ============================================================
+ *  Thread-local error message buffer
+ * ============================================================ */
+
+static thread_local std::string g_lastError;
+
+static void clearError() {
+    g_lastError.clear();
+}
+
+static TsStatus setError(TsStatus code, const std::string& msg) {
+    g_lastError = msg;
+    return code;
+}
+
+static TsStatus setError(TsStatus code, const std::exception& e) {
+    g_lastError = e.what();
+    return code;
+}
+
+static TsStatus handleException(const std::exception& e) {
+#if defined(_CPPRTTI) || defined(__GXX_RTTI)
+    // Try to classify exception type (requires RTTI enabled).
+    if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
+        return setError(TS_ERR_CONNECTION, e);
+    }
+    if (dynamic_cast<const ExecutionException*>(&e) ||
+        dynamic_cast<const StatementExecutionException*>(&e) ||
+        dynamic_cast<const BatchExecutionException*>(&e)) {
+        return setError(TS_ERR_EXECUTION, e);
+    }
+#endif
+    return setError(TS_ERR_UNKNOWN, e);
+}
+
+extern "C" {
+
+const char* ts_get_last_error(void) {
+    return g_lastError.c_str();
+}
+
+}  /* extern "C" */
+
+/* ============================================================
+ *  Helpers — convert C arrays to C++ vectors
+ * ============================================================ */
+
+static std::vector<std::string> toStringVec(const char* const* arr, int count) {
+    std::vector<std::string> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.emplace_back(arr[i]);
+    }
+    return v;
+}
+
+static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
+    std::vector<TSDataType::TSDataType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
+    std::vector<TSEncoding::TSEncoding> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
+    }
+    return v;
+}
+
+static std::vector<CompressionType::CompressionType> toCompressionVec(const TSCompressionType_C* arr, int count) {
+    std::vector<CompressionType::CompressionType> v;
+    v.reserve(count);
+    for (int i = 0; i < count; i++) {
+        v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
+    }
+    return v;
+}
+
+static std::map<std::string, std::string> toStringMap(int count, const char* const* keys, const char* const* values) {
+    std::map<std::string, std::string> m;
+    for (int i = 0; i < count; i++) {
+        m[keys[i]] = values[i];
+    }
+    return m;
+}
+
+/**
+ * Convert C typed values (void* const* values, TSDataType_C* types, int count)
+ * to C++ vector<char*> that Session expects.
+ * The caller must free the returned char* pointers using freeCharPtrVec().
+ */
+static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values, int count) {
+    std::vector<char*> result(count);
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN: {
+                bool* p = new bool(*static_cast<const bool*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT32: {
+                int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: {
+                int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_FLOAT: {
+                float* p = new float(*static_cast<const float*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_DOUBLE: {
+                double* p = new double(*static_cast<const double*>(values[i]));
+                result[i] = reinterpret_cast<char*>(p);
+                break;
+            }
+            case TS_TYPE_TEXT:
+            case TS_TYPE_STRING:
+            case TS_TYPE_BLOB:
+            default: {
+                const char* src = static_cast<const char*>(values[i]);
+                size_t len = strlen(src) + 1;
+                char* p = new char[len];
+                memcpy(p, src, len);
+                result[i] = p;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
+    for (int i = 0; i < count; i++) {
+        switch (types[i]) {
+            case TS_TYPE_BOOLEAN:   delete reinterpret_cast<bool*>(vec[i]); break;
+            case TS_TYPE_INT32:     delete reinterpret_cast<int32_t*>(vec[i]); break;
+            case TS_TYPE_INT64:
+            case TS_TYPE_TIMESTAMP: delete reinterpret_cast<int64_t*>(vec[i]); break;
+            case TS_TYPE_FLOAT:     delete reinterpret_cast<float*>(vec[i]); break;
+            case TS_TYPE_DOUBLE:    delete reinterpret_cast<double*>(vec[i]); break;
+            default:                delete[] vec[i]; break;
+        }
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Tree Model
+ * ============================================================ */
+
+extern "C" {
+
+CSession* ts_session_new(const char* host, int rpcPort,
+                          const char* username, const char* password) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_with_zone(const char* host, int rpcPort,
+                                    const char* username, const char* password,
+                                    const char* zoneId, int fetchSize) {
+    clearError();
+    try {
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(
+            std::string(host), rpcPort,
+            std::string(username), std::string(password),
+            std::string(zoneId), fetchSize);
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                     const char* username, const char* password) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        auto* cs = new CSession_();
+        cs->cpp = std::make_shared<Session>(urls, std::string(username), std::string(password));
+        return cs;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_session_destroy(CSession* session) {
+    delete session;
+}
+
+TsStatus ts_session_open(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open(enableRPCCompression);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_close(CSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Session Lifecycle  —  Table Model
+ * ============================================================ */
+
+CTableSession* ts_table_session_new(const char* host, int rpcPort,
+                                     const char* username, const char* password,
+                                     const char* database) {
+    clearError();
+    try {
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->host(std::string(host))
+            ->rpcPort(rpcPort)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int urlCount,
+                                                const char* username, const char* password,
+                                                const char* database) {
+    clearError();
+    try {
+        auto urls = toStringVec(nodeUrls, urlCount);
+        std::unique_ptr<TableSessionBuilder> builder(new TableSessionBuilder());
+        auto tableSession = builder->nodeUrls(urls)
+            ->username(std::string(username))
+            ->password(std::string(password))
+            ->database(std::string(database ? database : ""))
+            ->build();
+        auto* cts = new CTableSession_();
+        cts->cpp = tableSession;
+        return cts;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_table_session_destroy(CTableSession* session) {
+    delete session;
+}
+
+TsStatus ts_table_session_open(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->open();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_close(CTableSession* session) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->close();
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timezone
+ * ============================================================ */
+
+TsStatus ts_session_set_timezone(CSession* session, const char* zoneId) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->setTimeZone(std::string(zoneId));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!buf || bufLen <= 0) return setError(TS_ERR_INVALID_PARAM, "invalid buffer");
+    try {
+        std::string tz = session->cpp->getTimeZone();
+        if ((int)tz.size() >= bufLen) {
+            return setError(TS_ERR_INVALID_PARAM, "buffer too small");
+        }
+        strncpy(buf, tz.c_str(), bufLen);
+        buf[bufLen - 1] = '\0';
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Database Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_database(CSession* session, const char* database) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteDatabase(std::string(database));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_databases(CSession* session, const char* const* databases, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto dbs = toStringVec(databases, count);
+        session->cpp->deleteDatabases(dbs);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Timeseries Management  (Tree Model)
+ * ============================================================ */
+
+TsStatus ts_session_create_timeseries(CSession* session, const char* path,
+                                       TSDataType_C dataType, TSEncoding_C encoding,
+                                       TSCompressionType_C compressor) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_timeseries_ex(CSession* session, const char* path,
+                                          TSDataType_C dataType, TSEncoding_C encoding,
+                                          TSCompressionType_C compressor,
+                                          int propsCount,
+                                          const char* const* propKeys,
+                                          const char* const* propValues,
+                                          int tagsCount,
+                                          const char* const* tagKeys,
+                                          const char* const* tagValues,
+                                          int attrsCount,
+                                          const char* const* attrKeys,
+                                          const char* const* attrValues,
+                                          const char* measurementAlias) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::map<std::string, std::string> props = propsCount > 0 ? toStringMap(propsCount, propKeys, propValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> tags = tagsCount > 0 ? toStringMap(tagsCount, tagKeys, tagValues) : std::map<std::string, std::string>();
+        std::map<std::string, std::string> attrs = attrsCount > 0 ? toStringMap(attrsCount, attrKeys, attrValues) : std::map<std::string, std::string>();
+        session->cpp->createTimeseries(
+            std::string(path),
+            static_cast<TSDataType::TSDataType>(dataType),
+            static_cast<TSEncoding::TSEncoding>(encoding),
+            static_cast<CompressionType::CompressionType>(compressor),
+            &props, &tags, &attrs,
+            std::string(measurementAlias ? measurementAlias : ""));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_multi_timeseries(CSession* session, int count,
+                                             const char* const* paths,
+                                             const TSDataType_C* dataTypes,
+                                             const TSEncoding_C* encodings,
+                                             const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createMultiTimeseries(
+            pathsVec, typesVec, encVec, compVec,
+            nullptr, nullptr, nullptr, nullptr);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_create_aligned_timeseries(CSession* session, const char* deviceId,
+                                               int count,
+                                               const char* const* measurements,
+                                               const TSDataType_C* dataTypes,
+                                               const TSEncoding_C* encodings,
+                                               const TSCompressionType_C* compressors) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(dataTypes, count);
+        auto encVec = toEncodingVec(encodings, count);
+        auto compVec = toCompressionVec(compressors, count);
+        session->cpp->createAlignedTimeseries(
+            std::string(deviceId), measurementsVec, typesVec, encVec, compVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_check_timeseries_exists(CSession* session, const char* path, bool* exists) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!exists) return setError(TS_ERR_INVALID_PARAM, "exists pointer is null");
+    try {
+        *exists = session->cpp->checkTimeseriesExists(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries(CSession* session, const char* path) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->deleteTimeseries(std::string(path));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_delete_timeseries_batch(CSession* session, const char* const* paths, int count) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto pathsVec = toStringVec(paths, count);
+        session->cpp->deleteTimeseries(pathsVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Tablet Operations
+ * ============================================================ */
+
+CTablet* ts_tablet_new(const char* deviceId, int columnCount,
+                        const char* const* columnNames,
+                        const TSDataType_C* dataTypes,
+                        int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        schemas.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+CTablet* ts_tablet_new_with_category(const char* deviceId, int columnCount,
+                                      const char* const* columnNames,
+                                      const TSDataType_C* dataTypes,
+                                      const TSColumnCategory_C* columnCategories,
+                                      int maxRowNumber) {
+    try {
+        std::vector<std::pair<std::string, TSDataType::TSDataType>> schemas;
+        std::vector<ColumnCategory> colTypes;
+        schemas.reserve(columnCount);
+        colTypes.reserve(columnCount);
+        for (int i = 0; i < columnCount; i++) {
+            schemas.emplace_back(std::string(columnNames[i]),
+                                 static_cast<TSDataType::TSDataType>(dataTypes[i]));
+            colTypes.push_back(static_cast<ColumnCategory>(columnCategories[i]));
+        }
+        auto* ct = new CTablet_();
+        ct->cpp = Tablet(std::string(deviceId), schemas, colTypes, maxRowNumber);
+        return ct;
+    } catch (const std::exception& e) {
+        handleException(e);
+        return nullptr;
+    }
+}
+
+void ts_tablet_destroy(CTablet* tablet) {
+    delete tablet;
+}
+
+void ts_tablet_reset(CTablet* tablet) {
+    if (tablet) {
+        tablet->cpp.reset();
+    }
+}
+
+int ts_tablet_get_row_count(CTablet* tablet) {
+    if (!tablet) return 0;
+    return static_cast<int>(tablet->cpp.rowSize);
+}
+
+TsStatus ts_tablet_set_row_count(CTablet* tablet, int rowCount) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    tablet->cpp.rowSize = rowCount;
+    return TS_OK;
+}
+
+TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addTimestamp(static_cast<size_t>(rowIndex), timestamp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value) {
+    clearError();
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        std::string str(value);
+        tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), str);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Record, string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_record_str(CSession* session, const char* deviceId,
+                                       int64_t time, int count,
+                                       const char* const* measurements,
+                                       const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_record(CSession* session, const char* deviceId,
+                                   int64_t time, int count,
+                                   const char* const* measurements,
+                                   const TSDataType_C* types,
+                                   const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record_str(CSession* session, const char* deviceId,
+                                               int64_t time, int count,
+                                               const char* const* measurements,
+                                               const char* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto valuesVec = toStringVec(values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, valuesVec);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_record(CSession* session, const char* deviceId,
+                                           int64_t time, int count,
+                                           const char* const* measurements,
+                                           const TSDataType_C* types,
+                                           const void* const* values) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto measurementsVec = toStringVec(measurements, count);
+        auto typesVec = toTypeVec(types, count);
+        auto charVec = toCharPtrVec(types, values, count);
+        session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
+        freeCharPtrVec(charVec, types, count);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (string values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_str(CSession* session, int deviceCount,
+                                        const char* const* deviceIds,
+                                        const int64_t* times,
+                                        const int* measurementCounts,
+                                        const char* const* const* measurementsList,
+                                        const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_str(CSession* session, int deviceCount,
+                                                const char* const* deviceIds,
+                                                const int64_t* times,
+                                                const int* measurementCounts,
+                                                const char* const* const* measurementsList,
+                                                const char* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList, vList;
+        mList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, vList);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, multiple devices (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records(CSession* session, int deviceCount,
+                                    const char* const* deviceIds,
+                                    const int64_t* times,
+                                    const int* measurementCounts,
+                                    const char* const* const* measurementsList,
+                                    const TSDataType_C* const* typesList,
+                                    const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records(CSession* session, int deviceCount,
+                                            const char* const* deviceIds,
+                                            const int64_t* times,
+                                            const int* measurementCounts,
+                                            const char* const* const* measurementsList,
+                                            const TSDataType_C* const* typesList,
+                                            const void* const* const* valuesList) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        auto devVec = toStringVec(deviceIds, deviceCount);
+        std::vector<int64_t> timesVec(times, times + deviceCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(deviceCount);
+        tList.reserve(deviceCount);
+        vList.reserve(deviceCount);
+        for (int i = 0; i < deviceCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecords(devVec, timesVec, mList, tList, vList);
+        for (int i = 0; i < deviceCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Batch, single device (typed values)
+ * ============================================================ */
+
+TsStatus ts_session_insert_records_of_one_device(CSession* session, const char* deviceId,
+                                                  int rowCount, const int64_t* times,
+                                                  const int* measurementCounts,
+                                                  const char* const* const* measurementsList,
+                                                  const TSDataType_C* const* typesList,
+                                                  const void* const* const* valuesList,
+                                                  bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_records_of_one_device(CSession* session, const char* deviceId,
+                                                          int rowCount, const int64_t* times,
+                                                          const int* measurementCounts,
+                                                          const char* const* const* measurementsList,
+                                                          const TSDataType_C* const* typesList,
+                                                          const void* const* const* valuesList,
+                                                          bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::vector<int64_t> timesVec(times, times + rowCount);
+        std::vector<std::vector<std::string>> mList;
+        std::vector<std::vector<TSDataType::TSDataType>> tList;
+        std::vector<std::vector<char*>> vList;
+        mList.reserve(rowCount);
+        tList.reserve(rowCount);
+        vList.reserve(rowCount);
+        for (int i = 0; i < rowCount; i++) {
+            mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
+            tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
+            vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
+        }
+        session->cpp->insertAlignedRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList, sorted);
+        for (int i = 0; i < rowCount; i++) {
+            freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
+        }
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Tree Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_session_insert_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insertAlignedTablet(tablet->cpp, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_tablets(CSession* session, int tabletCount,
+                                    const char* const* deviceIds,
+                                    CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_insert_aligned_tablets(CSession* session, int tabletCount,
+                                            const char* const* deviceIds,
+                                            CTablet** tablets, bool sorted) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        std::unordered_map<std::string, Tablet*> tabletMap;
+        for (int i = 0; i < tabletCount; i++) {
+            tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
+        }
+        session->cpp->insertAlignedTablets(tabletMap, sorted);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Data Insertion  —  Table Model  (Tablet)
+ * ============================================================ */
+
+TsStatus ts_table_session_insert(CTableSession* session, CTablet* tablet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!tablet) return setError(TS_ERR_NULL_PTR, "tablet is null");
+    try {
+        session->cpp->insert(tablet->cpp);
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Tree Model
+ * ============================================================ */
+
+TsStatus ts_session_execute_query(CSession* session, const char* sql,
+                                   CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_query_with_timeout(CSession* session, const char* sql,
+                                                int64_t timeoutInMs,
+                                                CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_non_query(CSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_raw_data_query(CSession* session,
+                                            int pathCount, const char* const* paths,
+                                            int64_t startTime, int64_t endTime,
+                                            CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeRawDataQuery(pathsVec, startTime, endTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query(CSession* session,
+                                             int pathCount, const char* const* paths,
+                                             CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_session_execute_last_data_query_with_time(CSession* session,
+                                                       int pathCount, const char* const* paths,
+                                                       int64_t lastTime,
+                                                       CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto pathsVec = toStringVec(paths, pathCount);
+        auto ds = session->cpp->executeLastDataQuery(pathsVec, lastTime);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  Query  —  Table Model
+ * ============================================================ */
+
+TsStatus ts_table_session_execute_query(CTableSession* session, const char* sql,
+                                         CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql));
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_query_with_timeout(CTableSession* session, const char* sql,
+                                                      int64_t timeoutInMs,
+                                                      CSessionDataSet** dataSet) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    if (!dataSet) return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
+    try {
+        auto ds = session->cpp->executeQueryStatement(std::string(sql), timeoutInMs);
+        auto* cds = new CSessionDataSet_();
+        cds->cpp = std::move(ds);
+        *dataSet = cds;
+        return TS_OK;
+    } catch (const std::exception& e) {
+        *dataSet = nullptr;
+        return handleException(e);
+    }
+}
+
+TsStatus ts_table_session_execute_non_query(CTableSession* session, const char* sql) {
+    clearError();
+    if (!session) return setError(TS_ERR_NULL_PTR, "session is null");
+    try {
+        session->cpp->executeNonQueryStatement(std::string(sql));
+        return TS_OK;
+    } catch (const std::exception& e) {
+        return handleException(e);
+    }
+}
+
+/* ============================================================
+ *  SessionDataSet & RowRecord  —  Result Iteration
+ * ============================================================ */
+
+void ts_dataset_destroy(CSessionDataSet* dataSet) {
+    if (dataSet) {
+        if (dataSet->cpp) {
+            dataSet->cpp->closeOperationHandle();
+        }
+        delete dataSet;
+    }
+}
+
+bool ts_dataset_has_next(CSessionDataSet* dataSet) {
+    if (!dataSet || !dataSet->cpp) return false;
+    try {
+        return dataSet->cpp->hasNext();
+    } catch (...) {
+        return false;
+    }
+}

Review Comment:
   Updated dataset iteration APIs to populate ts_get_last_error() on errors; added clearError() + null-handle errors for consistency with other bindings.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Session: session-c interface [iotdb]

Posted by "jt2594838 (via GitHub)" <gi...@apache.org>.
jt2594838 closed pull request #17347: Session:  session-c interface
URL: https://github.com/apache/iotdb/pull/17347


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@iotdb.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org