| /** | |
| * 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 "SessionBuilder.h" | |
| #include "SessionDataSet.h" | |
| #include <cstring> | |
| #include <map> | |
| #include <memory> | |
| #include <string> | |
| #include <unordered_map> | |
| #include <vector> | |
| /* ============================================================ | |
| * Internal wrapper structs — the opaque handles point to these | |
| * ============================================================ */ | |
| struct CSession_ { | |
| std::shared_ptr<Session> 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 { | |
| SessionBuilder builder; | |
| builder.host(host) | |
| ->rpcPort(rpcPort) | |
| ->username(username) | |
| ->password(password) | |
| ->enableAutoFetch(false); | |
| auto cpp = std::make_shared<Session>(&builder); | |
| 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 { | |
| SessionBuilder builder; | |
| builder.host(host) | |
| ->rpcPort(rpcPort) | |
| ->username(username) | |
| ->password(password) | |
| ->zoneId(zoneId) | |
| ->fetchSize(fetchSize) | |
| ->enableAutoFetch(false); | |
| auto cpp = std::make_shared<Session>(&builder); | |
| 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); | |
| SessionBuilder builder; | |
| builder.nodeUrls(urls) | |
| ->username(username) | |
| ->password(password) | |
| ->enableAutoFetch(false); | |
| auto cpp = std::make_shared<Session>(&builder); | |
| 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); | |
| } | |
| } | |
| /* ============================================================ | |
| * 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; | |
| } | |
| } | |
| 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); | |
| } | |
| } | |
| /* ============================================================ | |
| * 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); | |
| } | |
| } | |
| /* ============================================================ | |
| * 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]; | |
| if (f.longV.is_initialized()) | |
| return f.longV.value(); | |
| if (f.intV.is_initialized()) | |
| return f.intV.value(); | |
| if (f.doubleV.is_initialized()) | |
| return static_cast<int64_t>(f.doubleV.value()); | |
| return 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_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" */ |