This is an automated email from the ASF dual-hosted git repository.
jt2594838 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 3767282ad12 feat(client-cpp): add SessionC DATE/BLOB support and
RowRecord getters (#17956)
3767282ad12 is described below
commit 3767282ad1210a11becf7c32499a899bb5acc845
Author: Hongzhi Gao <[email protected]>
AuthorDate: Tue Jun 16 16:59:09 2026 +0800
feat(client-cpp): add SessionC DATE/BLOB support and RowRecord getters
(#17956)
* feat(client-cpp): add SessionC DATE/BLOB support and RowRecord getters
Introduce TSDate_C for typed DATE inserts, ts_row_record_get_date_int32
and ts_row_record_get_string_byte_length, and expand c_rowDelete IT to
cover DATE/BLOB readback and deletion paths.
* test(client-cpp): cover TEXT and STRING in c_rowDelete IT
Add TS_TYPE_STRING readback assertions alongside explicit TEXT checks
in the RowRecord integration test.
* chore(client-cpp): remove redundant TSDate_C comment
---
iotdb-client/client-cpp/src/include/SessionC.h | 12 +++++++
iotdb-client/client-cpp/src/session/SessionC.cpp | 33 +++++++++++++++++++
iotdb-client/client-cpp/test/cpp/sessionCIT.cpp | 42 +++++++++++++++++++-----
3 files changed, 79 insertions(+), 8 deletions(-)
diff --git a/iotdb-client/client-cpp/src/include/SessionC.h
b/iotdb-client/client-cpp/src/include/SessionC.h
index 5c850c09c12..fdce5801a9d 100644
--- a/iotdb-client/client-cpp/src/include/SessionC.h
+++ b/iotdb-client/client-cpp/src/include/SessionC.h
@@ -105,6 +105,12 @@ typedef enum {
typedef enum { TS_COL_TAG = 0, TS_COL_FIELD = 1, TS_COL_ATTRIBUTE = 2 }
TSColumnCategory_C;
+typedef struct {
+ int year;
+ int month;
+ int day;
+} TSDate_C;
+
/* ============================================================
* Session Lifecycle — Tree Model
* ============================================================ */
@@ -382,6 +388,12 @@ double ts_row_record_get_double(CRowRecord* record, int
index);
const char* ts_row_record_get_string(CRowRecord* record, int index);
+/** Returns YYYYMMDD for DATE fields; 0 if record is null, index is out of
range, field is null, or not DATE. */
+int32_t ts_row_record_get_date_int32(CRowRecord* record, int index);
+
+/** Byte length of the string/binary field (TEXT, BLOB, STRING, OBJECT, ...);
0 if null or out of range. */
+size_t ts_row_record_get_string_byte_length(CRowRecord* record, int index);
+
/** Returns TS_TYPE_INVALID if record is null or index is out of range. */
TSDataType_C ts_row_record_get_data_type(CRowRecord* record, int index);
diff --git a/iotdb-client/client-cpp/src/session/SessionC.cpp
b/iotdb-client/client-cpp/src/session/SessionC.cpp
index e7c43cdb9f1..7d86bd348f8 100644
--- a/iotdb-client/client-cpp/src/session/SessionC.cpp
+++ b/iotdb-client/client-cpp/src/session/SessionC.cpp
@@ -18,6 +18,7 @@
*/
#include "SessionC.h"
+#include "Date.h"
#include "Session.h"
#include "TableSession.h"
#include "TableSessionBuilder.h"
@@ -184,6 +185,12 @@ static std::vector<char*> toCharPtrVec(const TSDataType_C*
types, const void* co
result[i] = reinterpret_cast<char*>(p);
break;
}
+ case TS_TYPE_DATE: {
+ const TSDate_C* src = static_cast<const TSDate_C*>(values[i]);
+ IoTDBDate* p = new IoTDBDate(src->year, src->month, src->day);
+ result[i] = reinterpret_cast<char*>(p);
+ break;
+ }
case TS_TYPE_TEXT:
case TS_TYPE_STRING:
case TS_TYPE_BLOB:
@@ -219,6 +226,9 @@ static void freeCharPtrVec(std::vector<char*>& vec, const
TSDataType_C* types, i
case TS_TYPE_DOUBLE:
delete reinterpret_cast<double*>(vec[i]);
break;
+ case TS_TYPE_DATE:
+ delete reinterpret_cast<IoTDBDate*>(vec[i]);
+ break;
default:
delete[] vec[i];
break;
@@ -1471,6 +1481,29 @@ const char* ts_row_record_get_string(CRowRecord* record,
int index) {
return "";
}
+int32_t ts_row_record_get_date_int32(CRowRecord* record, int index) {
+ if (!record || !record->cpp)
+ return 0;
+ if (index < 0 || index >= (int)record->cpp->fields.size())
+ return 0;
+ const Field& f = record->cpp->fields[index];
+ if (f.dataType != TSDataType::DATE || !f.dateV.is_initialized())
+ return 0;
+ return parseDateExpressionToInt(f.dateV.value());
+}
+
+size_t ts_row_record_get_string_byte_length(CRowRecord* record, int index) {
+ if (!record || !record->cpp)
+ return 0;
+ if (index < 0 || index >= (int)record->cpp->fields.size())
+ return 0;
+ const Field& f = record->cpp->fields[index];
+ if (f.stringV.is_initialized()) {
+ return f.stringV.value().size();
+ }
+ return 0;
+}
+
TSDataType_C ts_row_record_get_data_type(CRowRecord* record, int index) {
if (!record || !record->cpp)
return TS_TYPE_INVALID;
diff --git a/iotdb-client/client-cpp/test/cpp/sessionCIT.cpp
b/iotdb-client/client-cpp/test/cpp/sessionCIT.cpp
index f56e10bb5ae..9db5361635d 100644
--- a/iotdb-client/client-cpp/test/cpp/sessionCIT.cpp
+++ b/iotdb-client/client-cpp/test/cpp/sessionCIT.cpp
@@ -687,7 +687,10 @@ TEST_CASE("C API - RowRecord and delete data APIs",
"[c_rowDelete]") {
const char* pf = "root.cov_types.d1.sf";
const char* pd = "root.cov_types.d1.sd";
const char* pt = "root.cov_types.d1.st";
- const char* tpaths[] = {pb, pi, pf, pd, pt};
+ const char* pstr = "root.cov_types.d1.sstr";
+ const char* pdate = "root.cov_types.d1.sdate";
+ const char* pblob = "root.cov_types.d1.sblob";
+ const char* tpaths[] = {pb, pi, pf, pd, pt, pstr, pdate, pblob};
for (const char* tp : tpaths) {
dropTimeseriesIfExists(g_session, tp);
}
@@ -701,36 +704,56 @@ TEST_CASE("C API - RowRecord and delete data APIs",
"[c_rowDelete]") {
TS_COMPRESSION_SNAPPY) == TS_OK);
REQUIRE(ts_session_create_timeseries(g_session, pt, TS_TYPE_TEXT,
TS_ENCODING_PLAIN,
TS_COMPRESSION_SNAPPY) == TS_OK);
+ REQUIRE(ts_session_create_timeseries(g_session, pstr, TS_TYPE_STRING,
TS_ENCODING_PLAIN,
+ TS_COMPRESSION_SNAPPY) == TS_OK);
+ REQUIRE(ts_session_create_timeseries(g_session, pdate, TS_TYPE_DATE,
TS_ENCODING_PLAIN,
+ TS_COMPRESSION_SNAPPY) == TS_OK);
+ REQUIRE(ts_session_create_timeseries(g_session, pblob, TS_TYPE_BLOB,
TS_ENCODING_PLAIN,
+ TS_COMPRESSION_SNAPPY) == TS_OK);
const char* dev = "root.cov_types.d1";
- const char* names[] = {"sb", "si", "sf", "sd", "st"};
+ const char* names[] = {"sb", "si", "sf", "sd", "st", "sstr", "sdate",
"sblob"};
TSDataType_C types[] = {TS_TYPE_BOOLEAN, TS_TYPE_INT32, TS_TYPE_FLOAT,
TS_TYPE_DOUBLE,
- TS_TYPE_TEXT};
+ TS_TYPE_TEXT, TS_TYPE_STRING, TS_TYPE_DATE,
TS_TYPE_BLOB};
bool bv = true;
int32_t iv = 42;
float fv = 2.5f;
double dv = 3.25;
const char* tv = "hi";
- const void* vals[] = {&bv, &iv, &fv, &dv, tv};
- REQUIRE(ts_session_insert_record(g_session, dev, 500LL, 5, names, types,
vals) == TS_OK);
+ const char* strVal = "hello";
+ TSDate_C dateVal = {2025, 5, 7};
+ const char* blobVal = "blobXY";
+ const void* vals[] = {&bv, &iv, &fv, &dv, tv, strVal, &dateVal, blobVal};
+ REQUIRE(ts_session_insert_record(g_session, dev, 500LL, 8, names, types,
vals) == TS_OK);
CSessionDataSet* dataSet = nullptr;
REQUIRE(ts_session_execute_query(g_session,
- "select sb,si,sf,sd,st from
root.cov_types.d1 where time=500",
+ "select sb,si,sf,sd,st,sstr,sdate,sblob
from root.cov_types.d1 "
+ "where time=500",
&dataSet) == TS_OK);
REQUIRE(dataSet != nullptr);
REQUIRE(ts_dataset_has_next(dataSet));
CRowRecord* record = ts_dataset_next(dataSet);
REQUIRE(record != nullptr);
REQUIRE(ts_row_record_get_timestamp(record) == 500LL);
- REQUIRE(ts_row_record_get_field_count(record) == 5);
+ REQUIRE(ts_row_record_get_field_count(record) == 8);
REQUIRE_FALSE(ts_row_record_is_null(record, 0));
REQUIRE(ts_row_record_get_bool(record, 0) == true);
REQUIRE(ts_row_record_get_int32(record, 1) == 42);
REQUIRE(std::fabs(ts_row_record_get_float(record, 2) - 2.5f) < 1e-4f);
REQUIRE(std::fabs(ts_row_record_get_double(record, 3) - 3.25) < 1e-9);
+ REQUIRE(ts_row_record_get_data_type(record, 4) == TS_TYPE_TEXT);
REQUIRE(std::string(ts_row_record_get_string(record, 4)) == "hi");
- REQUIRE(ts_row_record_get_data_type(record, 0) == TS_TYPE_BOOLEAN);
+ REQUIRE(ts_row_record_get_string_byte_length(record, 4) == 2);
+ REQUIRE(ts_row_record_get_data_type(record, 5) == TS_TYPE_STRING);
+ REQUIRE(std::string(ts_row_record_get_string(record, 5)) == "hello");
+ REQUIRE(ts_row_record_get_string_byte_length(record, 5) == 5);
+ REQUIRE(ts_row_record_get_date_int32(record, 6) == 20250507);
+ REQUIRE(std::string(ts_row_record_get_string(record, 7)) == "blobXY");
+ REQUIRE(ts_row_record_get_string_byte_length(record, 7) == 6);
+ for (int i = 0; i < 8; i++) {
+ REQUIRE(ts_row_record_get_data_type(record, i) == types[i]);
+ }
ts_row_record_destroy(record);
ts_dataset_destroy(dataSet);
@@ -743,5 +766,8 @@ TEST_CASE("C API - RowRecord and delete data APIs",
"[c_rowDelete]") {
REQUIRE(ts_session_delete_timeseries(g_session, pf) == TS_OK);
REQUIRE(ts_session_delete_timeseries(g_session, pd) == TS_OK);
REQUIRE(ts_session_delete_timeseries(g_session, pt) == TS_OK);
+ REQUIRE(ts_session_delete_timeseries(g_session, pstr) == TS_OK);
+ REQUIRE(ts_session_delete_timeseries(g_session, pdate) == TS_OK);
+ REQUIRE(ts_session_delete_timeseries(g_session, pblob) == TS_OK);
REQUIRE(ts_session_delete_database(g_session, sg) == TS_OK);
}