This is an automated email from the ASF dual-hosted git repository.
jiangtian 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 b85ac145a4c Fix C++ client time column access causing UB for non-long
types (#17397) (#17400)
b85ac145a4c is described below
commit b85ac145a4c0e040ff81d51867ff7c5f616027a2
Author: ZIHAN DAI <[email protected]>
AuthorDate: Wed Apr 1 13:11:45 2026 +1100
Fix C++ client time column access causing UB for non-long types (#17397)
(#17400)
Add tsBlockColumnIndex < 0 guards to all ByTsBlockColumnIndex getter
methods that were missing them. When the time pseudo-column is accessed
through a typed getter other than getLong or getString, throw
IoTDBException instead of causing undefined behavior via getColumn(-1).
Signed-off-by: Zihan Dai <[email protected]>
---
.../client-cpp/src/main/IoTDBRpcDataSet.cpp | 19 ++++++++++++
iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp | 35 ++++++++++++++++++++++
2 files changed, 54 insertions(+)
diff --git a/iotdb-client/client-cpp/src/main/IoTDBRpcDataSet.cpp
b/iotdb-client/client-cpp/src/main/IoTDBRpcDataSet.cpp
index eba013598e4..2afef2b9968 100644
--- a/iotdb-client/client-cpp/src/main/IoTDBRpcDataSet.cpp
+++ b/iotdb-client/client-cpp/src/main/IoTDBRpcDataSet.cpp
@@ -271,8 +271,15 @@ boost::optional<bool> IoTDBRpcDataSet::getBoolean(const
std::string& columnName)
return getBooleanByTsBlockColumnIndex(index);
}
+// Note: tsBlockColumnIndex < 0 indicates the time pseudo-column in tree model.
+// Only getLong and getString support reading the time column directly.
+// All other typed getters throw IoTDBException to prevent undefined behavior
+// from accessing valueColumns_ with a negative index.
boost::optional<bool> IoTDBRpcDataSet::getBooleanByTsBlockColumnIndex(int32_t
tsBlockColumnIndex) {
checkRecord();
+ if (tsBlockColumnIndex < 0) {
+ throw IoTDBException("Cannot read boolean from time column");
+ }
if (!isNull(tsBlockColumnIndex, tsBlockIndex_)) {
lastReadWasNull_ = false;
return
curTsBlock_->getColumn(tsBlockColumnIndex)->getBoolean(tsBlockIndex_);
@@ -295,6 +302,9 @@ boost::optional<double> IoTDBRpcDataSet::getDouble(const
std::string& columnName
boost::optional<double> IoTDBRpcDataSet::getDoubleByTsBlockColumnIndex(int32_t
tsBlockColumnIndex) {
checkRecord();
+ if (tsBlockColumnIndex < 0) {
+ throw IoTDBException("Cannot read double from time column");
+ }
if (!isNull(tsBlockColumnIndex, tsBlockIndex_)) {
lastReadWasNull_ = false;
return
curTsBlock_->getColumn(tsBlockColumnIndex)->getDouble(tsBlockIndex_);
@@ -317,6 +327,9 @@ boost::optional<float> IoTDBRpcDataSet::getFloat(const
std::string& columnName)
boost::optional<float> IoTDBRpcDataSet::getFloatByTsBlockColumnIndex(int32_t
tsBlockColumnIndex) {
checkRecord();
+ if (tsBlockColumnIndex < 0) {
+ throw IoTDBException("Cannot read float from time column");
+ }
if (!isNull(tsBlockColumnIndex, tsBlockIndex_)) {
lastReadWasNull_ = false;
return
curTsBlock_->getColumn(tsBlockColumnIndex)->getFloat(tsBlockIndex_);
@@ -339,6 +352,9 @@ boost::optional<int32_t> IoTDBRpcDataSet::getInt(const
std::string& columnName)
boost::optional<int32_t> IoTDBRpcDataSet::getIntByTsBlockColumnIndex(int32_t
tsBlockColumnIndex) {
checkRecord();
+ if (tsBlockColumnIndex < 0) {
+ throw IoTDBException("Cannot read int32 from time column");
+ }
if (!isNull(tsBlockColumnIndex, tsBlockIndex_)) {
lastReadWasNull_ = false;
TSDataType::TSDataType dataType =
curTsBlock_->getColumn(tsBlockColumnIndex)->getDataType();
@@ -395,6 +411,9 @@ std::shared_ptr<Binary> IoTDBRpcDataSet::getBinary(const
std::string& columnName
std::shared_ptr<Binary> IoTDBRpcDataSet::getBinaryByTsBlockColumnIndex(int32_t
tsBlockColumnIndex) {
checkRecord();
+ if (tsBlockColumnIndex < 0) {
+ throw IoTDBException("Cannot read binary from time column");
+ }
if (!isNull(tsBlockColumnIndex, tsBlockIndex_)) {
lastReadWasNull_ = false;
return
curTsBlock_->getColumn(tsBlockColumnIndex)->getBinary(tsBlockIndex_);
diff --git a/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp
b/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp
index d08e0c3fbb0..af7f13855a4 100644
--- a/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp
+++ b/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp
@@ -504,6 +504,41 @@ TEST_CASE("Test insertTablet multi datatype",
"[testInsertTabletMultiDatatype]")
REQUIRE(count == 100);
}
+TEST_CASE("Test boolean column via DataIterator",
"[testBooleanColumnDataIterator]") {
+ CaseReporter cr("testBooleanColumnDataIterator");
+ string deviceId = "root.test.d1";
+ string timeseries = "root.test.d1.s1";
+
+ if (session->checkTimeseriesExists(timeseries)) {
+ session->deleteTimeseries(timeseries);
+ }
+ session->createTimeseries(timeseries, TSDataType::BOOLEAN,
TSEncoding::PLAIN, CompressionType::SNAPPY);
+
+ // Insert boolean values: even timestamps get true, odd get false
+ vector<string> measurements = {"s1"};
+ vector<TSDataType::TSDataType> types = {TSDataType::BOOLEAN};
+ for (int64_t time = 0; time < 10; time++) {
+ bool val = (time % 2 == 0);
+ vector<char*> values = {(char*)&val};
+ session->insertRecord(deviceId, time, measurements, types, values);
+ }
+
+ unique_ptr<SessionDataSet> sessionDataSet =
session->executeQueryStatement("select s1 from root.test.d1");
+ auto dataIter = sessionDataSet->getIterator();
+ sessionDataSet->setFetchSize(1024);
+ int count = 0;
+ while (dataIter.next()) {
+ bool expected = (count % 2 == 0);
+ // Column 1 is Time, column 2 is s1
+ REQUIRE(dataIter.getBooleanByIndex(2).value() == expected);
+ // Accessing time column (index 1) as boolean should throw
+ REQUIRE_THROWS_AS(dataIter.getBooleanByIndex(1), IoTDBException);
+ count++;
+ }
+ REQUIRE(count == 10);
+ session->deleteTimeseries(timeseries);
+}
+
TEST_CASE("Test Last query ", "[testLastQuery]") {
CaseReporter cr("testLastQuery");
prepareTimeseries();