Skip to content

Commit 137bfbf

Browse files
authored
Backport client fixes to rc/2.0.10: fix DATE NULL handling crash and add missing numeric getters for Column (#18095)
* 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. * Fix C++ client reading FLOAT inference columns declared as DOUBLE (#17759) * Fix C++ client reading FLOAT inference columns declared as DOUBLE CALL INFERENCE returns FLOAT data in TsBlock while the result schema declares DOUBLE. Coerce by actual column type in getDouble/getFloat to avoid Unsupported operation: getDouble when iterating RowRecord. * Move column type coercion test into sessionIT.cpp Avoid a separate test source file; keep the same coverage in session_tests. * Implement numeric widening getters on C++ Column classes Align with Java TsFile: IntColumn/FloatColumn/LongColumn support cross-type getters (e.g. FloatColumn::getDouble). IoTDBRpcDataSet delegates directly to column getters like the Java client. Fixes CALL INFERENCE crash when schema declares DOUBLE but TsBlock stores FLOAT (including RLE-encoded columns).
1 parent b5c8957 commit 137bfbf

7 files changed

Lines changed: 87 additions & 5 deletions

File tree

iotdb-client/client-cpp/src/include/Column.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ class IntColumn : public Column {
196196
ColumnEncoding getEncoding() const override;
197197

198198
int32_t getInt(int32_t position) const override;
199+
int64_t getLong(int32_t position) const override;
200+
float getFloat(int32_t position) const override;
201+
double getDouble(int32_t position) const override;
199202
std::vector<int32_t> getInts() const override;
200203

201204
bool mayHaveNull() const override;
@@ -220,6 +223,7 @@ class FloatColumn : public Column {
220223
ColumnEncoding getEncoding() const override;
221224

222225
float getFloat(int32_t position) const override;
226+
double getDouble(int32_t position) const override;
223227
std::vector<float> getFloats() const override;
224228

225229
bool mayHaveNull() const override;
@@ -244,6 +248,7 @@ class LongColumn : public Column {
244248
ColumnEncoding getEncoding() const override;
245249

246250
int64_t getLong(int32_t position) const override;
251+
double getDouble(int32_t position) const override;
247252
std::vector<int64_t> getLongs() const override;
248253

249254
bool mayHaveNull() const override;

iotdb-client/client-cpp/src/include/Common.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ class Field {
185185
case TSDataType::BLOB:
186186
return !stringV.is_initialized();
187187
case TSDataType::DATE:
188-
return !dateV.is_initialized();
188+
return !dateV.is_initialized() || dateV.value().is_not_a_date();
189189
default:
190190
return true;
191191
}

iotdb-client/client-cpp/src/session/Column.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,18 @@ int32_t IntColumn::getInt(int32_t position) const {
152152
return values_[position + arrayOffset_];
153153
}
154154

155+
int64_t IntColumn::getLong(int32_t position) const {
156+
return values_[position + arrayOffset_];
157+
}
158+
159+
float IntColumn::getFloat(int32_t position) const {
160+
return values_[position + arrayOffset_];
161+
}
162+
163+
double IntColumn::getDouble(int32_t position) const {
164+
return values_[position + arrayOffset_];
165+
}
166+
155167
std::vector<int32_t> IntColumn::getInts() const {
156168
return values_;
157169
}
@@ -204,6 +216,10 @@ float FloatColumn::getFloat(int32_t position) const {
204216
return values_[position + arrayOffset_];
205217
}
206218

219+
double FloatColumn::getDouble(int32_t position) const {
220+
return values_[position + arrayOffset_];
221+
}
222+
207223
std::vector<float> FloatColumn::getFloats() const {
208224
return values_;
209225
}
@@ -256,6 +272,10 @@ int64_t LongColumn::getLong(int32_t position) const {
256272
return values_[position + arrayOffset_];
257273
}
258274

275+
double LongColumn::getDouble(int32_t position) const {
276+
return values_[position + arrayOffset_];
277+
}
278+
259279
std::vector<int64_t> LongColumn::getLongs() const {
260280
return values_;
261281
}

iotdb-client/client-cpp/src/session/Date.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,6 @@ int32_t parseDateExpressionToInt(const IoTDBDate& date) {
5353
}
5454

5555
IoTDBDate parseIntToDate(int32_t dateInt) {
56-
if (dateInt == EMPTY_DATE_INT) {
57-
return IoTDBDate::notADate();
58-
}
5956
const int year = dateInt / 10000;
6057
const int month = (dateInt % 10000) / 100;
6158
const int day = dateInt % 100;

iotdb-client/client-cpp/src/session/SessionC.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1487,8 +1487,10 @@ int32_t ts_row_record_get_date_int32(CRowRecord* record, int index) {
14871487
if (index < 0 || index >= (int)record->cpp->fields.size())
14881488
return 0;
14891489
const Field& f = record->cpp->fields[index];
1490-
if (f.dataType != TSDataType::DATE || !f.dateV.is_initialized())
1490+
if (f.dataType != TSDataType::DATE || !f.dateV.is_initialized() ||
1491+
f.dateV.value().is_not_a_date()) {
14911492
return 0;
1493+
}
14921494
return parseDateExpressionToInt(f.dateV.value());
14931495
}
14941496

iotdb-client/client-cpp/test/cpp/sessionCIT.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,3 +771,33 @@ TEST_CASE("C API - RowRecord and delete data APIs", "[c_rowDelete]") {
771771
REQUIRE(ts_session_delete_timeseries(g_session, pblob) == TS_OK);
772772
REQUIRE(ts_session_delete_database(g_session, sg) == TS_OK);
773773
}
774+
775+
TEST_CASE("C API - Query DATE 1000-01-01", "[c_dateMinYear]") {
776+
CaseReporter cr("c_dateMinYear");
777+
778+
const char* path = "root.ctest.d1.s_date_min";
779+
ensureTimeseries(g_session, path, TS_TYPE_DATE, TS_ENCODING_PLAIN, TS_COMPRESSION_SNAPPY);
780+
781+
const char* deviceId = "root.ctest.d1";
782+
const char* measurements[] = {"s_date_min"};
783+
TSDataType_C types[] = {TS_TYPE_DATE};
784+
TSDate_C dateVal = {1000, 1, 1};
785+
const void* vals[] = {&dateVal};
786+
REQUIRE(ts_session_insert_record(g_session, deviceId, 1000LL, 1, measurements, types, vals) ==
787+
TS_OK);
788+
789+
CSessionDataSet* dataSet = nullptr;
790+
REQUIRE(ts_session_execute_query(g_session,
791+
"select s_date_min from root.ctest.d1 where time=1000",
792+
&dataSet) == TS_OK);
793+
REQUIRE(dataSet != nullptr);
794+
REQUIRE(ts_dataset_has_next(dataSet));
795+
CRowRecord* record = ts_dataset_next(dataSet);
796+
REQUIRE(record != nullptr);
797+
REQUIRE_FALSE(ts_row_record_is_null(record, 0));
798+
REQUIRE(ts_row_record_get_date_int32(record, 0) == 10000101);
799+
ts_row_record_destroy(record);
800+
ts_dataset_destroy(dataSet);
801+
802+
REQUIRE(ts_session_delete_timeseries(g_session, path) == TS_OK);
803+
}

iotdb-client/client-cpp/test/cpp/sessionIT.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,3 +1037,31 @@ TEST_CASE("SessionPool getSession times out when exhausted", "[sessionPool]") {
10371037
reused.release();
10381038
pool->close();
10391039
}
1040+
1041+
TEST_CASE("Query DATE 1000-01-01", "[dateMinYear]") {
1042+
CaseReporter cr("dateMinYear");
1043+
const string timeseries = "root.test.d1.s_date_min";
1044+
if (session->checkTimeseriesExists(timeseries)) {
1045+
session->deleteTimeseries(timeseries);
1046+
}
1047+
session->createTimeseries(timeseries, TSDataType::DATE, TSEncoding::PLAIN,
1048+
CompressionType::SNAPPY);
1049+
1050+
const string deviceId = "root.test.d1";
1051+
const vector<string> measurements = {"s_date_min"};
1052+
const vector<TSDataType::TSDataType> types = {TSDataType::DATE};
1053+
const IoTDBDate dateValue(1000, 1, 1);
1054+
vector<char*> values = {const_cast<char*>(reinterpret_cast<const char*>(&dateValue))};
1055+
session->insertRecord(deviceId, 1000LL, measurements, types, values);
1056+
1057+
unique_ptr<SessionDataSet> sessionDataSet =
1058+
session->executeQueryStatement("select s_date_min from root.test.d1 where time=1000");
1059+
REQUIRE(sessionDataSet->hasNext());
1060+
auto record = sessionDataSet->next();
1061+
REQUIRE(record->fields.size() == 1);
1062+
REQUIRE_FALSE(record->fields[0].isNull());
1063+
REQUIRE(record->fields[0].dateV.value() == dateValue);
1064+
REQUIRE(record->fields[0].dateV.value().toIsoExtendedString() == "1000-01-01");
1065+
1066+
session->deleteTimeseries(timeseries);
1067+
}

0 commit comments

Comments
 (0)