blob: e7c43cdb9f1befcf1f919d0e2793100df694046c [file]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "SessionC.h"
#include "Session.h"
#include "TableSession.h"
#include "TableSessionBuilder.h"
#include "SessionBuilder.h"
#include "SessionDataSet.h"
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include <memory>
/* ============================================================
* Internal wrapper structs — the opaque handles point to these
* ============================================================ */
struct CSession_ {
std::shared_ptr<Session> cpp;
};
struct CTableSession_ {
std::shared_ptr<TableSession> cpp;
};
struct CTablet_ {
Tablet cpp;
};
struct CSessionDataSet_ {
std::unique_ptr<SessionDataSet> cpp;
};
struct CRowRecord_ {
std::shared_ptr<RowRecord> cpp;
};
/* ============================================================
* Thread-local error message buffer
* ============================================================ */
static thread_local std::string g_lastError;
static void clearError() {
g_lastError.clear();
}
static TsStatus setError(TsStatus code, const std::string& msg) {
g_lastError = msg;
return code;
}
static TsStatus setError(TsStatus code, const std::exception& e) {
g_lastError = e.what();
return code;
}
static TsStatus handleException(const std::exception& e) {
#if defined(_CPPRTTI) || defined(__GXX_RTTI)
// Try to classify exception type (requires RTTI enabled).
if (dynamic_cast<const IoTDBConnectionException*>(&e)) {
return setError(TS_ERR_CONNECTION, e);
}
if (dynamic_cast<const ExecutionException*>(&e) ||
dynamic_cast<const StatementExecutionException*>(&e) ||
dynamic_cast<const BatchExecutionException*>(&e)) {
return setError(TS_ERR_EXECUTION, e);
}
#endif
return setError(TS_ERR_UNKNOWN, e);
}
extern "C" {
const char* ts_get_last_error(void) {
return g_lastError.c_str();
}
} /* extern "C" */
/* ============================================================
* Helpers — convert C arrays to C++ vectors
* ============================================================ */
static std::vector<std::string> toStringVec(const char* const* arr, int count) {
std::vector<std::string> v;
v.reserve(count);
for (int i = 0; i < count; i++) {
v.emplace_back(arr[i]);
}
return v;
}
static std::vector<TSDataType::TSDataType> toTypeVec(const TSDataType_C* arr, int count) {
std::vector<TSDataType::TSDataType> v;
v.reserve(count);
for (int i = 0; i < count; i++) {
v.push_back(static_cast<TSDataType::TSDataType>(arr[i]));
}
return v;
}
static std::vector<TSEncoding::TSEncoding> toEncodingVec(const TSEncoding_C* arr, int count) {
std::vector<TSEncoding::TSEncoding> v;
v.reserve(count);
for (int i = 0; i < count; i++) {
v.push_back(static_cast<TSEncoding::TSEncoding>(arr[i]));
}
return v;
}
static std::vector<CompressionType::CompressionType>
toCompressionVec(const TSCompressionType_C* arr, int count) {
std::vector<CompressionType::CompressionType> v;
v.reserve(count);
for (int i = 0; i < count; i++) {
v.push_back(static_cast<CompressionType::CompressionType>(arr[i]));
}
return v;
}
static std::map<std::string, std::string> toStringMap(int count, const char* const* keys,
const char* const* values) {
std::map<std::string, std::string> m;
for (int i = 0; i < count; i++) {
m[keys[i]] = values[i];
}
return m;
}
/**
* Convert C typed values (void* const* values, TSDataType_C* types, int count)
* to C++ vector<char*> that Session expects.
* The caller must free the returned char* pointers using freeCharPtrVec().
*/
static std::vector<char*> toCharPtrVec(const TSDataType_C* types, const void* const* values,
int count) {
std::vector<char*> result(count);
for (int i = 0; i < count; i++) {
switch (types[i]) {
case TS_TYPE_BOOLEAN: {
bool* p = new bool(*static_cast<const bool*>(values[i]));
result[i] = reinterpret_cast<char*>(p);
break;
}
case TS_TYPE_INT32: {
int32_t* p = new int32_t(*static_cast<const int32_t*>(values[i]));
result[i] = reinterpret_cast<char*>(p);
break;
}
case TS_TYPE_INT64:
case TS_TYPE_TIMESTAMP: {
int64_t* p = new int64_t(*static_cast<const int64_t*>(values[i]));
result[i] = reinterpret_cast<char*>(p);
break;
}
case TS_TYPE_FLOAT: {
float* p = new float(*static_cast<const float*>(values[i]));
result[i] = reinterpret_cast<char*>(p);
break;
}
case TS_TYPE_DOUBLE: {
double* p = new double(*static_cast<const double*>(values[i]));
result[i] = reinterpret_cast<char*>(p);
break;
}
case TS_TYPE_TEXT:
case TS_TYPE_STRING:
case TS_TYPE_BLOB:
default: {
const char* src = static_cast<const char*>(values[i]);
size_t len = strlen(src) + 1;
char* p = new char[len];
memcpy(p, src, len);
result[i] = p;
break;
}
}
}
return result;
}
static void freeCharPtrVec(std::vector<char*>& vec, const TSDataType_C* types, int count) {
for (int i = 0; i < count; i++) {
switch (types[i]) {
case TS_TYPE_BOOLEAN:
delete reinterpret_cast<bool*>(vec[i]);
break;
case TS_TYPE_INT32:
delete reinterpret_cast<int32_t*>(vec[i]);
break;
case TS_TYPE_INT64:
case TS_TYPE_TIMESTAMP:
delete reinterpret_cast<int64_t*>(vec[i]);
break;
case TS_TYPE_FLOAT:
delete reinterpret_cast<float*>(vec[i]);
break;
case TS_TYPE_DOUBLE:
delete reinterpret_cast<double*>(vec[i]);
break;
default:
delete[] vec[i];
break;
}
}
}
/* ============================================================
* Session Lifecycle — Tree Model
* ============================================================ */
extern "C" {
CSession* ts_session_new(const char* host, int rpcPort, const char* username,
const char* password) {
clearError();
try {
auto 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);
}
}
/* ============================================================
* 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);
}
}
/* ============================================================
* 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");
tablet->cpp.rowSize = rowCount;
return TS_OK;
}
TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp) {
clearError();
if (!tablet)
return setError(TS_ERR_NULL_PTR, "tablet is null");
try {
tablet->cpp.addTimestamp(static_cast<size_t>(rowIndex), timestamp);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value) {
clearError();
if (!tablet)
return setError(TS_ERR_NULL_PTR, "tablet is null");
try {
tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value) {
clearError();
if (!tablet)
return setError(TS_ERR_NULL_PTR, "tablet is null");
try {
tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value) {
clearError();
if (!tablet)
return setError(TS_ERR_NULL_PTR, "tablet is null");
try {
tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value) {
clearError();
if (!tablet)
return setError(TS_ERR_NULL_PTR, "tablet is null");
try {
tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value) {
clearError();
if (!tablet)
return setError(TS_ERR_NULL_PTR, "tablet is null");
try {
tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), value);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex,
const char* value) {
clearError();
if (!tablet)
return setError(TS_ERR_NULL_PTR, "tablet is null");
try {
std::string str(value);
tablet->cpp.addValue(static_cast<size_t>(colIndex), static_cast<size_t>(rowIndex), str);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
/* ============================================================
* Data Insertion — Tree Model (Record, string values)
* ============================================================ */
TsStatus ts_session_insert_record_str(CSession* session, const char* deviceId, int64_t time,
int count, const char* const* measurements,
const char* const* values) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
auto measurementsVec = toStringVec(measurements, count);
auto valuesVec = toStringVec(values, count);
session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, valuesVec);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_session_insert_record(CSession* session, const char* deviceId, int64_t time, int count,
const char* const* measurements, const TSDataType_C* types,
const void* const* values) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
auto measurementsVec = toStringVec(measurements, count);
auto typesVec = toTypeVec(types, count);
auto charVec = toCharPtrVec(types, values, count);
session->cpp->insertRecord(std::string(deviceId), time, measurementsVec, typesVec, charVec);
freeCharPtrVec(charVec, types, count);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_session_insert_aligned_record_str(CSession* session, const char* deviceId, int64_t time,
int count, const char* const* measurements,
const char* const* values) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
auto measurementsVec = toStringVec(measurements, count);
auto valuesVec = toStringVec(values, count);
session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, valuesVec);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_session_insert_aligned_record(CSession* session, const char* deviceId, int64_t time,
int count, const char* const* measurements,
const TSDataType_C* types, const void* const* values) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
auto measurementsVec = toStringVec(measurements, count);
auto typesVec = toTypeVec(types, count);
auto charVec = toCharPtrVec(types, values, count);
session->cpp->insertAlignedRecord(std::string(deviceId), time, measurementsVec, typesVec,
charVec);
freeCharPtrVec(charVec, types, count);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
/* ============================================================
* Data Insertion — Batch, multiple devices (string values)
* ============================================================ */
TsStatus ts_session_insert_records_str(CSession* session, int deviceCount,
const char* const* deviceIds, const int64_t* times,
const int* measurementCounts,
const char* const* const* measurementsList,
const char* const* const* valuesList) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
auto devVec = toStringVec(deviceIds, deviceCount);
std::vector<int64_t> timesVec(times, times + deviceCount);
std::vector<std::vector<std::string>> mList, vList;
mList.reserve(deviceCount);
vList.reserve(deviceCount);
for (int i = 0; i < deviceCount; i++) {
mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
}
session->cpp->insertRecords(devVec, timesVec, mList, vList);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_session_insert_aligned_records_str(CSession* session, int deviceCount,
const char* const* deviceIds, const int64_t* times,
const int* measurementCounts,
const char* const* const* measurementsList,
const char* const* const* valuesList) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
auto devVec = toStringVec(deviceIds, deviceCount);
std::vector<int64_t> timesVec(times, times + deviceCount);
std::vector<std::vector<std::string>> mList, vList;
mList.reserve(deviceCount);
vList.reserve(deviceCount);
for (int i = 0; i < deviceCount; i++) {
mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
vList.push_back(toStringVec(valuesList[i], measurementCounts[i]));
}
session->cpp->insertAlignedRecords(devVec, timesVec, mList, vList);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
/* ============================================================
* Data Insertion — Batch, multiple devices (typed values)
* ============================================================ */
TsStatus ts_session_insert_records(CSession* session, int deviceCount, const char* const* deviceIds,
const int64_t* times, const int* measurementCounts,
const char* const* const* measurementsList,
const TSDataType_C* const* typesList,
const void* const* const* valuesList) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
auto devVec = toStringVec(deviceIds, deviceCount);
std::vector<int64_t> timesVec(times, times + deviceCount);
std::vector<std::vector<std::string>> mList;
std::vector<std::vector<TSDataType::TSDataType>> tList;
std::vector<std::vector<char*>> vList;
mList.reserve(deviceCount);
tList.reserve(deviceCount);
vList.reserve(deviceCount);
for (int i = 0; i < deviceCount; i++) {
mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
}
session->cpp->insertRecords(devVec, timesVec, mList, tList, vList);
for (int i = 0; i < deviceCount; i++) {
freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
}
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_session_insert_aligned_records(CSession* session, int deviceCount,
const char* const* deviceIds, const int64_t* times,
const int* measurementCounts,
const char* const* const* measurementsList,
const TSDataType_C* const* typesList,
const void* const* const* valuesList) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
auto devVec = toStringVec(deviceIds, deviceCount);
std::vector<int64_t> timesVec(times, times + deviceCount);
std::vector<std::vector<std::string>> mList;
std::vector<std::vector<TSDataType::TSDataType>> tList;
std::vector<std::vector<char*>> vList;
mList.reserve(deviceCount);
tList.reserve(deviceCount);
vList.reserve(deviceCount);
for (int i = 0; i < deviceCount; i++) {
mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
}
session->cpp->insertAlignedRecords(devVec, timesVec, mList, tList, vList);
for (int i = 0; i < deviceCount; i++) {
freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
}
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
/* ============================================================
* Data Insertion — Batch, single device (typed values)
* ============================================================ */
TsStatus ts_session_insert_records_of_one_device(
CSession* session, const char* deviceId, int rowCount, const int64_t* times,
const int* measurementCounts, const char* const* const* measurementsList,
const TSDataType_C* const* typesList, const void* const* const* valuesList, bool sorted) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
std::vector<int64_t> timesVec(times, times + rowCount);
std::vector<std::vector<std::string>> mList;
std::vector<std::vector<TSDataType::TSDataType>> tList;
std::vector<std::vector<char*>> vList;
mList.reserve(rowCount);
tList.reserve(rowCount);
vList.reserve(rowCount);
for (int i = 0; i < rowCount; i++) {
mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
}
session->cpp->insertRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList, vList,
sorted);
for (int i = 0; i < rowCount; i++) {
freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
}
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_session_insert_aligned_records_of_one_device(
CSession* session, const char* deviceId, int rowCount, const int64_t* times,
const int* measurementCounts, const char* const* const* measurementsList,
const TSDataType_C* const* typesList, const void* const* const* valuesList, bool sorted) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
std::vector<int64_t> timesVec(times, times + rowCount);
std::vector<std::vector<std::string>> mList;
std::vector<std::vector<TSDataType::TSDataType>> tList;
std::vector<std::vector<char*>> vList;
mList.reserve(rowCount);
tList.reserve(rowCount);
vList.reserve(rowCount);
for (int i = 0; i < rowCount; i++) {
mList.push_back(toStringVec(measurementsList[i], measurementCounts[i]));
tList.push_back(toTypeVec(typesList[i], measurementCounts[i]));
vList.push_back(toCharPtrVec(typesList[i], valuesList[i], measurementCounts[i]));
}
session->cpp->insertAlignedRecordsOfOneDevice(std::string(deviceId), timesVec, mList, tList,
vList, sorted);
for (int i = 0; i < rowCount; i++) {
freeCharPtrVec(vList[i], typesList[i], measurementCounts[i]);
}
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
/* ============================================================
* Data Insertion — Tree Model (Tablet)
* ============================================================ */
TsStatus ts_session_insert_tablet(CSession* session, CTablet* tablet, bool sorted) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
if (!tablet)
return setError(TS_ERR_NULL_PTR, "tablet is null");
try {
session->cpp->insertTablet(tablet->cpp, sorted);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
if (!tablet)
return setError(TS_ERR_NULL_PTR, "tablet is null");
try {
session->cpp->insertAlignedTablet(tablet->cpp, sorted);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_session_insert_tablets(CSession* session, int tabletCount, const char* const* deviceIds,
CTablet** tablets, bool sorted) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
std::unordered_map<std::string, Tablet*> tabletMap;
for (int i = 0; i < tabletCount; i++) {
tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
}
session->cpp->insertTablets(tabletMap, sorted);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
TsStatus ts_session_insert_aligned_tablets(CSession* session, int tabletCount,
const char* const* deviceIds, CTablet** tablets,
bool sorted) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
try {
std::unordered_map<std::string, Tablet*> tabletMap;
for (int i = 0; i < tabletCount; i++) {
tabletMap[std::string(deviceIds[i])] = &(tablets[i]->cpp);
}
session->cpp->insertAlignedTablets(tabletMap, sorted);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
/* ============================================================
* Data Insertion — Table Model (Tablet)
* ============================================================ */
TsStatus ts_table_session_insert(CTableSession* session, CTablet* tablet) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
if (!tablet)
return setError(TS_ERR_NULL_PTR, "tablet is null");
try {
session->cpp->insert(tablet->cpp);
return TS_OK;
} catch (const std::exception& e) {
return handleException(e);
}
}
/* ============================================================
* Query — Tree Model
* ============================================================ */
TsStatus ts_session_execute_query(CSession* session, const char* sql, CSessionDataSet** dataSet) {
clearError();
if (!session)
return setError(TS_ERR_NULL_PTR, "session is null");
if (!dataSet)
return setError(TS_ERR_INVALID_PARAM, "dataSet pointer is null");
try {
auto ds = session->cpp->executeQueryStatement(std::string(sql));
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 "";
}
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" */