| /** | |
| * 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 "Date.h" | |
| #include "Session.h" | |
| #include "TableSession.h" | |
| #include "TableSessionBuilder.h" | |
| #include "SessionBuilder.h" | |
| #include "SessionDataSet.h" | |
| #include <cstring> | |
| #include <map> | |
| #include <memory> | |
| #include <stdexcept> | |
| #include <string> | |
| #include <unordered_map> | |
| #include <vector> | |
| /* ============================================================ | |
| * 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); | |
| } | |
| if (dynamic_cast<const std::out_of_range*>(&e) || | |
| dynamic_cast<const std::invalid_argument*>(&e)) { | |
| return setError(TS_ERR_INVALID_PARAM, 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; | |
| } | |
| static TsStatus setSslConfig(SslConfig& config, const char* trustCertFilePath, | |
| const char* clientCertificateFilePath, | |
| const char* clientPrivateKeyFilePath) { | |
| if (trustCertFilePath == nullptr) { | |
| return setError(TS_ERR_INVALID_PARAM, "trustCertFilePath is null"); | |
| } | |
| if ((clientCertificateFilePath == nullptr) != (clientPrivateKeyFilePath == nullptr)) { | |
| return setError(TS_ERR_INVALID_PARAM, | |
| "clientCertificateFilePath and clientPrivateKeyFilePath must both be null or " | |
| "both be set"); | |
| } | |
| config.useSsl = true; | |
| config.trustCertFilePath = trustCertFilePath; | |
| config.clientCertificateFilePath = | |
| clientCertificateFilePath == nullptr ? "" : clientCertificateFilePath; | |
| config.clientPrivateKeyFilePath = | |
| clientPrivateKeyFilePath == nullptr ? "" : clientPrivateKeyFilePath; | |
| return TS_OK; | |
| } | |
| /** | |
| * 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_DATE: { | |
| const TSDate_C* src = static_cast<const TSDate_C*>(values[i]); | |
| IoTDBDate* p = new IoTDBDate(src->year, src->month, src->day); | |
| 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; | |
| case TS_TYPE_DATE: | |
| delete reinterpret_cast<IoTDBDate*>(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 cpp = std::make_shared<Session>(std::string(host), rpcPort, std::string(username), | |
| std::string(password)); | |
| auto* cs = new CSession_(); | |
| cs->cpp = std::move(cpp); | |
| 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 cpp = std::make_shared<Session>(std::string(host), rpcPort, std::string(username), | |
| std::string(password), std::string(zoneId), fetchSize); | |
| auto* cs = new CSession_(); | |
| cs->cpp = std::move(cpp); | |
| 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 cpp = std::make_shared<Session>(urls, std::string(username), std::string(password)); | |
| auto* cs = new CSession_(); | |
| cs->cpp = std::move(cpp); | |
| 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); | |
| } | |
| } | |
| TsStatus ts_session_set_ssl_config(CSession* session, const char* trustCertFilePath, | |
| const char* clientCertificateFilePath, | |
| const char* clientPrivateKeyFilePath) { | |
| clearError(); | |
| if (!session) | |
| return setError(TS_ERR_NULL_PTR, "session is null"); | |
| try { | |
| SslConfig config; | |
| TsStatus status = setSslConfig(config, trustCertFilePath, clientCertificateFilePath, | |
| clientPrivateKeyFilePath); | |
| if (status != TS_OK) { | |
| return status; | |
| } | |
| session->cpp->setSslConfig(config); | |
| 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(); | |
| CTableSession_ tmp{}; | |
| tmp.cpp = std::move(tableSession); | |
| auto* cts = new CTableSession_(); | |
| cts->cpp = std::move(tmp.cpp); | |
| 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(); | |
| CTableSession_ tmp{}; | |
| tmp.cpp = std::move(tableSession); | |
| auto* cts = new CTableSession_(); | |
| cts->cpp = std::move(tmp.cpp); | |
| 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); | |
| } | |
| } | |
| CTableSession* ts_table_session_new_with_ssl(const char* host, int rpcPort, const char* username, | |
| const char* password, const char* database, | |
| const char* trustCertFilePath, | |
| const char* clientCertificateFilePath, | |
| const char* clientPrivateKeyFilePath) { | |
| clearError(); | |
| try { | |
| SslConfig config; | |
| if (setSslConfig(config, trustCertFilePath, clientCertificateFilePath, | |
| clientPrivateKeyFilePath) != TS_OK) { | |
| return nullptr; | |
| } | |
| TableSessionBuilder builder; | |
| builder.host(std::string(host)) | |
| ->rpcPort(rpcPort) | |
| ->username(std::string(username)) | |
| ->password(std::string(password)) | |
| ->database(std::string(database ? database : "")); | |
| builder.sslConfig = config; | |
| auto tableSession = builder.build(); | |
| auto* session = new CTableSession_(); | |
| session->cpp = std::move(tableSession); | |
| return session; | |
| } catch (const std::exception& e) { | |
| handleException(e); | |
| return nullptr; | |
| } | |
| } | |
| /* ============================================================ | |
| * 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])); | |
| } | |
| Tablet tablet(std::string(deviceId), schemas, maxRowNumber); | |
| auto* ct = new CTablet_(); | |
| ct->cpp = std::move(tablet); | |
| 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])); | |
| } | |
| Tablet tablet(std::string(deviceId), schemas, colTypes, maxRowNumber); | |
| auto* ct = new CTablet_(); | |
| ct->cpp = std::move(tablet); | |
| 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"); | |
| if (rowCount < 0 || static_cast<size_t>(rowCount) > tablet->cpp.maxRowNumber) { | |
| return setError(TS_ERR_INVALID_PARAM, "rowCount out of range [0, maxRowNumber]"); | |
| } | |
| tablet->cpp.rowSize = static_cast<size_t>(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)); | |
| CSessionDataSet_ tmp{}; | |
| tmp.cpp = std::move(ds); | |
| auto* cds = new CSessionDataSet_(); | |
| cds->cpp = std::move(tmp.cpp); | |
| *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); | |
| CSessionDataSet_ tmp{}; | |
| tmp.cpp = std::move(ds); | |
| auto* cds = new CSessionDataSet_(); | |
| cds->cpp = std::move(tmp.cpp); | |
| *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); | |
| CSessionDataSet_ tmp{}; | |
| tmp.cpp = std::move(ds); | |
| auto* cds = new CSessionDataSet_(); | |
| cds->cpp = std::move(tmp.cpp); | |
| *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); | |
| CSessionDataSet_ tmp{}; | |
| tmp.cpp = std::move(ds); | |
| auto* cds = new CSessionDataSet_(); | |
| cds->cpp = std::move(tmp.cpp); | |
| *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); | |
| CSessionDataSet_ tmp{}; | |
| tmp.cpp = std::move(ds); | |
| auto* cds = new CSessionDataSet_(); | |
| cds->cpp = std::move(tmp.cpp); | |
| *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)); | |
| CSessionDataSet_ tmp{}; | |
| tmp.cpp = std::move(ds); | |
| auto* cds = new CSessionDataSet_(); | |
| cds->cpp = std::move(tmp.cpp); | |
| *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); | |
| CSessionDataSet_ tmp{}; | |
| tmp.cpp = std::move(ds); | |
| auto* cds = new CSessionDataSet_(); | |
| cds->cpp = std::move(tmp.cpp); | |
| *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) { | |
| clearError(); | |
| if (!dataSet) { | |
| (void)setError(TS_ERR_NULL_PTR, "dataSet is null"); | |
| return false; | |
| } | |
| if (!dataSet->cpp) { | |
| (void)setError(TS_ERR_NULL_PTR, "dataSet is not initialized"); | |
| return false; | |
| } | |
| try { | |
| return dataSet->cpp->hasNext(); | |
| } catch (const std::exception& e) { | |
| (void)handleException(e); | |
| return false; | |
| } catch (...) { | |
| (void)setError(TS_ERR_UNKNOWN, "non-standard exception"); | |
| return false; | |
| } | |
| } | |
| CRowRecord* ts_dataset_next(CSessionDataSet* dataSet) { | |
| clearError(); | |
| if (!dataSet) { | |
| (void)setError(TS_ERR_NULL_PTR, "dataSet is null"); | |
| return nullptr; | |
| } | |
| if (!dataSet->cpp) { | |
| (void)setError(TS_ERR_NULL_PTR, "dataSet is not initialized"); | |
| return nullptr; | |
| } | |
| try { | |
| auto row = dataSet->cpp->next(); | |
| if (!row) | |
| return nullptr; | |
| CRowRecord_ tmp{}; | |
| tmp.cpp = std::move(row); | |
| auto* crr = new CRowRecord_(); | |
| crr->cpp = std::move(tmp.cpp); | |
| return crr; | |
| } catch (const std::exception& e) { | |
| (void)handleException(e); | |
| return nullptr; | |
| } catch (...) { | |
| (void)setError(TS_ERR_UNKNOWN, "non-standard exception"); | |
| 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; | |
| return record->cpp->fields[index].isNull(); | |
| } | |
| 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 ""; | |
| } | |
| int32_t ts_row_record_get_date_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]; | |
| if (f.dataType != TSDataType::DATE || !f.dateV.is_initialized() || | |
| f.dateV.value().is_not_a_date()) { | |
| return 0; | |
| } | |
| return parseDateExpressionToInt(f.dateV.value()); | |
| } | |
| size_t ts_row_record_get_string_byte_length(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]; | |
| if (f.stringV.is_initialized()) { | |
| return f.stringV.value().size(); | |
| } | |
| return 0; | |
| } | |
| TSDataType_C ts_row_record_get_data_type(CRowRecord* record, int index) { | |
| if (!record || !record->cpp) | |
| return TS_TYPE_INVALID; | |
| if (index < 0 || index >= (int)record->cpp->fields.size()) | |
| return TS_TYPE_INVALID; | |
| return static_cast<TSDataType_C>(record->cpp->fields[index].dataType); | |
| } | |
| /* ============================================================ | |
| * Data Deletion (Tree Model) | |
| * ============================================================ */ | |
| TsStatus ts_session_delete_data(CSession* session, const char* path, int64_t endTime) { | |
| clearError(); | |
| if (!session) | |
| return setError(TS_ERR_NULL_PTR, "session is null"); | |
| try { | |
| session->cpp->deleteData(std::string(path), endTime); | |
| return TS_OK; | |
| } catch (const std::exception& e) { | |
| return handleException(e); | |
| } | |
| } | |
| TsStatus ts_session_delete_data_batch(CSession* session, int pathCount, const char* const* paths, | |
| int64_t endTime) { | |
| clearError(); | |
| if (!session) | |
| return setError(TS_ERR_NULL_PTR, "session is null"); | |
| try { | |
| auto pathsVec = toStringVec(paths, pathCount); | |
| session->cpp->deleteData(pathsVec, endTime); | |
| return TS_OK; | |
| } catch (const std::exception& e) { | |
| return handleException(e); | |
| } | |
| } | |
| TsStatus ts_session_delete_data_range(CSession* session, int pathCount, const char* const* paths, | |
| int64_t startTime, int64_t endTime) { | |
| clearError(); | |
| if (!session) | |
| return setError(TS_ERR_NULL_PTR, "session is null"); | |
| try { | |
| auto pathsVec = toStringVec(paths, pathCount); | |
| session->cpp->deleteData(pathsVec, startTime, endTime); | |
| return TS_OK; | |
| } catch (const std::exception& e) { | |
| return handleException(e); | |
| } | |
| } | |
| } /* extern "C" */ |