This is an automated email from the ASF dual-hosted git repository.

ColinLeeo pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/tsfile.git


The following commit(s) were added to refs/heads/develop by this push:
     new 705c971be Add standalone Cppcheck CI (#895)
705c971be is described below

commit 705c971beb65dd937bf2187f2050e9d693a1b9a7
Author: Colin Lee <[email protected]>
AuthorDate: Tue Aug 4 17:13:45 2026 +0800

    Add standalone Cppcheck CI (#895)
    
    * Add standalone Cppcheck CI
    
    * Fix existing Cppcheck findings
    
    * Avoid goto in C example cleanup
---
 .github/workflows/cppcheck.yml                     | 167 +++++++++++++++++++++
 cpp/examples/c_examples/demo_write.c               | 105 +++++++++----
 cpp/pom.xml                                        |  26 ++++
 cpp/src/common/path.h                              |   5 +-
 cpp/src/common/row_record.h                        |  11 +-
 cpp/src/common/tsblock/tsblock.cc                  |  18 ++-
 cpp/src/common/tsblock/tsblock.h                   |   8 +-
 cpp/src/common/tsblock/tuple_desc.h                |   4 +-
 cpp/src/compress/gzip_compressor.cc                |   8 +-
 cpp/src/file/write_file.h                          |   2 +-
 cpp/src/parser/path_visitor.cpp                    |   3 +-
 cpp/src/reader/expression.cc                       |  12 +-
 cpp/src/reader/filter/binary_filter.h              |   2 +-
 cpp/src/reader/table_result_set.cc                 |  20 ++-
 cpp/test/common/row_record_test.cc                 |   1 +
 .../reader/expression_test.cc}                     |  41 +++--
 16 files changed, 347 insertions(+), 86 deletions(-)

diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml
new file mode 100644
index 000000000..5676a965c
--- /dev/null
+++ b/.github/workflows/cppcheck.yml
@@ -0,0 +1,167 @@
+# 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.
+
+name: Cppcheck
+
+on:
+  push:
+    branches:
+      - develop
+      - iotdb
+      - rc/*
+    paths:
+      - '.github/workflows/cppcheck.yml'
+      - '.mvn/**'
+      - 'cpp/**'
+      - 'mvnw'
+      - 'pom.xml'
+  pull_request:
+    branches:
+      - develop
+      - dev/*
+      - iotdb
+      - rc/*
+    paths:
+      - '.github/workflows/cppcheck.yml'
+      - '.mvn/**'
+      - 'cpp/**'
+      - 'mvnw'
+      - 'pom.xml'
+  workflow_dispatch:
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+permissions:
+  contents: read
+
+env:
+  CPPCHECK_VERSION: 2.17.1
+  CPPCHECK_SOURCE_SHA256: 
bfd681868248ec03855ca7c2aea7bcb1f39b8b18860d76aec805a92a967b966c
+  MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false 
-Dmaven.wagon.http.retryHandler.class=standard 
-Dmaven.wagon.http.retryHandler.count=3
+  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
+
+jobs:
+  cppcheck:
+    runs-on: ubuntu-24.04
+    timeout-minutes: 30
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v7
+
+      - name: Set up JDK 17
+        uses: actions/[email protected]
+        with:
+          distribution: corretto
+          java-version: 17
+
+      - name: Cache Maven packages
+        uses: actions/cache@v6
+        with:
+          path: ~/.m2
+          key: ${{ runner.os }}-m2-cppcheck-${{ hashFiles('**/pom.xml') }}
+          restore-keys: ${{ runner.os }}-m2-
+
+      - name: Cache Cppcheck
+        uses: actions/cache@v6
+        with:
+          path: ~/.cache/cppcheck/${{ env.CPPCHECK_VERSION }}
+          key: cppcheck-${{ runner.os }}-${{ runner.arch }}-${{ 
env.CPPCHECK_VERSION }}-${{ env.CPPCHECK_SOURCE_SHA256 }}
+
+      - name: Install dependencies
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y uuid-dev
+
+      - name: Install Cppcheck
+        shell: bash
+        run: |
+          set -euo pipefail
+
+          cppcheck_home="${HOME}/.cache/cppcheck/${CPPCHECK_VERSION}"
+          if [[ ! -x "${cppcheck_home}/bin/cppcheck" ]]; then
+            build_root="$(mktemp -d)"
+            archive="${build_root}/cppcheck.tar.gz"
+            source_dir="${build_root}/source"
+            build_dir="${build_root}/build"
+
+            mkdir -p "${source_dir}" "${build_dir}" "${cppcheck_home}"
+            curl --fail --location --retry 3 \
+              
"https://github.com/cppcheck-opensource/cppcheck/archive/refs/tags/${CPPCHECK_VERSION}.tar.gz";
 \
+              --output "${archive}"
+            echo "${CPPCHECK_SOURCE_SHA256}  ${archive}" | sha256sum --check -
+            tar -xzf "${archive}" --strip-components=1 -C "${source_dir}"
+
+            cmake -S "${source_dir}" -B "${build_dir}" \
+              -DCMAKE_BUILD_TYPE=Release \
+              -DUSE_MATCHCOMPILER=ON \
+              -DBUILD_GUI=OFF \
+              -DBUILD_TESTS=OFF \
+              -DCMAKE_INSTALL_PREFIX="${cppcheck_home}"
+            cmake --build "${build_dir}" --parallel "$(nproc)"
+            cmake --install "${build_dir}"
+          fi
+
+          echo "${cppcheck_home}/bin" >> "${GITHUB_PATH}"
+          "${cppcheck_home}/bin/cppcheck" --version
+
+      - name: Generate compilation database with Maven
+        run: |
+          ./mvnw -P with-cpp,with-cppcheck -pl cpp \
+            -Dbuild.test=OFF \
+            cmake:generate@cmake-generate-test-compile
+          test -s cpp/target/build/compile_commands.json
+
+      - name: Run Cppcheck
+        shell: bash
+        run: |
+          mkdir -p cpp/target/cppcheck
+
+          set +e
+          cppcheck \
+            --project=cpp/target/build/compile_commands.json \
+            --file-filter='*cpp/src/*' \
+            --file-filter='*cpp/tools/*' \
+            --file-filter='*cpp/examples/*' \
+            --enable=warning,performance,portability \
+            --check-level=exhaustive \
+            --inline-suppr \
+            --suppress=missingIncludeSystem \
+            --suppress=unusedFunction \
+            --suppress='*:*/third_party/*' \
+            --suppress='*:*/target/*' \
+            --suppress='*:*/parser/generated/*' \
+            --error-exitcode=2 \
+            --template=gcc \
+            --output-file=cpp/target/cppcheck/cppcheck.txt \
+            --quiet \
+            -j "$(nproc)"
+          cppcheck_status=$?
+          set -e
+
+          cat cpp/target/cppcheck/cppcheck.txt
+          exit "${cppcheck_status}"
+
+      - name: Upload Cppcheck report
+        if: always()
+        uses: actions/upload-artifact@v7
+        with:
+          name: cppcheck-report
+          path: cpp/target/cppcheck/cppcheck.txt
+          if-no-files-found: ignore
diff --git a/cpp/examples/c_examples/demo_write.c 
b/cpp/examples/c_examples/demo_write.c
index cf5ffea28..7c795452e 100644
--- a/cpp/examples/c_examples/demo_write.c
+++ b/cpp/examples/c_examples/demo_write.c
@@ -33,25 +33,69 @@ static char* duplicate_string(const char* src) {
     return dst;
 }
 
+static ERRNO cleanup_write_tsfile_resources(WriteFile* file,
+                                            TsFileWriter writer, Tablet* 
tablet,
+                                            TableSchema* table_schema,
+                                            ERRNO code) {
+    if (*tablet != NULL) {
+        free_tablet(tablet);
+    }
+
+    if (writer != NULL) {
+        ERRNO close_code = tsfile_writer_close(writer);
+        if (code == RET_OK) {
+            code = close_code;
+        }
+    }
+
+    if (table_schema->table_name != NULL ||
+        table_schema->column_schemas != NULL) {
+        free_table_schema(*table_schema);
+    }
+
+    if (*file != NULL) {
+        free_write_file(file);
+    }
+
+    if (code != RET_OK) {
+        printf("get err no: %d", code);
+    }
+    return code;
+}
+
 // This example shows you how to write tsfile.
 ERRNO write_tsfile() {
     ERRNO code = 0;
+    WriteFile file = NULL;
+    TsFileWriter writer = NULL;
+    Tablet tablet = NULL;
+    TableSchema table_schema = {0};
+
     code = set_global_compression(TS_COMPRESSION_LZ4);
     if (code != RET_OK) {
-        return code;
+        return cleanup_write_tsfile_resources(&file, writer, &tablet,
+                                              &table_schema, code);
     }
     code = set_datatype_encoding(TS_DATATYPE_INT32, TS_ENCODING_TS_2DIFF);
     if (code != RET_OK) {
-        return code;
+        return cleanup_write_tsfile_resources(&file, writer, &tablet,
+                                              &table_schema, code);
     }
     char* table_name = "table1";
 
     // Create table schema to describe a table in a tsfile.
-    TableSchema table_schema;
     table_schema.table_name = duplicate_string(table_name);
-    table_schema.column_num = 3;
+    if (table_schema.table_name == NULL) {
+        return cleanup_write_tsfile_resources(&file, writer, &tablet,
+                                              &table_schema, RET_OOM);
+    }
     table_schema.column_schemas =
-        (ColumnSchema*)malloc(sizeof(ColumnSchema) * 3);
+        (ColumnSchema*)calloc(3, sizeof(ColumnSchema));
+    if (table_schema.column_schemas == NULL) {
+        return cleanup_write_tsfile_resources(&file, writer, &tablet,
+                                              &table_schema, RET_OOM);
+    }
+    table_schema.column_num = 3;
     table_schema.column_schemas[0] =
         (ColumnSchema){.column_name = duplicate_string("id1"),
                        .data_type = TS_DATATYPE_STRING,
@@ -64,22 +108,37 @@ ERRNO write_tsfile() {
         (ColumnSchema){.column_name = duplicate_string("s1"),
                        .data_type = TS_DATATYPE_INT32,
                        .column_category = FIELD};
+    if (table_schema.column_schemas[0].column_name == NULL ||
+        table_schema.column_schemas[1].column_name == NULL ||
+        table_schema.column_schemas[2].column_name == NULL) {
+        return cleanup_write_tsfile_resources(&file, writer, &tablet,
+                                              &table_schema, RET_OOM);
+    }
 
     remove("test_c.tsfile");
     // Create a file with specify path to write tsfile.
-    WriteFile file = write_file_new("test_c.tsfile", &code);
-    HANDLE_ERROR(code);
+    file = write_file_new("test_c.tsfile", &code);
+    if (code != RET_OK) {
+        return cleanup_write_tsfile_resources(&file, writer, &tablet,
+                                              &table_schema, code);
+    }
 
     // Create tsfile writer with specify table schema.
-    TsFileWriter writer = tsfile_writer_new(file, &table_schema, &code);
-    HANDLE_ERROR(code);
+    writer = tsfile_writer_new(file, &table_schema, &code);
+    if (code != RET_OK) {
+        return cleanup_write_tsfile_resources(&file, writer, &tablet,
+                                              &table_schema, code);
+    }
 
     // Create tablet to insert data.
-    Tablet tablet =
-        tablet_new((char*[]){"id1", "id2", "s1"},
-                   (TSDataType[]){TS_DATATYPE_STRING, TS_DATATYPE_STRING,
-                                  TS_DATATYPE_INT32},
-                   3, 5);
+    tablet = tablet_new((char*[]){"id1", "id2", "s1"},
+                        (TSDataType[]){TS_DATATYPE_STRING, TS_DATATYPE_STRING,
+                                       TS_DATATYPE_INT32},
+                        3, 5);
+    if (tablet == NULL) {
+        return cleanup_write_tsfile_resources(&file, writer, &tablet,
+                                              &table_schema, RET_OOM);
+    }
 
     for (int row = 0; row < 5; row++) {
         Timestamp timestamp = row;
@@ -92,19 +151,7 @@ ERRNO write_tsfile() {
     }
 
     // Write tablet data.
-    HANDLE_ERROR(tsfile_writer_write(writer, tablet));
-
-    // Free tablet.
-    free_tablet(&tablet);
-
-    // Free table schema we used before.
-    free_table_schema(table_schema);
-
-    // Close writer.
-    HANDLE_ERROR(tsfile_writer_close(writer));
-
-    // Close write file after closing writer.
-    free_write_file(&file);
-
-    return 0;
+    code = tsfile_writer_write(writer, tablet);
+    return cleanup_write_tsfile_resources(&file, writer, &tablet, 
&table_schema,
+                                          code);
 }
diff --git a/cpp/pom.xml b/cpp/pom.xml
index 9b47ce0a7..e94debff2 100644
--- a/cpp/pom.xml
+++ b/cpp/pom.xml
@@ -292,6 +292,32 @@
                 </plugins>
             </build>
         </profile>
+        <!--
+            Generate compile_commands.json for the standalone Cppcheck CI job.
+            This profile only changes the CMake configure step; it does not run
+            Cppcheck or add static analysis to normal C++ builds.
+        -->
+        <profile>
+            <id>with-cppcheck</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>com.googlecode.cmake-maven-project</groupId>
+                        <artifactId>cmake-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>cmake-generate-test-compile</id>
+                                <configuration>
+                                    <options combine.children="append">
+                                        
<option>-DCMAKE_EXPORT_COMPILE_COMMANDS=ON</option>
+                                    </options>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
         <profile>
             <id>.java-9-and-above</id>
             <activation>
diff --git a/cpp/src/common/path.h b/cpp/src/common/path.h
index f09b8c316..05f988696 100644
--- a/cpp/src/common/path.h
+++ b/cpp/src/common/path.h
@@ -40,9 +40,8 @@ struct Path {
 
     Path(std::string& device, std::string& measurement)
         : measurement_(measurement),
-          device_id_(std::make_shared<StringArrayDeviceID>(device)) {
-        full_path_ = device + "." + measurement;
-    }
+          device_id_(std::make_shared<StringArrayDeviceID>(device)),
+          full_path_(device + "." + measurement) {}
 
     Path(const std::string& path_sc, bool if_split = true) {
         if (!path_sc.empty()) {
diff --git a/cpp/src/common/row_record.h b/cpp/src/common/row_record.h
index d6da63efd..a640ecc29 100644
--- a/cpp/src/common/row_record.h
+++ b/cpp/src/common/row_record.h
@@ -188,8 +188,8 @@ FORCE_INLINE Field* make_null_literal() {
 
 class RowRecord {
    public:
-    explicit RowRecord(uint32_t col_num) : col_num_(col_num) {
-        fields_ = new std::vector<Field*>();
+    explicit RowRecord(uint32_t col_num)
+        : time_(0), col_num_(col_num), fields_(new std::vector<Field*>()) {
         fields_->reserve(col_num);
         for (uint32_t i = 0; i < col_num; ++i) {
             Field* val = make_null_literal();
@@ -197,8 +197,8 @@ class RowRecord {
         }
     }
 
-    RowRecord(int64_t time, uint32_t col_num) : time_(time), col_num_(col_num) 
{
-        fields_ = new std::vector<Field*>();
+    RowRecord(int64_t time, uint32_t col_num)
+        : time_(time), col_num_(col_num), fields_(new std::vector<Field*>()) {
         fields_->reserve(col_num_);
         for (uint32_t i = 0; i < col_num_; ++i) {
             Field* val = make_null_literal();
@@ -206,6 +206,9 @@ class RowRecord {
         }
     }
 
+    RowRecord(const RowRecord&) = delete;
+    RowRecord& operator=(const RowRecord&) = delete;
+
     ~RowRecord() {
         if (fields_) {
             int size = fields_->size();
diff --git a/cpp/src/common/tsblock/tsblock.cc 
b/cpp/src/common/tsblock/tsblock.cc
index 9d69566be..fd29f9f07 100644
--- a/cpp/src/common/tsblock/tsblock.cc
+++ b/cpp/src/common/tsblock/tsblock.cc
@@ -18,6 +18,7 @@
  */
 #include "tsblock.h"
 
+#include <cstring>
 #include <string>
 
 namespace common {
@@ -73,29 +74,32 @@ void TsBlock::write_data(ByteStream* __restrict byte_stream,
     std::string strval;
     switch (type) {
         case common::INT64: {
-            int64_t ival = *reinterpret_cast<int64_t*>(val);
+            int64_t ival;
+            std::memcpy(&ival, val, sizeof(ival));
             strval = std::to_string(ival);
             break;
         }
         case common::INT32: {
-            int32_t ival = *reinterpret_cast<int32_t*>(val);
+            int32_t ival;
+            std::memcpy(&ival, val, sizeof(ival));
             strval = std::to_string(ival);
             break;
         }
         case common::FLOAT: {
-            float ival = *reinterpret_cast<float*>(
-                val);  // cppcheck-suppress invalidPointerCast
+            float ival;
+            std::memcpy(&ival, val, sizeof(ival));
             strval = std::to_string(ival);
             break;
         }
         case common::DOUBLE: {
-            double ival = *reinterpret_cast<double*>(
-                val);  // cppcheck-suppress invalidPointerCast
+            double ival;
+            std::memcpy(&ival, val, sizeof(ival));
             strval = std::to_string(ival);
             break;
         }
         case common::BOOLEAN: {
-            bool ival = *reinterpret_cast<bool*>(val);
+            bool ival;
+            std::memcpy(&ival, val, sizeof(ival));
             if (ival) {
                 strval = "true";
             } else {
diff --git a/cpp/src/common/tsblock/tsblock.h b/cpp/src/common/tsblock/tsblock.h
index b68af1611..7959f7c36 100644
--- a/cpp/src/common/tsblock/tsblock.h
+++ b/cpp/src/common/tsblock/tsblock.h
@@ -21,6 +21,8 @@
 
 #include <stdint.h>
 
+#include <cstring>
+
 #include "common/allocator/byte_stream.h"
 #include "common/container/byte_buffer.h"
 #include "common/global.h"
@@ -158,11 +160,13 @@ class RowAppender {
         // TODO(Colin): Refine this.
         TSDataType datatype = vec->get_vector_type();
         if (len == 4 && datatype == INT64) {
-            int32_t int32_val = *reinterpret_cast<const int32_t*>(value);
+            int32_t int32_val;
+            std::memcpy(&int32_val, value, sizeof(int32_val));
             int64_t int64_val = static_cast<int64_t>(int32_val);
             vec->append(reinterpret_cast<const char*>(&int64_val), 8);
         } else if (len == 4 && datatype == DOUBLE) {
-            float float_val = *reinterpret_cast<const float*>(value);
+            float float_val;
+            std::memcpy(&float_val, value, sizeof(float_val));
             double double_val = static_cast<double>(float_val);
             vec->append(reinterpret_cast<const char*>(&double_val), 8);
         } else {
diff --git a/cpp/src/common/tsblock/tuple_desc.h 
b/cpp/src/common/tsblock/tuple_desc.h
index 981d42385..39dce6273 100644
--- a/cpp/src/common/tsblock/tuple_desc.h
+++ b/cpp/src/common/tsblock/tuple_desc.h
@@ -62,7 +62,7 @@ class TupleDesc {
         time_column_index_ = -1;
     }
 
-    FORCE_INLINE void push_back(ColumnSchema schema) {
+    FORCE_INLINE void push_back(const ColumnSchema& schema) {
         if (schema.column_category_ == ColumnCategory::TIME) {
             ASSERT(time_column_index_ == -1);
             time_column_index_ = static_cast<int>(column_list_.size());
@@ -97,7 +97,7 @@ class TupleDesc {
         return time_column_index_;
     }
 
-    FORCE_INLINE std::string get_column_name(uint32_t index) {
+    FORCE_INLINE const std::string& get_column_name(uint32_t index) const {
         return column_list_[index].column_name_;
     }
 
diff --git a/cpp/src/compress/gzip_compressor.cc 
b/cpp/src/compress/gzip_compressor.cc
index b49830bf2..49e7ac566 100644
--- a/cpp/src/compress/gzip_compressor.cc
+++ b/cpp/src/compress/gzip_compressor.cc
@@ -23,7 +23,8 @@ using namespace common;
 
 namespace storage {
 
-GzipCompressor::GzipCompressor() : compressed_buf() { zstream_valid_ = false; }
+GzipCompressor::GzipCompressor()
+    : compress_stream_(), compressed_buf(), zstream_valid_(false) {}
 
 GzipCompressor::~GzipCompressor() { end_zstream(); }
 
@@ -140,9 +141,8 @@ int GzipCompressor::compress(char* uncompressed_buf,
     return ret;
 }
 
-GzipDeCompressor::GzipDeCompressor() : decompressed_buf() {
-    zstream_valid_ = false;
-}
+GzipDeCompressor::GzipDeCompressor()
+    : decompress_stream_(), decompressed_buf(), zstream_valid_(false) {}
 
 GzipDeCompressor::~GzipDeCompressor() { end_zstream(); }
 
diff --git a/cpp/src/file/write_file.h b/cpp/src/file/write_file.h
index 014fa1ae6..84358dcdf 100644
--- a/cpp/src/file/write_file.h
+++ b/cpp/src/file/write_file.h
@@ -46,7 +46,7 @@ class WriteFile {
     int truncate(int64_t size);
     /** Seek to end of file. Used after open to position for append. */
     int seek_to_end();
-    FORCE_INLINE std::string get_file_path() { return path_; }
+    FORCE_INLINE const std::string& get_file_path() const { return path_; }
     /** Current file offset. After seek_to_end(), equals file size (for
      * recovery). */
     int64_t get_position();
diff --git a/cpp/src/parser/path_visitor.cpp b/cpp/src/parser/path_visitor.cpp
index 6c7968410..96b6c9478 100644
--- a/cpp/src/parser/path_visitor.cpp
+++ b/cpp/src/parser/path_visitor.cpp
@@ -103,7 +103,8 @@ bool PathVisitor::ends_with(const std::string& str, const 
std::string& suffix) {
 
 bool PathVisitor::is_creatable(const std::string& str) {
     try {
-        std::stod(str);
+        const double parsed_value = std::stod(str);
+        (void)parsed_value;
         return true;
     } catch (const std::invalid_argument& e) {
         return false;
diff --git a/cpp/src/reader/expression.cc b/cpp/src/reader/expression.cc
index 06a615e15..08ffac98e 100644
--- a/cpp/src/reader/expression.cc
+++ b/cpp/src/reader/expression.cc
@@ -108,8 +108,9 @@ Expression* 
QueryExpression::merge_second_tree_to_first_tree(
 
 Expression* QueryExpression::push_global_time_filter_to_all_series(
     Expression* time_filter, std::vector<Path>& selected_series) {
-    if (selected_series.size() == 0) {
+    if (selected_series.empty()) {
         std::cout << "size of selectSeries could not be 0" << std::endl;
+        return nullptr;
     }
 
     Expression* expression = new Expression(SERIES_EXPR, selected_series.at(0),
@@ -129,6 +130,9 @@ Expression* QueryExpression::handle_one_global_time_filter(
     Expression* left, Expression* expression,
     std::vector<Path>& selected_series, ExpressionType type) {
     Expression* expr = optimize(expression, selected_series);
+    if (expr == nullptr) {
+        return nullptr;
+    }
 
     if (expr->type_ == GLOBALTIME_EXPR) {
         return combine_two_global_time_filter(left, expr, type);
@@ -140,6 +144,9 @@ Expression* QueryExpression::handle_one_global_time_filter(
     } else if (type == OR_EXPR) {
         Expression* after_transform =
             push_global_time_filter_to_all_series(left, selected_series);
+        if (after_transform == nullptr) {
+            return nullptr;
+        }
         return merge_second_tree_to_first_tree(after_transform, expr);
     }
     std::cout << "unknown relation in Expression:" << type << std::endl;
@@ -168,6 +175,9 @@ Expression* QueryExpression::optimize(Expression* 
expression,
                    right->type_ != GLOBALTIME_EXPR) {
             Expression* regular_left = optimize(left, series_paths);
             Expression* regular_right = optimize(right, series_paths);
+            if (regular_left == nullptr || regular_right == nullptr) {
+                return nullptr;
+            }
             Expression* mid_ret = nullptr;
             if (type == AND_EXPR) {
                 mid_ret = new Expression(AND_EXPR, regular_left, 
regular_right);
diff --git a/cpp/src/reader/filter/binary_filter.h 
b/cpp/src/reader/filter/binary_filter.h
index 4cacd52a1..586e94f6a 100644
--- a/cpp/src/reader/filter/binary_filter.h
+++ b/cpp/src/reader/filter/binary_filter.h
@@ -25,7 +25,7 @@
 namespace storage {
 class BinaryFilter : public Filter {
    public:
-    BinaryFilter() : Filter() {}
+    BinaryFilter() : Filter(), left_(nullptr), right_(nullptr) {}
     BinaryFilter(Filter* left, Filter* right)
         : Filter(), left_(left), right_(right) {}
     virtual ~BinaryFilter() {}
diff --git a/cpp/src/reader/table_result_set.cc 
b/cpp/src/reader/table_result_set.cc
index 6de093d24..1a8d2a687 100644
--- a/cpp/src/reader/table_result_set.cc
+++ b/cpp/src/reader/table_result_set.cc
@@ -20,6 +20,8 @@
 
 #include <utils/storage_utils.h>
 
+#include <cstring>
+
 namespace storage {
 void TableResultSet::init() {
     row_record_ = new RowRecord(column_names_.size() + 1);
@@ -127,14 +129,16 @@ bool TableResultSet::is_null(uint32_t column_index) {
 // the column's physical storage width (DATE is int32, not int64).  On a
 // mismatch it fires in debug instead of silently splicing the adjacent cell's
 // bytes into the result.
-#define TSFILE_FAST_PRIMITIVE_READ(TYPE, DFLT)                         \
-    if (!row_ready_) return DFLT;                                      \
-    common::Vector* vec = row_iterator_->get_vector(column_index - 1); \
-    ASSERT(common::TypeMatch<TYPE>(vec->get_vector_type()));           \
-    if (vec->has_null() && vec->is_null(row_iterator_->get_row_id()))  \
-        return DFLT;                                                   \
-    return *reinterpret_cast<TYPE*>(vec->get_value_data().get_data() + \
-                                    vec->get_offset())
+#define TSFILE_FAST_PRIMITIVE_READ(TYPE, DFLT)                                \
+    if (!row_ready_) return DFLT;                                             \
+    common::Vector* vec = row_iterator_->get_vector(column_index - 1);        \
+    ASSERT(common::TypeMatch<TYPE>(vec->get_vector_type()));                  \
+    if (vec->has_null() && vec->is_null(row_iterator_->get_row_id()))         \
+        return DFLT;                                                          \
+    TYPE value;                                                               \
+    std::memcpy(&value, vec->get_value_data().get_data() + vec->get_offset(), \
+                sizeof(value));                                               \
+    return value
 
 bool TableResultSet::get_bool_at(uint32_t column_index) {
     TSFILE_FAST_PRIMITIVE_READ(bool, false);
diff --git a/cpp/test/common/row_record_test.cc 
b/cpp/test/common/row_record_test.cc
index 6b8b54a15..a88d7e2b9 100644
--- a/cpp/test/common/row_record_test.cc
+++ b/cpp/test/common/row_record_test.cc
@@ -104,6 +104,7 @@ TEST(FieldTest, MakeNullLiteral) {
 
 TEST(RowRecordTest, ConstructorWithColNum) {
     RowRecord row_record(5);
+    EXPECT_EQ(row_record.get_timestamp(), 0);
     EXPECT_EQ(row_record.get_fields()->size(), 5);
     for (Field* field : *row_record.get_fields()) {
         EXPECT_EQ(field->type_, common::NULL_TYPE);
diff --git a/cpp/src/reader/filter/binary_filter.h 
b/cpp/test/reader/expression_test.cc
similarity index 53%
copy from cpp/src/reader/filter/binary_filter.h
copy to cpp/test/reader/expression_test.cc
index 4cacd52a1..f4f9fbacc 100644
--- a/cpp/src/reader/filter/binary_filter.h
+++ b/cpp/test/reader/expression_test.cc
@@ -4,7 +4,7 @@
  * 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
+ * "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
@@ -16,30 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-#ifndef READER_FILTER_BASIC_BINARY_FILTER_H
-#define READER_FILTER_BASIC_BINARY_FILTER_H
 
-#include "filter.h"
-#include "filter_type.h"
+#include "reader/expression.h"
+
+#include <gtest/gtest.h>
+
+#include <vector>
 
 namespace storage {
-class BinaryFilter : public Filter {
-   public:
-    BinaryFilter() : Filter() {}
-    BinaryFilter(Filter* left, Filter* right)
-        : Filter(), left_(left), right_(right) {}
-    virtual ~BinaryFilter() {}
-
-    void set_left(Filter* left) { left_ = left; }
-    void set_right(Filter* right) { right_ = right; }
-    Filter get_left() { return *left_; }
-    Filter get_right() { return *right_; }
-
-   protected:
-    Filter* left_;
-    Filter* right_;
-};
 
-}  // namespace storage
+TEST(QueryExpressionTest, EmptySelectedSeriesReturnsNull) {
+    QueryExpression query_expression;
+    std::vector<Path> selected_series;
+    Expression* expression = new Expression(
+        OR_EXPR, new Expression(GLOBALTIME_EXPR, 
static_cast<Filter*>(nullptr)),
+        new Expression(SERIES_EXPR, Path(), static_cast<Filter*>(nullptr)));
 
-#endif  // READER_FILTER_BASIC_BINARY_FILTER_H
+    EXPECT_EQ(query_expression.optimize(expression, selected_series), nullptr);
+
+    delete expression;
+}
+
+}  // namespace storage

Reply via email to