From 01490a6d213bae9e19ce5b465db187940b1576d1 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Thu, 2 Jul 2026 19:04:22 +0800 Subject: [PATCH] Fix C++ client DATE 1000-01-01 query crash Stop treating EMPTY_DATE_INT as a null sentinel when decoding query results, since it encodes the valid date 1000-01-01 and caused C API reads to throw in parseDateExpressionToInt. --- iotdb-client/client-cpp/src/include/Common.h | 2 +- iotdb-client/client-cpp/src/session/Date.cpp | 3 -- .../client-cpp/src/session/SessionC.cpp | 4 ++- .../client-cpp/test/cpp/sessionCIT.cpp | 30 +++++++++++++++++++ .../client-cpp/test/cpp/sessionIT.cpp | 28 +++++++++++++++++ 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/iotdb-client/client-cpp/src/include/Common.h b/iotdb-client/client-cpp/src/include/Common.h index 49dca0bca4b14..f36b38206b8df 100644 --- a/iotdb-client/client-cpp/src/include/Common.h +++ b/iotdb-client/client-cpp/src/include/Common.h @@ -185,7 +185,7 @@ class Field { case TSDataType::BLOB: return !stringV.is_initialized(); case TSDataType::DATE: - return !dateV.is_initialized(); + return !dateV.is_initialized() || dateV.value().is_not_a_date(); default: return true; } diff --git a/iotdb-client/client-cpp/src/session/Date.cpp b/iotdb-client/client-cpp/src/session/Date.cpp index 91095c250dad4..1e6f62edd1d2c 100644 --- a/iotdb-client/client-cpp/src/session/Date.cpp +++ b/iotdb-client/client-cpp/src/session/Date.cpp @@ -53,9 +53,6 @@ int32_t parseDateExpressionToInt(const IoTDBDate& date) { } IoTDBDate parseIntToDate(int32_t dateInt) { - if (dateInt == EMPTY_DATE_INT) { - return IoTDBDate::notADate(); - } const int year = dateInt / 10000; const int month = (dateInt % 10000) / 100; const int day = dateInt % 100; diff --git a/iotdb-client/client-cpp/src/session/SessionC.cpp b/iotdb-client/client-cpp/src/session/SessionC.cpp index 79287cf025235..7365d6ff645bf 100644 --- a/iotdb-client/client-cpp/src/session/SessionC.cpp +++ b/iotdb-client/client-cpp/src/session/SessionC.cpp @@ -1495,8 +1495,10 @@ int32_t ts_row_record_get_date_int32(CRowRecord* record, int index) { 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()) + if (f.dataType != TSDataType::DATE || !f.dateV.is_initialized() || + f.dateV.value().is_not_a_date()) { return 0; + } return parseDateExpressionToInt(f.dateV.value()); } diff --git a/iotdb-client/client-cpp/test/cpp/sessionCIT.cpp b/iotdb-client/client-cpp/test/cpp/sessionCIT.cpp index 0e0791c5d2943..08aace0ca93ca 100644 --- a/iotdb-client/client-cpp/test/cpp/sessionCIT.cpp +++ b/iotdb-client/client-cpp/test/cpp/sessionCIT.cpp @@ -792,3 +792,33 @@ TEST_CASE("C API - RowRecord and delete data APIs", "[c_rowDelete]") { REQUIRE(ts_session_delete_timeseries(g_session, pblob) == TS_OK); REQUIRE(ts_session_delete_database(g_session, sg) == TS_OK); } + +TEST_CASE("C API - Query DATE 1000-01-01", "[c_dateMinYear]") { + CaseReporter cr("c_dateMinYear"); + + const char* path = "root.ctest.d1.s_date_min"; + ensureTimeseries(g_session, path, TS_TYPE_DATE, TS_ENCODING_PLAIN, TS_COMPRESSION_SNAPPY); + + const char* deviceId = "root.ctest.d1"; + const char* measurements[] = {"s_date_min"}; + TSDataType_C types[] = {TS_TYPE_DATE}; + TSDate_C dateVal = {1000, 1, 1}; + const void* vals[] = {&dateVal}; + REQUIRE(ts_session_insert_record(g_session, deviceId, 1000LL, 1, measurements, types, vals) == + TS_OK); + + CSessionDataSet* dataSet = nullptr; + REQUIRE(ts_session_execute_query(g_session, + "select s_date_min from root.ctest.d1 where time=1000", + &dataSet) == TS_OK); + REQUIRE(dataSet != nullptr); + REQUIRE(ts_dataset_has_next(dataSet)); + CRowRecord* record = ts_dataset_next(dataSet); + REQUIRE(record != nullptr); + REQUIRE_FALSE(ts_row_record_is_null(record, 0)); + REQUIRE(ts_row_record_get_date_int32(record, 0) == 10000101); + ts_row_record_destroy(record); + ts_dataset_destroy(dataSet); + + REQUIRE(ts_session_delete_timeseries(g_session, path) == TS_OK); +} diff --git a/iotdb-client/client-cpp/test/cpp/sessionIT.cpp b/iotdb-client/client-cpp/test/cpp/sessionIT.cpp index 3b19f2e2b25d6..97c078d9d5979 100644 --- a/iotdb-client/client-cpp/test/cpp/sessionIT.cpp +++ b/iotdb-client/client-cpp/test/cpp/sessionIT.cpp @@ -1062,3 +1062,31 @@ TEST_CASE("SessionPool getSession times out when exhausted", "[sessionPool]") { reused.release(); pool->close(); } + +TEST_CASE("Query DATE 1000-01-01", "[dateMinYear]") { + CaseReporter cr("dateMinYear"); + const string timeseries = "root.test.d1.s_date_min"; + if (session->checkTimeseriesExists(timeseries)) { + session->deleteTimeseries(timeseries); + } + session->createTimeseries(timeseries, TSDataType::DATE, TSEncoding::PLAIN, + CompressionType::SNAPPY); + + const string deviceId = "root.test.d1"; + const vector measurements = {"s_date_min"}; + const vector types = {TSDataType::DATE}; + const IoTDBDate dateValue(1000, 1, 1); + vector values = {const_cast(reinterpret_cast(&dateValue))}; + session->insertRecord(deviceId, 1000LL, measurements, types, values); + + unique_ptr sessionDataSet = + session->executeQueryStatement("select s_date_min from root.test.d1 where time=1000"); + REQUIRE(sessionDataSet->hasNext()); + auto record = sessionDataSet->next(); + REQUIRE(record->fields.size() == 1); + REQUIRE_FALSE(record->fields[0].isNull()); + REQUIRE(record->fields[0].dateV.value() == dateValue); + REQUIRE(record->fields[0].dateV.value().toIsoExtendedString() == "1000-01-01"); + + session->deleteTimeseries(timeseries); +}