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

taiyangli pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-gluten.git


The following commit(s) were added to refs/heads/main by this push:
     new 3add1fb66d [GLUTEN-7659][CH] Implement splittable bzip2 decompression 
(#7638)
3add1fb66d is described below

commit 3add1fb66d4f786a711cae26dda32970683f20c3
Author: 李扬 <[email protected]>
AuthorDate: Fri Oct 25 16:22:24 2024 +0800

    [GLUTEN-7659][CH] Implement splittable bzip2 decompression (#7638)
    
    * clean some codes
    
    * finish refactor
    
    * special process for first block
    
    * finish last block special process
    
    * minor refactors
    
    * finish dev
    
    * enable bzip2 file split
    
    * fix bugs
    
    * fix failed uts
    
    * fix failed uts in hdfs suite
---
 .../backendsapi/clickhouse/CHValidatorApi.scala    |   2 +-
 cpp-ch/local-engine/CMakeLists.txt                 |   4 +-
 cpp-ch/local-engine/Common/GlutenConfig.cpp        |  13 +-
 cpp-ch/local-engine/Common/GlutenConfig.h          |   9 -
 .../SparkFunctionDecimalBinaryArithmetic.cpp       |   6 +-
 .../local-engine/IO/SplittableBzip2ReadBuffer.cpp  | 957 +++++++++++++++++++++
 cpp-ch/local-engine/IO/SplittableBzip2ReadBuffer.h | 306 +++++++
 cpp-ch/local-engine/Parser/CHColumnToSparkRow.cpp  |   4 +-
 cpp-ch/local-engine/Parser/CHColumnToSparkRow.h    |   4 +-
 .../SubstraitSource/ExcelTextFormatFile.cpp        |   2 +-
 .../Storages/SubstraitSource/FormatFile.h          |   1 -
 .../Storages/SubstraitSource/JSONFormatFile.cpp    |   2 +-
 .../Storages/SubstraitSource/ReadBufferBuilder.cpp | 244 ++++--
 .../Storages/SubstraitSource/ReadBufferBuilder.h   |  13 +-
 .../Storages/SubstraitSource/TextFormatFile.cpp    |   2 +-
 15 files changed, 1462 insertions(+), 107 deletions(-)

diff --git 
a/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHValidatorApi.scala
 
b/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHValidatorApi.scala
index 6081b87e1d..5fe7406946 100644
--- 
a/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHValidatorApi.scala
+++ 
b/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHValidatorApi.scala
@@ -67,7 +67,7 @@ class CHValidatorApi extends ValidatorApi with 
AdaptiveSparkPlanHelper with Logg
 
   /** Validate whether the compression method support splittable at clickhouse 
backend. */
   override def doCompressionSplittableValidate(compressionMethod: String): 
Boolean = {
-    false
+    compressionMethod == "BZip2Codec"
   }
 
   override def doColumnarShuffleExchangeExecValidate(
diff --git a/cpp-ch/local-engine/CMakeLists.txt 
b/cpp-ch/local-engine/CMakeLists.txt
index 03e17ec311..4392b55008 100644
--- a/cpp-ch/local-engine/CMakeLists.txt
+++ b/cpp-ch/local-engine/CMakeLists.txt
@@ -63,6 +63,7 @@ add_headers_and_sources(jni jni)
 add_headers_and_sources(aggregate_functions AggregateFunctions)
 add_headers_and_sources(disks Disks)
 add_headers_and_sources(disks Disks/ObjectStorages)
+add_headers_and_sources(io IO)
 
 include_directories(
   ${JNI_INCLUDE_DIRS}
@@ -101,7 +102,8 @@ add_library(
   ${operator_sources}
   ${aggregate_functions_sources}
   ${jni_sources}
-  ${disks_sources})
+  ${disks_sources}
+  ${io_sources})
 
 target_link_libraries(
   gluten_clickhouse_backend_libs
diff --git a/cpp-ch/local-engine/Common/GlutenConfig.cpp 
b/cpp-ch/local-engine/Common/GlutenConfig.cpp
index 212a87dcfe..44d77cf372 100644
--- a/cpp-ch/local-engine/Common/GlutenConfig.cpp
+++ b/cpp-ch/local-engine/Common/GlutenConfig.cpp
@@ -76,6 +76,7 @@ GraceMergingAggregateConfig 
GraceMergingAggregateConfig::loadFromContext(const D
         = 
context->getConfigRef().getDouble(MAX_ALLOWED_MEMORY_USAGE_RATIO_FOR_AGGREGATE_MERGING,
 0.9);
     return config;
 }
+
 StreamingAggregateConfig StreamingAggregateConfig::loadFromContext(const 
DB::ContextPtr & context)
 {
     StreamingAggregateConfig config;
@@ -88,6 +89,7 @@ StreamingAggregateConfig 
StreamingAggregateConfig::loadFromContext(const DB::Con
     config.enable_streaming_aggregating = 
context->getConfigRef().getBool(ENABLE_STREAMING_AGGREGATING, true);
     return config;
 }
+
 JoinConfig JoinConfig::loadFromContext(const DB::ContextPtr & context)
 {
     JoinConfig config;
@@ -96,6 +98,7 @@ JoinConfig JoinConfig::loadFromContext(const DB::ContextPtr & 
context)
         = 
context->getConfigRef().getUInt64(MULTI_JOIN_ON_CLAUSES_BUILD_SIDE_ROWS_LIMIT, 
10000000);
     return config;
 }
+
 ExecutorConfig ExecutorConfig::loadFromContext(const DB::ContextPtr & context)
 {
     ExecutorConfig config;
@@ -103,16 +106,6 @@ ExecutorConfig ExecutorConfig::loadFromContext(const 
DB::ContextPtr & context)
     config.use_local_format = 
context->getConfigRef().getBool(USE_LOCAL_FORMAT, false);
     return config;
 }
-HdfsConfig HdfsConfig::loadFromContext(const Poco::Util::AbstractConfiguration 
& config, const DB::ReadSettings & read_settings)
-{
-    HdfsConfig hdfs;
-    if (read_settings.enable_filesystem_cache)
-        hdfs.hdfs_async = false;
-    else
-        hdfs.hdfs_async = config.getBool(HDFS_ASYNC, true);
-
-    return hdfs;
-}
 
 S3Config S3Config::loadFromContext(const DB::ContextPtr & context)
 {
diff --git a/cpp-ch/local-engine/Common/GlutenConfig.h 
b/cpp-ch/local-engine/Common/GlutenConfig.h
index 3ff8f205a1..82402eaafa 100644
--- a/cpp-ch/local-engine/Common/GlutenConfig.h
+++ b/cpp-ch/local-engine/Common/GlutenConfig.h
@@ -108,15 +108,6 @@ struct ExecutorConfig
     static ExecutorConfig loadFromContext(const DB::ContextPtr & context);
 };
 
-struct HdfsConfig
-{
-    inline static const String HDFS_ASYNC = "hdfs.enable_async_io";
-
-    bool hdfs_async;
-
-    static HdfsConfig loadFromContext(const Poco::Util::AbstractConfiguration 
& config, const DB::ReadSettings & read_settings);
-};
-
 struct S3Config
 {
     inline static const String S3_LOCAL_CACHE_ENABLE = 
"s3.local_cache.enabled";
diff --git 
a/cpp-ch/local-engine/Functions/SparkFunctionDecimalBinaryArithmetic.cpp 
b/cpp-ch/local-engine/Functions/SparkFunctionDecimalBinaryArithmetic.cpp
index a2d5dec739..830fc0e652 100644
--- a/cpp-ch/local-engine/Functions/SparkFunctionDecimalBinaryArithmetic.cpp
+++ b/cpp-ch/local-engine/Functions/SparkFunctionDecimalBinaryArithmetic.cpp
@@ -279,7 +279,7 @@ private:
         const ScaleDataType & scale_b,
         ColumnUInt8::Container & vec_null_map_to,
         const ResultDataType & resultDataType,
-        const size_t & max_scale)
+        size_t max_scale)
     {
         using NativeResultType = NativeType<typename 
ResultDataType::FieldType>;
 
@@ -357,7 +357,7 @@ private:
         const ScaleDataType & scale_right,
         NativeResultType & res,
         const ResultDataType & resultDataType,
-        const size_t & max_scale)
+        size_t max_scale)
     {
         if constexpr (CalculateWith256)
             return calculateImpl<Int256>(l, r, scale_left, scale_right, res, 
resultDataType, max_scale);
@@ -375,7 +375,7 @@ private:
         const ScaleDataType & scale_right,
         NativeResultType & res,
         const ResultDataType & resultDataType,
-        const size_t & max_scale)
+        size_t max_scale)
     {
         CalcType scaled_l = applyScaled(static_cast<CalcType>(l), 
static_cast<CalcType>(scale_left));
         CalcType scaled_r = applyScaled(static_cast<CalcType>(r), 
static_cast<CalcType>(scale_right));
diff --git a/cpp-ch/local-engine/IO/SplittableBzip2ReadBuffer.cpp 
b/cpp-ch/local-engine/IO/SplittableBzip2ReadBuffer.cpp
new file mode 100644
index 0000000000..701918e38d
--- /dev/null
+++ b/cpp-ch/local-engine/IO/SplittableBzip2ReadBuffer.cpp
@@ -0,0 +1,957 @@
+/*
+ * 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.
+ */
+
+#include "SplittableBzip2ReadBuffer.h"
+
+#if USE_BZIP2
+#include <IO/SeekableReadBuffer.h>
+#include <IO/VarInt.h>
+#include <base/find_symbols.h>
+
+
+namespace DB
+{
+
+namespace ErrorCodes
+{
+extern const int LOGICAL_ERROR;
+extern const int POSITION_OUT_OF_BOUND;
+}
+
+std::vector<Int32> & SplittableBzip2ReadBuffer::Data::initTT(Int32 length)
+{
+    if (tt.size() < static_cast<size_t>(length))
+        tt.resize(length);
+    return tt;
+}
+
+template <typename T>
+std::string SplittableBzip2ReadBuffer::Data::arrayToString(const 
std::vector<T> & arr)
+{
+    std::string result = "[";
+    for (size_t i = 0; i < arr.size(); i++)
+    {
+        if (i)
+            result += ", ";
+
+        result += std::to_string(static_cast<Int32>(arr[i]));
+    }
+    result += "]";
+    return result;
+}
+
+template <typename T>
+std::string SplittableBzip2ReadBuffer::Data::array2DToString(T 
arr[BZip2Constants::N_GROUPS][BZip2Constants::MAX_ALPHA_SIZE])
+{
+    std::string result = "[";
+    for (int i = 0; i < BZip2Constants::N_GROUPS; i++)
+    {
+        if (i)
+            result += ", ";
+
+        result += arrayToString(arr[i], BZip2Constants::MAX_ALPHA_SIZE);
+    }
+    result += "]";
+    return result;
+}
+
+template <typename T>
+std::string SplittableBzip2ReadBuffer::Data::arrayToString(const T * arr, 
size_t size)
+{
+    std::string result = "[";
+    for (size_t i = 0; i < size; i++)
+    {
+        if (i)
+            result += ", ";
+
+        if constexpr (std::is_same_v<T, bool>)
+            result += arr[i] ? "true" : "false";
+        else
+            result += std::to_string(static_cast<Int32>(arr[i]));
+    }
+    result += "]";
+    return result;
+}
+
+std::string SplittableBzip2ReadBuffer::Data::toString()
+{
+    std::string result = "Data{";
+    result += "\ninUse=" + arrayToString(inUse, 256);
+    result += "\nseqToUnseq=" + arrayToString(seqToUnseq, 256);
+    result += "\nselector=" + arrayToString(selector, 
BZip2Constants::MAX_SELECTORS);
+    result += "\nselectorMtf=" + arrayToString(selectorMtf, 
BZip2Constants::MAX_SELECTORS);
+    result += "\nunzftab=" + arrayToString(unzftab, 256);
+    result += "\nlimit=" + array2DToString(limit);
+    result += "\nbase=" + array2DToString(base);
+    result += "\nperm=" + array2DToString(perm);
+    result += "\nminLens=" + arrayToString(minLens, BZip2Constants::N_GROUPS);
+    result += "\ncftab=" + arrayToString(cftab, 257);
+    result += "\ngetAndMoveToFrontDecode_yy=" + 
arrayToString(getAndMoveToFrontDecode_yy, 256);
+    result += "\ntemp_charArray2d=" + array2DToString(temp_charArray2d);
+    result += "\nrecvDecodingTables_pos=" + 
arrayToString(recvDecodingTables_pos, BZip2Constants::N_GROUPS);
+    result += "\ntt=" + arrayToString(tt);
+    result += "\nll8=" + arrayToString(ll8);
+    result += "}";
+    return result;
+}
+
+
+void SplittableBzip2ReadBuffer::hbCreateDecodeTables(
+    int * __restrict limit,
+    int * __restrict base,
+    int * __restrict perm,
+    const UInt16 * __restrict length,
+    int minLen,
+    int maxLen,
+    int alphaSize)
+{
+    for (int i = minLen, pp = 0; i <= maxLen; i++)
+    {
+        for (int j = 0; j < alphaSize; j++)
+            if (length[j] == i)
+                perm[pp++] = j;
+    }
+    for (int i = BZip2Constants::MAX_CODE_LEN - 1; i > 0; --i)
+    {
+        base[i] = 0;
+        limit[i] = 0;
+    }
+
+    for (int i = 0; i < alphaSize; i++)
+        base[length[i] + 1]++;
+
+    for (int i = 1, b = base[0]; i < BZip2Constants::MAX_CODE_LEN; i++)
+    {
+        b += base[i];
+        base[i] = b;
+    }
+
+    for (int i = minLen, vec = 0, b = base[i]; i <= maxLen; i++)
+    {
+        int nb = base[i + 1];
+        vec += nb - b;
+        b = nb;
+        limit[i] = vec - 1;
+        vec <<= 1;
+    }
+
+    for (int i = minLen + 1; i <= maxLen; i++)
+        base[i] = ((limit[i - 1] + 1) << 1) - base[i];
+}
+
+SplittableBzip2ReadBuffer::SplittableBzip2ReadBuffer(
+    std::unique_ptr<ReadBuffer> in_,
+    bool first_block_need_special_process_,
+    bool last_block_need_special_process_,
+    size_t buf_size,
+    char * existing_memory,
+    size_t alignment)
+    : CompressedReadBufferWrapper(std::move(in_), buf_size, existing_memory, 
alignment)
+    , first_block_need_special_process(first_block_need_special_process_)
+    , last_block_need_special_process(last_block_need_special_process_)
+    , is_first_block(true)
+    , blockSize100k(9)
+    , currentState(STATE::NO_PROCESS_STATE)
+    , skipResult(false)
+    , currentChar(0)
+    , storedBlockCRC(0)
+    , blockRandomised(false)
+    , data(nullptr)
+    , computedBlockCRC(0)
+    , storedCombinedCRC(0)
+    , computedCombinedCRC(0)
+    , origPtr(0)
+    , nInUse(0)
+    , bsBuff(0)
+    , bsLive(0)
+    , last(0)
+{
+    auto * seekable = dynamic_cast<SeekableReadBuffer*>(in.get());
+    skipResult = skipToNextMarker(BLOCK_DELIMITER, DELIMITER_BIT_LENGTH);
+    if (seekable && skipResult)
+    {
+        /// Update adjusted_start
+        adjusted_start = seekable->getPosition();
+    }
+    changeStateToProcessABlock();
+}
+
+Int32 SplittableBzip2ReadBuffer::read(char * dest, size_t dest_size, size_t 
offs, size_t len)
+{
+    if (offs + len > dest_size)
+        throw Exception(ErrorCodes::POSITION_OUT_OF_BOUND, "offs({}) + len({}) 
> dest_size({}).", offs, len, dest_size);
+
+    const size_t hi = offs + len;
+    size_t destOffs = offs;
+    Int32 b = 0;
+    for (; (destOffs < hi && (b = read0()) >= 0); ++destOffs)
+    {
+        dest[destOffs] = static_cast<char>(b);
+    }
+
+    Int32 result = static_cast<Int32>(destOffs - offs);
+    if (result == 0)
+    {
+        result = b;
+        skipResult = 
skipToNextMarker(SplittableBzip2ReadBuffer::BLOCK_DELIMITER, 
DELIMITER_BIT_LENGTH);
+        changeStateToProcessABlock();
+    }
+    return result;
+}
+
+bool SplittableBzip2ReadBuffer::nextImpl()
+{
+    Position dest = internal_buffer.begin();
+    size_t dest_size = internal_buffer.size();
+    size_t offset = 0;
+    Int32 result;
+    do
+    {
+        result = read(dest, dest_size, offset, dest_size - offset);
+        if (result > 0)
+            offset += result;
+        else if (result == BZip2Constants::END_OF_BLOCK && is_first_block && 
first_block_need_special_process)
+        {
+            /// Special processing for the first block
+            /// Notice that row delim could be \n (Unix) or \r\n (DOS/Windows) 
or \n\r (Mac OS Classic)
+            is_first_block = false;
+            Position end = dest + offset;
+            auto * pos = find_last_symbols_or_null<'\n'>(dest, end);
+            if (pos)
+            {
+                if (pos == end - 1 || (pos == end - 2 && *(pos + 1) == '\r'))
+                {
+                    /// The last row ends with \n or \r\n or \n\r, discard all 
lines in internal buffer
+                    offset = 0;
+                }
+                else
+                {
+                    /// The last row does not end with \n or \r\n or \n\r, 
rewrite the last row to internal buffer
+                    Position last_line = pos + 1;
+                    size_t last_line_size = end - pos - 1;
+                    if (*(pos + 1) == '\r')
+                        last_line_size--;
+
+                    memmove(dest, last_line, last_line_size);
+                    offset = last_line_size;
+                }
+            }
+        }
+        else if (result == BZip2Constants::END_OF_STREAM && 
last_block_need_special_process)
+        {
+            /// Special processing for the last block
+            Position end = dest + offset;
+            auto * pos = find_last_symbols_or_null<'\n'>(dest, end);
+
+            if (!pos)
+            {
+                /// Only one incomplete row in the last block, discard it
+                offset = 0;
+            }
+            else
+            {
+                /// Discard the last incomplete row(if there is) in last block.
+                offset = pos - dest + 1;
+                if (pos + 1 < end && *(pos + 1) == '\r')
+                    offset++;
+            }
+        }
+    } while (result != BZip2Constants::END_OF_STREAM && offset < dest_size);
+
+    if (offset)
+    {
+        working_buffer.resize(offset);
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+Int32 SplittableBzip2ReadBuffer::read0()
+{
+    Int32 retChar = currentChar;
+
+    switch (currentState)
+    {
+        case STATE::END_OF_FILE:
+            return BZip2Constants::END_OF_STREAM;
+        case STATE::NO_PROCESS_STATE:
+            return BZip2Constants::END_OF_BLOCK;
+        case STATE::START_BLOCK_STATE:
+            throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong state {}", 
magic_enum::enum_name(currentState));
+        case STATE::RAND_PART_A_STATE:
+            throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong state {}", 
magic_enum::enum_name(currentState));
+        case STATE::RAND_PART_B_STATE:
+            setupRandPartB();
+            break;
+        case STATE::RAND_PART_C_STATE:
+            setupRandPartC();
+            break;
+        case STATE::NO_RAND_PART_A_STATE:
+            throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong state {}", 
magic_enum::enum_name(currentState));
+        case STATE::NO_RAND_PART_B_STATE:
+            setupNoRandPartB();
+            break;
+        case STATE::NO_RAND_PART_C_STATE:
+            setupNoRandPartC();
+            break;
+    }
+    return retChar;
+}
+
+Int32 SplittableBzip2ReadBuffer::readAByte(ReadBuffer & in_)
+{
+    char c;
+    if (in_.read(c))
+        return static_cast<Int32>(c) & 0xff;
+    else
+        return -1;
+}
+
+bool SplittableBzip2ReadBuffer::skipToNextMarker(Int64 marker, Int32 
markerBitLength, ReadBuffer & in_, Int64 & bsBuff_, Int64 & bsLive_)
+{
+    try
+    {
+        if (markerBitLength > 63)
+            throw Exception(ErrorCodes::LOGICAL_ERROR, "skipToNextMarker can 
not find patterns greater than 63 bits");
+
+        Int64 bytes = bsR(markerBitLength, in_, bsBuff_, bsLive_);
+        if (bytes == -1)
+        {
+            return false;
+        }
+
+        while (true)
+        {
+            if (bytes == marker)
+            {
+                return true;
+            }
+            else
+            {
+                bytes = bytes << 1;
+                bytes = bytes & ((1L << markerBitLength) - 1);
+                Int32 oneBit = static_cast<Int32>(bsR(1, in_, bsBuff_, 
bsLive_));
+                if (oneBit != -1)
+                {
+                    bytes = bytes | oneBit;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+        }
+    }
+    catch (const Exception &)
+    {
+        return false;
+    }
+}
+
+bool SplittableBzip2ReadBuffer::skipToNextMarker(Int64 marker, Int32 
markerBitLength)
+{
+    return skipToNextMarker(marker, markerBitLength, *in, bsBuff, bsLive);
+}
+
+void SplittableBzip2ReadBuffer::reportCRCError()
+{
+    throw Exception(ErrorCodes::LOGICAL_ERROR, "CRC error");
+}
+
+void SplittableBzip2ReadBuffer::makeMaps()
+{
+    Int32 nInUseShadow = 0;
+    for (Int32 i = 0; i < 256; i++)
+        if (data->inUse[i])
+            data->seqToUnseq[nInUseShadow++] = i;
+    nInUse = nInUseShadow;
+}
+
+void SplittableBzip2ReadBuffer::changeStateToProcessABlock()
+{
+    if (skipResult == true)
+    {
+        initBlock();
+        setupBlock();
+    }
+    else
+    {
+        currentState = STATE::END_OF_FILE;
+    }
+}
+
+void SplittableBzip2ReadBuffer::initBlock()
+{
+    storedBlockCRC = bsGetInt();
+    blockRandomised = (bsR(1) == 1);
+
+    if (!data)
+        data = std::make_unique<Data>(blockSize100k);
+
+    getAndMoveToFrontDecode();
+    crc.initialiseCRC();
+    currentState = STATE::START_BLOCK_STATE;
+}
+
+void SplittableBzip2ReadBuffer::endBlock()
+{
+    computedBlockCRC = crc.getFinalCRC();
+    if (storedBlockCRC != computedBlockCRC)
+    {
+        computedCombinedCRC = (storedCombinedCRC << 1) | 
(static_cast<UInt32>(storedCombinedCRC) >> 31);
+        computedCombinedCRC ^= storedBlockCRC;
+        reportCRCError();
+    }
+    computedCombinedCRC = (computedCombinedCRC << 1) | 
(static_cast<UInt32>(computedCombinedCRC) >> 31);
+    computedCombinedCRC ^= computedBlockCRC;
+}
+
+void SplittableBzip2ReadBuffer::complete()
+{
+    storedCombinedCRC = bsGetInt();
+    currentState = STATE::END_OF_FILE;
+    data = nullptr;
+    if (storedCombinedCRC != computedCombinedCRC)
+        reportCRCError();
+}
+
+Int64 SplittableBzip2ReadBuffer::bsR(Int64 n, ReadBuffer & in_, Int64 & 
bsBuff_, Int64 & bsLive_)
+{
+    Int64 bsLiveShadow = bsLive_;
+    Int64 bsBuffShadow = bsBuff_;
+    if (bsLiveShadow < n)
+    {
+        do
+        {
+            Int32 thech = readAByte(in_);
+            if (thech < 0)
+                DB::throwReadAfterEOF();
+
+            bsBuffShadow = (bsBuffShadow << 8) | thech;
+            bsLiveShadow += 8;
+        } while (bsLiveShadow < n);
+
+        bsBuff_ = bsBuffShadow;
+    }
+
+    bsLive_ = bsLiveShadow - n;
+    return (bsBuffShadow >> (bsLiveShadow - n)) & ((1L << n) - 1);
+}
+
+Int64 SplittableBzip2ReadBuffer::bsR(Int64 n)
+{
+    return bsR(n, *in, bsBuff, bsLive);
+}
+
+bool SplittableBzip2ReadBuffer::bsGetBit()
+{
+    Int64 bsLiveShadow = bsLive;
+    Int64 bsBuffShadow = bsBuff;
+    if (bsLiveShadow < 1)
+    {
+        Int32 thech = readAByte(*in);
+        if (thech < 0)
+            DB::throwReadAfterEOF();
+
+        bsBuffShadow = (bsBuffShadow << 8) | thech;
+        bsLiveShadow += 8;
+        bsBuff = bsBuffShadow;
+    }
+    bsLive = bsLiveShadow - 1;
+    return ((bsBuffShadow >> (bsLiveShadow - 1)) & 1) != 0;
+}
+
+void SplittableBzip2ReadBuffer::recvDecodingTables()
+{
+    Data * dataShadow = data.get();
+    bool * inUse = dataShadow->inUse;
+    char * pos = dataShadow->recvDecodingTables_pos;
+    char * selector = dataShadow->selector;
+    char * selectorMtf = dataShadow->selectorMtf;
+
+    Int32 inUse16 = 0;
+    for (Int32 i = 0; i < 16; ++i)
+        if (bsGetBit())
+            inUse16 |= 1 << i;
+
+    for (Int32 i = 255; i >= 0; --i)
+        inUse[i] = false;
+
+    for (Int32 i = 0; i < 16; ++i)
+    {
+        if ((inUse16 & (1 << i)) != 0)
+        {
+            Int32 i16 = i << 4;
+            for (Int32 j = 0; j < 16; j++)
+                if (bsGetBit())
+                    inUse[i16 + j] = true;
+        }
+    }
+    makeMaps();
+
+    Int32 alphaSize = nInUse + 2;
+    Int32 nGroups = static_cast<Int32>(bsR(3));
+    Int32 nSelectors = static_cast<Int32>(bsR(15));
+    for (Int32 i = 0; i < nSelectors; ++i)
+    {
+        Int32 j = 0;
+        while (bsGetBit())
+            j++;
+        selectorMtf[i] = j;
+    }
+
+    for (Int32 v = nGroups - 1; v >= 0; --v)
+        pos[v] = v;
+
+    for (Int32 i = 0; i < nSelectors; ++i)
+    {
+        Int32 v = selectorMtf[i] & 0xff;
+        char tmp = pos[v];
+        while (v > 0)
+        {
+            pos[v] = pos[v - 1];
+            v--;
+        }
+        pos[0] = tmp;
+        selector[i] = tmp;
+    }
+
+    auto * len = dataShadow->temp_charArray2d;
+    for (Int32 t = 0; t < nGroups; t++)
+    {
+        Int32 curr = static_cast<Int32>(bsR(5));
+        auto * len_t = len[t];
+        for (Int32 i = 0; i < alphaSize; i++)
+        {
+            while (bsGetBit())
+                curr += bsGetBit() ? -1 : 1;
+            len_t[i] = curr;
+        }
+    }
+
+    createHuffmanDecodingTables(alphaSize, nGroups);
+}
+
+void SplittableBzip2ReadBuffer::createHuffmanDecodingTables(Int32 alphaSize, 
Int32 nGroups)
+{
+    Data * dataShadow = data.get();
+    auto * len = dataShadow->temp_charArray2d;
+    auto * minLens = dataShadow->minLens;
+    auto * limit = dataShadow->limit;
+    auto * base = dataShadow->base;
+    auto * perm = dataShadow->perm;
+    for (Int32 t = 0; t < nGroups; t++)
+    {
+        Int32 minLen = 32;
+        Int32 maxLen = 0;
+        auto * len_t = len[t];
+        for (Int32 i = alphaSize - 1; i >= 0; --i)
+        {
+            Int32 lent = len_t[i];
+            if (lent > maxLen)
+                maxLen = lent;
+            if (lent < minLen)
+                minLen = lent;
+        }
+        hbCreateDecodeTables(limit[t], base[t], perm[t], len[t], minLen, 
maxLen, alphaSize);
+        minLens[t] = minLen;
+    }
+}
+
+void SplittableBzip2ReadBuffer::getAndMoveToFrontDecode()
+{
+    origPtr = static_cast<Int32>(bsR(24));
+    recvDecodingTables();
+
+    ReadBuffer * inShadow = in.get();
+    Data * dataShadow = data.get();
+    auto & ll8 = dataShadow->ll8;
+    Int32 * unzftab = dataShadow->unzftab;
+    char * selector = dataShadow->selector;
+    auto * seqToUnseq = dataShadow->seqToUnseq;
+    auto * yy = dataShadow->getAndMoveToFrontDecode_yy;
+    Int32 * minLens = dataShadow->minLens;
+    auto * limit = dataShadow->limit;
+    auto * base = dataShadow->base;
+    auto * perm = dataShadow->perm;
+    Int32 limitLast = blockSize100k * 100000;
+
+    for (Int32 i = 256; --i >= 0;)
+    {
+        yy[i] = i;
+        unzftab[i] = 0;
+    }
+
+    Int32 groupNo = 0;
+    Int32 groupPos = BZip2Constants::G_SIZE - 1;
+    Int32 eob = nInUse + 1;
+    Int32 nextSym = getAndMoveToFrontDecode0(0);
+    Int32 bsBuffShadow = static_cast<Int32>(bsBuff);
+    Int32 bsLiveShadow = static_cast<Int32>(bsLive);
+    Int32 lastShadow = -1;
+    Int32 zt = selector[groupNo] & 0xff;
+    Int32 * base_zt = base[zt];
+    Int32 * limit_zt = limit[zt];
+    Int32 * perm_zt = perm[zt];
+    Int32 minLens_zt = minLens[zt];
+
+    while (nextSym != eob)
+    {
+        if ((nextSym == BZip2Constants::RUNA) || (nextSym == 
BZip2Constants::RUNB))
+        {
+            Int32 s = -1;
+            for (Int32 n = 1; true; n <<= 1)
+            {
+                if (nextSym == BZip2Constants::RUNA)
+                    s += n;
+                else if (nextSym == BZip2Constants::RUNB)
+                    s += n << 1;
+                else
+                    break;
+
+                if (groupPos == 0)
+                {
+                    groupPos = BZip2Constants::G_SIZE - 1;
+                    zt = selector[++groupNo] & 0xff;
+                    base_zt = base[zt];
+                    limit_zt = limit[zt];
+                    perm_zt = perm[zt];
+                    minLens_zt = minLens[zt];
+                }
+                else
+                {
+                    groupPos--;
+                }
+
+                Int32 zn = minLens_zt;
+                while (bsLiveShadow < zn)
+                {
+                    Int32 thech = readAByte(*inShadow);
+                    if (thech < 0)
+                        DB::throwReadAfterEOF();
+
+                    bsBuffShadow = (bsBuffShadow << 8) | thech;
+                    bsLiveShadow += 8;
+                }
+
+                Int64 zvec = (bsBuffShadow >> (bsLiveShadow - zn)) & ((1 << 
zn) - 1);
+                bsLiveShadow -= zn;
+                while (zvec > limit_zt[zn])
+                {
+                    zn++;
+                    while (bsLiveShadow < 1)
+                    {
+                        Int32 thech = readAByte(*inShadow);
+                        if (thech < 0)
+                            DB::throwReadAfterEOF();
+
+                        bsBuffShadow = (bsBuffShadow << 8) | thech;
+                        bsLiveShadow += 8;
+                    }
+                    bsLiveShadow--;
+                    zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
+                }
+                nextSym = perm_zt[static_cast<Int32>(zvec - base_zt[zn])];
+            }
+
+            char ch = seqToUnseq[yy[0]];
+            unzftab[ch & 0xff] += s + 1;
+            while (s-- >= 0)
+                ll8[++lastShadow] = ch;
+            if (lastShadow >= limitLast)
+                throw Exception(ErrorCodes::LOGICAL_ERROR, "Block overrun");
+        }
+        else
+        {
+            if (++lastShadow >= limitLast)
+                throw Exception(ErrorCodes::LOGICAL_ERROR, "Block overrun");
+            auto tmp = yy[nextSym - 1];
+            unzftab[seqToUnseq[tmp] & 0xff]++;
+            ll8[lastShadow] = seqToUnseq[tmp];
+
+            if (nextSym <= 16)
+                for (Int32 j = nextSym - 1; j > 0; --j)
+                    yy[j] = yy[j - 1];
+            else
+                memmove(&yy[1], &yy[0], (nextSym - 1) * sizeof(yy[0]));
+            yy[0] = tmp;
+
+            if (groupPos == 0)
+            {
+                groupPos = BZip2Constants::G_SIZE - 1;
+                zt = selector[++groupNo] & 0xff;
+                base_zt = base[zt];
+                limit_zt = limit[zt];
+                perm_zt = perm[zt];
+                minLens_zt = minLens[zt];
+            }
+            else
+            {
+                groupPos--;
+            }
+
+            Int32 zn = minLens_zt;
+            while (bsLiveShadow < zn)
+            {
+                Int32 thech = readAByte(*inShadow);
+                if (thech < 0)
+                    DB::throwReadAfterEOF();
+
+                bsBuffShadow = (bsBuffShadow << 8) | thech;
+                bsLiveShadow += 8;
+            }
+
+            Int32 zvec = (bsBuffShadow >> (bsLiveShadow - zn)) & ((1 << zn) - 
1);
+            bsLiveShadow -= zn;
+            while (zvec > limit_zt[zn])
+            {
+                zn++;
+                while (bsLiveShadow < 1)
+                {
+                    Int32 thech = readAByte(*inShadow);
+                    if (thech < 0)
+                        DB::throwReadAfterEOF();
+
+                    bsBuffShadow = (bsBuffShadow << 8) | thech;
+                    bsLiveShadow += 8;
+                }
+                bsLiveShadow--;
+                zvec = ((zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1));
+            }
+            nextSym = perm_zt[zvec - base_zt[zn]];
+        }
+    }
+
+    last = lastShadow;
+    bsLive = bsLiveShadow;
+    bsBuff = bsBuffShadow;
+}
+
+Int32 SplittableBzip2ReadBuffer::getAndMoveToFrontDecode0(Int32 groupNo)
+{
+    ReadBuffer * inShadow = in.get();
+    Data * dataShadow = data.get();
+    Int32 zt = dataShadow->selector[groupNo] & 0xff;
+    Int32 * limit_zt = dataShadow->limit[zt];
+    Int32 zn = dataShadow->minLens[zt];
+    Int32 zvec = static_cast<Int32>(bsR(zn));
+    Int32 bsLiveShadow = static_cast<Int32>(bsLive);
+    Int32 bsBuffShadow = static_cast<Int32>(bsBuff);
+    while (zvec > limit_zt[zn])
+    {
+        zn++;
+        while (bsLiveShadow < 1)
+        {
+            Int32 thech = readAByte(*inShadow);
+            if (thech < 0)
+                DB::throwReadAfterEOF();
+
+            bsBuffShadow = (bsBuffShadow << 8) | thech;
+            bsLiveShadow += 8;
+        }
+        bsLiveShadow--;
+        zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1);
+    }
+
+    bsLive = bsLiveShadow;
+    bsBuff = bsBuffShadow;
+    return dataShadow->perm[zt][zvec - dataShadow->base[zt][zn]];
+}
+
+void SplittableBzip2ReadBuffer::setupBlock()
+{
+    if (!data)
+        return;
+
+    Int32 * cftab = data->cftab;
+    std::vector<Int32> & tt = data->initTT(last + 1);
+    auto & ll8 = data->ll8;
+    cftab[0] = 0;
+    memcpy(&cftab[1], &data->unzftab[0], 256 * sizeof(cftab[0]));
+    for (Int32 i = 1, c = cftab[0]; i <= 256; i++)
+    {
+        c += cftab[i];
+        cftab[i] = c;
+    }
+    for (Int32 i = 0, lastShadow = last; i <= lastShadow; i++)
+        tt[cftab[ll8[i] & 0xff]++] = i;
+
+    if (origPtr < 0 || static_cast<size_t>(origPtr) >= tt.size())
+        throw Exception(ErrorCodes::LOGICAL_ERROR, "Stream corrupted");
+
+    su_tPos = tt[origPtr];
+    su_count = 0;
+    su_i2 = 0;
+    su_ch2 = 256;
+    if (blockRandomised)
+    {
+        su_rNToGo = 0;
+        su_rTPos = 0;
+        setupRandPartA();
+    }
+    else
+    {
+        setupNoRandPartA();
+    }
+}
+
+void SplittableBzip2ReadBuffer::setupRandPartA()
+{
+    if (su_i2 <= last)
+    {
+        su_chPrev = su_ch2;
+        Int32 su_ch2Shadow = data->ll8[su_tPos] & 0xff;
+        su_tPos = data->tt[su_tPos];
+        if (su_rNToGo == 0)
+        {
+            su_rNToGo = BZip2Constants::rNums[su_rTPos] - 1;
+            if (++su_rTPos == 512)
+                su_rTPos = 0;
+        }
+        else
+        {
+            su_rNToGo--;
+        }
+        su_ch2 = ((su_ch2Shadow ^= (su_rNToGo == 1)) ? 1 : 0);
+        su_i2++;
+        currentChar = su_ch2Shadow;
+        currentState = STATE::RAND_PART_B_STATE;
+        crc.updateCRC(su_ch2Shadow);
+    }
+    else
+    {
+        endBlock();
+        currentState = STATE::NO_PROCESS_STATE;
+    }
+}
+
+void SplittableBzip2ReadBuffer::setupNoRandPartA()
+{
+    if (su_i2 <= last)
+    {
+        su_chPrev = su_ch2;
+        Int32 su_ch2Shadow = data->ll8[su_tPos] & 0xff;
+        su_ch2 = su_ch2Shadow;
+        su_tPos = data->tt[su_tPos];
+        su_i2++;
+        currentChar = su_ch2Shadow;
+        currentState = STATE::NO_RAND_PART_B_STATE;
+        crc.updateCRC(su_ch2Shadow);
+    }
+    else
+    {
+        currentState = STATE::NO_RAND_PART_A_STATE;
+        endBlock();
+        currentState = STATE::NO_PROCESS_STATE;
+    }
+}
+
+void SplittableBzip2ReadBuffer::setupRandPartB()
+{
+    if (su_ch2 != su_chPrev)
+    {
+        currentState = STATE::RAND_PART_A_STATE;
+        su_count = 1;
+        setupRandPartA();
+    }
+    else if (++su_count >= 4)
+    {
+        su_z = static_cast<char>(data->ll8[su_tPos] & 0xff);
+        su_tPos = data->tt[su_tPos];
+        if (su_rNToGo == 0)
+        {
+            su_rNToGo = BZip2Constants::rNums[su_rTPos] - 1;
+            if (++su_rTPos == 512)
+                su_rTPos = 0;
+        }
+        else
+        {
+            su_rNToGo--;
+        }
+        su_j2 = 0;
+        currentState = STATE::RAND_PART_C_STATE;
+        if (su_rNToGo == 1)
+            su_z ^= 1;
+        setupRandPartC();
+    }
+    else
+    {
+        currentState = STATE::RAND_PART_A_STATE;
+        setupRandPartA();
+    }
+}
+
+void SplittableBzip2ReadBuffer::setupRandPartC()
+{
+    if (su_j2 < su_z)
+    {
+        currentChar = su_ch2;
+        crc.updateCRC(su_ch2);
+        su_j2++;
+    }
+    else
+    {
+        currentState = STATE::RAND_PART_A_STATE;
+        su_i2++;
+        su_count = 0;
+        setupRandPartA();
+    }
+}
+
+void SplittableBzip2ReadBuffer::setupNoRandPartB()
+{
+    if (su_ch2 != su_chPrev)
+    {
+        su_count = 1;
+        setupNoRandPartA();
+    }
+    else if (++su_count >= 4)
+    {
+        su_z = static_cast<char>(data->ll8[su_tPos] & 0xff);
+        su_tPos = data->tt[su_tPos];
+        su_j2 = 0;
+        setupNoRandPartC();
+    }
+    else
+    {
+        setupNoRandPartA();
+    }
+}
+
+void SplittableBzip2ReadBuffer::setupNoRandPartC()
+{
+    if (su_j2 < su_z)
+    {
+        Int32 su_ch2Shadow = su_ch2;
+        currentChar = su_ch2Shadow;
+        crc.updateCRC(su_ch2Shadow);
+        su_j2++;
+        currentState = STATE::NO_RAND_PART_C_STATE;
+    }
+    else
+    {
+        su_i2++;
+        su_count = 0;
+        setupNoRandPartA();
+    }
+}
+
+}
+#endif
diff --git a/cpp-ch/local-engine/IO/SplittableBzip2ReadBuffer.h 
b/cpp-ch/local-engine/IO/SplittableBzip2ReadBuffer.h
new file mode 100644
index 0000000000..e5702c7a7a
--- /dev/null
+++ b/cpp-ch/local-engine/IO/SplittableBzip2ReadBuffer.h
@@ -0,0 +1,306 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#if USE_BZIP2
+#include <vector>
+#include <IO/CompressedReadBufferWrapper.h>
+
+namespace DB
+{
+
+class BZip2Constants
+{
+public:
+    static constexpr Int32 BASE_BLOCK_SIZE = 100000;
+    static constexpr Int32 MAX_ALPHA_SIZE = 258;
+    static constexpr Int32 MAX_CODE_LEN = 23;
+    static constexpr Int32 RUNA = 0;
+    static constexpr Int32 RUNB = 1;
+    static constexpr Int32 N_GROUPS = 6;
+    static constexpr Int32 G_SIZE = 50;
+    static constexpr Int32 N_ITERS = 4;
+    static constexpr Int32 MAX_SELECTORS = (2 + (900000 / G_SIZE));
+    static constexpr Int32 NUM_OVERSHOOT_BYTES = 20;
+    static constexpr Int32 END_OF_BLOCK = -2;
+    static constexpr Int32 END_OF_STREAM = -1;
+    static constexpr Int32 rNums[]
+        = {619, 720, 127, 481, 931, 816, 813, 233, 566, 247, 985, 724, 205, 
454, 863, 491, 741, 242, 949, 214, 733, 859, 335, 708, 621, 574,
+           73,  654, 730, 472, 419, 436, 278, 496, 867, 210, 399, 680, 480, 
51,  878, 465, 811, 169, 869, 675, 611, 697, 867, 561, 862, 687,
+           507, 283, 482, 129, 807, 591, 733, 623, 150, 238, 59,  379, 684, 
877, 625, 169, 643, 105, 170, 607, 520, 932, 727, 476, 693, 425,
+           174, 647, 73,  122, 335, 530, 442, 853, 695, 249, 445, 515, 909, 
545, 703, 919, 874, 474, 882, 500, 594, 612, 641, 801, 220, 162,
+           819, 984, 589, 513, 495, 799, 161, 604, 958, 533, 221, 400, 386, 
867, 600, 782, 382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
+           98,  553, 163, 354, 666, 933, 424, 341, 533, 870, 227, 730, 475, 
186, 263, 647, 537, 686, 600, 224, 469, 68,  770, 919, 190, 373,
+           294, 822, 808, 206, 184, 943, 795, 384, 383, 461, 404, 758, 839, 
887, 715, 67,  618, 276, 204, 918, 873, 777, 604, 560, 951, 160,
+           578, 722, 79,  804, 96,  409, 713, 940, 652, 934, 970, 447, 318, 
353, 859, 672, 112, 785, 645, 863, 803, 350, 139, 93,  354, 99,
+           820, 908, 609, 772, 154, 274, 580, 184, 79,  626, 630, 742, 653, 
282, 762, 623, 680, 81,  927, 626, 789, 125, 411, 521, 938, 300,
+           821, 78,  343, 175, 128, 250, 170, 774, 972, 275, 999, 639, 495, 
78,  352, 126, 857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
+           669, 112, 134, 694, 363, 992, 809, 743, 168, 974, 944, 375, 748, 
52,  600, 747, 642, 182, 862, 81,  344, 805, 988, 739, 511, 655,
+           814, 334, 249, 515, 897, 955, 664, 981, 649, 113, 974, 459, 893, 
228, 433, 837, 553, 268, 926, 240, 102, 654, 459, 51,  686, 754,
+           806, 760, 493, 403, 415, 394, 687, 700, 946, 670, 656, 610, 738, 
392, 760, 799, 887, 653, 978, 321, 576, 617, 626, 502, 894, 679,
+           243, 440, 680, 879, 194, 572, 640, 724, 926, 56,  204, 700, 707, 
151, 457, 449, 797, 195, 791, 558, 945, 679, 297, 59,  87,  824,
+           713, 663, 412, 693, 342, 606, 134, 108, 571, 364, 631, 212, 174, 
643, 304, 329, 343, 97,  430, 751, 497, 314, 983, 374, 822, 928,
+           140, 206, 73,  263, 980, 736, 876, 478, 430, 305, 170, 514, 364, 
692, 829, 82,  855, 953, 676, 246, 369, 970, 294, 750, 807, 827,
+           150, 790, 288, 923, 804, 378, 215, 828, 592, 281, 565, 555, 710, 
82,  896, 831, 547, 261, 524, 462, 293, 465, 502, 56,  661, 821,
+           976, 991, 658, 869, 905, 758, 745, 193, 768, 550, 608, 933, 378, 
286, 215, 979, 792, 961, 61,  688, 793, 644, 986, 403, 106, 366,
+           905, 644, 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, 780, 
773, 635, 389, 707, 100, 626, 958, 165, 504, 920, 176, 193, 713,
+           857, 265, 203, 50,  668, 108, 645, 990, 626, 197, 510, 357, 358, 
850, 858, 364, 936, 638};
+};
+
+
+class SplittableBzip2ReadBuffer : public CompressedReadBufferWrapper
+{
+public:
+    static constexpr Int64 BLOCK_DELIMITER = 0X314159265359L;
+    static constexpr Int64 EOS_DELIMITER = 0X177245385090L;
+    static constexpr Int32 DELIMITER_BIT_LENGTH = 48;
+
+private:
+    struct Data
+    {
+        bool inUse[256] = {false};
+
+        char seqToUnseq[256] = {0};
+        char selector[BZip2Constants::MAX_SELECTORS] = {0};
+        char selectorMtf[BZip2Constants::MAX_SELECTORS] = {0};
+
+        Int32 unzftab[256] = {0};
+
+        Int32 limit[BZip2Constants::N_GROUPS][BZip2Constants::MAX_ALPHA_SIZE] 
= {{0}};
+        Int32 base[BZip2Constants::N_GROUPS][BZip2Constants::MAX_ALPHA_SIZE] = 
{{0}};
+        Int32 perm[BZip2Constants::N_GROUPS][BZip2Constants::MAX_ALPHA_SIZE] = 
{{0}};
+        Int32 minLens[BZip2Constants::N_GROUPS] = {0};
+
+        Int32 cftab[257] = {0};
+        UInt16 getAndMoveToFrontDecode_yy[256] = {0};
+        UInt16 
temp_charArray2d[BZip2Constants::N_GROUPS][BZip2Constants::MAX_ALPHA_SIZE] = 
{{0}};
+
+        char recvDecodingTables_pos[BZip2Constants::N_GROUPS] = {0};
+
+        std::vector<Int32> tt;
+        std::vector<char> ll8;
+
+        explicit Data(Int32 blockSize100k_) : ll8(blockSize100k_ * 
BZip2Constants::BASE_BLOCK_SIZE) { }
+
+        std::vector<Int32> & initTT(Int32 length);
+        std::string toString();
+
+    private:
+        /// Helper static functions for toString()
+        template <typename T>
+        static std::string arrayToString(const std::vector<T> & arr);
+        template <typename T>
+        static std::string array2DToString(T 
arr[BZip2Constants::N_GROUPS][BZip2Constants::MAX_ALPHA_SIZE]);
+        template <typename T>
+        static std::string arrayToString(const T * arr, size_t size);
+    };
+
+    class CRC
+    {
+    public:
+        static constexpr UInt32 crc32Table[]
+            = {0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 
0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f,
+               0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 
0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
+               0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 
0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
+               0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 
0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
+               0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 
0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027,
+               0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 
0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+               0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 
0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c,
+               0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 
0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
+               0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 
0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
+               0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 
0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
+               0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 
0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044,
+               0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 
0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+               0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 
0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59,
+               0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 
0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
+               0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 
0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
+               0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 
0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
+               0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 
0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601,
+               0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 
0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+               0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 
0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad,
+               0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 
0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
+               0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 
0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
+               0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 
0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
+               0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 
0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12,
+               0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 
0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+               0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 
0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06,
+               0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 
0xb1f740b4};
+
+        CRC() { initialiseCRC(); }
+        ~CRC() = default;
+
+        void initialiseCRC() { globalCrc = 0xffffffff; }
+        Int32 getFinalCRC() const { return ~globalCrc; }
+        Int32 getGlobalCRC() const { return globalCrc; }
+        void setGlobalCRC(Int32 newCrc) { globalCrc = newCrc; }
+        void updateCRC(Int32 inCh)
+        {
+            Int32 temp = (globalCrc >> 24) ^ inCh;
+            if (temp < 0)
+                temp = 256 + temp;
+            globalCrc = (globalCrc << 8) ^ 
static_cast<Int32>(crc32Table[temp]);
+        }
+        void updateCRC(Int32 inCh, Int32 repeat)
+        {
+            Int32 globalCrcShadow = globalCrc;
+            while (repeat-- > 0)
+            {
+                Int32 temp = (globalCrcShadow >> 24) ^ inCh;
+                globalCrcShadow = (globalCrcShadow << 8) ^ 
static_cast<Int32>(crc32Table[(temp >= 0) ? temp : (temp + 256)]);
+            }
+            globalCrc = globalCrcShadow;
+        }
+
+    private:
+        Int32 globalCrc;
+    };
+
+    enum class STATE : int8_t
+    {
+        END_OF_FILE = 0,
+        START_BLOCK_STATE,
+        RAND_PART_A_STATE,
+        RAND_PART_B_STATE,
+        RAND_PART_C_STATE,
+        NO_RAND_PART_A_STATE,
+        NO_RAND_PART_B_STATE,
+        NO_RAND_PART_C_STATE,
+        NO_PROCESS_STATE
+    };
+
+    /// If current bzip2 file split is not the fist one, then 
start_need_special_process should be true.
+    /// When it is true, the decompressed result of the first block will be 
ignored except the last line.
+    /// Case1:
+    /// e.g. "line1 \n line2 \n line3", line1 and line2 will be ignored 
because they are processed in the previous split.
+    /// We are not sure if line3 is a completed line, so line3 will be 
concated before the decompressed result of the next block in current split.
+    /// Case2:
+    /// e.g. "line2 \n line2 \n line3 \n", all lines will be ignored because 
they are processed in the previous split.
+    const bool first_block_need_special_process;
+
+    /// If current bzip2 file split is not the last one, then 
end_need_special_process should be true.
+    /// When it is true, the decompressed result of the last block will be 
processed except the last line.
+    /// Case1:
+    /// e.g. "line1 \n line2 \n line3", line3 will be ignored because it is 
processed in the next split.
+    /// Case2:
+    /// e.g. "line1 \n line2 \n line3 \n", all lines will be processed because 
we are pretty sure that line3 is a completed line.
+    const bool last_block_need_special_process;
+    bool is_first_block;
+
+    Int32 blockSize100k;
+    STATE currentState;
+    bool skipResult;
+    Int32 currentChar;
+    Int32 storedBlockCRC;
+    bool blockRandomised;
+    std::unique_ptr<Data> data;
+    CRC crc;
+    Int32 computedBlockCRC;
+    Int32 storedCombinedCRC;
+    Int32 computedCombinedCRC;
+
+    Int32 origPtr;
+    Int32 nInUse;
+    Int64 bsBuff;
+    Int64 bsLive;
+    Int32 last;
+
+    Int32 su_count;
+    Int32 su_ch2;
+    Int32 su_chPrev;
+    Int32 su_i2;
+    Int32 su_j2;
+    Int32 su_rNToGo;
+    Int32 su_rTPos;
+    Int32 su_tPos;
+    char su_z;
+
+    /// SplittableBzip2ReadBuffer will skip bytes before the first block 
header. adjusted_start records file position after skipping.
+    /// It is only valid when input stream is seekable and block header could 
be found in input stream.
+    std::optional<size_t> adjusted_start;
+
+    static void hbCreateDecodeTables(
+        Int32 * __restrict limit,
+        Int32 * __restrict base,
+        Int32 * __restrict perm,
+        const UInt16 * __restrict length,
+        Int32 minLen,
+        Int32 maxLen,
+        Int32 alphaSize);
+
+public:
+    explicit SplittableBzip2ReadBuffer(
+        std::unique_ptr<ReadBuffer> in_,
+        bool first_block_need_special_process_ = false,
+        bool last_block_need_special_process_ = false,
+        size_t buf_size = DBMS_DEFAULT_BUFFER_SIZE,
+        char * existing_memory = nullptr,
+        size_t alignment = 0);
+
+    ~SplittableBzip2ReadBuffer() override = default;
+
+    std::optional<size_t> getAdjustedStart() const { return adjusted_start; }
+
+    bool nextImpl() override;
+
+    static bool skipToNextMarker(Int64 marker, Int32 markerBitLength, 
ReadBuffer & in_, Int64 & bsBuff_, Int64 & bsLive_);
+
+private:
+    Int32 read(char * dest, size_t dest_size, size_t offs, size_t len);
+    Int32 read0();
+    static Int32 readAByte(ReadBuffer & in_);
+
+    bool skipToNextMarker(Int64 marker, Int32 markerBitLength);
+
+    [[noreturn]] void reportCRCError();
+
+    void makeMaps();
+
+    void changeStateToProcessABlock();
+
+    void initBlock();
+
+    void endBlock();
+
+    void complete();
+
+    static Int64 bsR(Int64 n, ReadBuffer & in_, Int64 & bsBuff_, Int64 & 
bsLive_);
+    Int64 bsR(Int64 n);
+    bool bsGetBit();
+    char bsGetUByte() { return bsR(8); }
+    Int32 bsGetInt() { return static_cast<Int32>((((((bsR(8) << 8) | bsR(8)) 
<< 8) | bsR(8)) << 8) | bsR(8)); }
+
+    void recvDecodingTables();
+
+    void createHuffmanDecodingTables(Int32 alphaSize, Int32 nGroups);
+
+    void getAndMoveToFrontDecode();
+    Int32 getAndMoveToFrontDecode0(Int32 groupNo);
+
+    void setupBlock();
+    void setupRandPartA();
+    void setupNoRandPartA();
+    void setupRandPartB();
+    void setupRandPartC();
+    void setupNoRandPartB();
+    void setupNoRandPartC();
+};
+
+}
+#endif
diff --git a/cpp-ch/local-engine/Parser/CHColumnToSparkRow.cpp 
b/cpp-ch/local-engine/Parser/CHColumnToSparkRow.cpp
index 602cd3d683..d5a6c96419 100644
--- a/cpp-ch/local-engine/Parser/CHColumnToSparkRow.cpp
+++ b/cpp-ch/local-engine/Parser/CHColumnToSparkRow.cpp
@@ -299,8 +299,8 @@ static void writeValue(
 SparkRowInfo::SparkRowInfo(
     const DB::ColumnsWithTypeAndName & cols,
     const DB::DataTypes & dataTypes,
-    const size_t & col_size,
-    const size_t & row_size,
+    size_t col_size,
+    size_t row_size,
     const MaskVector & masks)
     : types(dataTypes)
     , num_rows(masks == nullptr ? row_size : masks->size())
diff --git a/cpp-ch/local-engine/Parser/CHColumnToSparkRow.h 
b/cpp-ch/local-engine/Parser/CHColumnToSparkRow.h
index cb46a3850d..6af580850d 100644
--- a/cpp-ch/local-engine/Parser/CHColumnToSparkRow.h
+++ b/cpp-ch/local-engine/Parser/CHColumnToSparkRow.h
@@ -44,8 +44,8 @@ public:
     explicit SparkRowInfo(
         const DB::ColumnsWithTypeAndName & cols,
         const DB::DataTypes & types,
-        const size_t & col_size,
-        const size_t & row_size,
+        size_t col_size,
+        size_t row_size,
         const MaskVector & masks = nullptr);
 
     const DB::DataTypes & getDataTypes() const;
diff --git 
a/cpp-ch/local-engine/Storages/SubstraitSource/ExcelTextFormatFile.cpp 
b/cpp-ch/local-engine/Storages/SubstraitSource/ExcelTextFormatFile.cpp
index 7e87d11dc7..6e12938e45 100644
--- a/cpp-ch/local-engine/Storages/SubstraitSource/ExcelTextFormatFile.cpp
+++ b/cpp-ch/local-engine/Storages/SubstraitSource/ExcelTextFormatFile.cpp
@@ -65,7 +65,7 @@ bool ExcelTextFormatFile::useThis(const DB::ContextPtr & 
context)
 FormatFile::InputFormatPtr ExcelTextFormatFile::createInputFormat(const 
DB::Block & header)
 {
     auto res = std::make_shared<FormatFile::InputFormat>();
-    res->read_buffer = read_buffer_builder->build(file_info, true);
+    res->read_buffer = read_buffer_builder->build(file_info);
 
     DB::FormatSettings format_settings = createFormatSettings();
     size_t max_block_size = file_info.text().max_block_size();
diff --git a/cpp-ch/local-engine/Storages/SubstraitSource/FormatFile.h 
b/cpp-ch/local-engine/Storages/SubstraitSource/FormatFile.h
index 8ab82f312f..14b53f6842 100644
--- a/cpp-ch/local-engine/Storages/SubstraitSource/FormatFile.h
+++ b/cpp-ch/local-engine/Storages/SubstraitSource/FormatFile.h
@@ -81,7 +81,6 @@ protected:
     ReadBufferBuilderPtr read_buffer_builder;
     std::vector<String> partition_keys;
     std::map<String, String> partition_values;
-    // std::optional<SourceFilter> filter;
     std::shared_ptr<const DB::KeyCondition> key_condition;
 };
 using FormatFilePtr = std::shared_ptr<FormatFile>;
diff --git a/cpp-ch/local-engine/Storages/SubstraitSource/JSONFormatFile.cpp 
b/cpp-ch/local-engine/Storages/SubstraitSource/JSONFormatFile.cpp
index b3e47681a8..e6e7573be7 100644
--- a/cpp-ch/local-engine/Storages/SubstraitSource/JSONFormatFile.cpp
+++ b/cpp-ch/local-engine/Storages/SubstraitSource/JSONFormatFile.cpp
@@ -31,7 +31,7 @@ JSONFormatFile::JSONFormatFile(DB::ContextPtr context_, const 
substrait::ReadRel
 FormatFile::InputFormatPtr JSONFormatFile::createInputFormat(const DB::Block & 
header)
 {
     auto res = std::make_shared<FormatFile::InputFormat>();
-    res->read_buffer = 
read_buffer_builder->buildWithCompressionWrapper(file_info, true);
+    res->read_buffer = 
read_buffer_builder->buildWithCompressionWrapper(file_info);
 
     DB::FormatSettings format_settings = DB::getFormatSettings(context);
     format_settings.with_names_use_header = true;
diff --git a/cpp-ch/local-engine/Storages/SubstraitSource/ReadBufferBuilder.cpp 
b/cpp-ch/local-engine/Storages/SubstraitSource/ReadBufferBuilder.cpp
index 56205f224a..e3753495b4 100644
--- a/cpp-ch/local-engine/Storages/SubstraitSource/ReadBufferBuilder.cpp
+++ b/cpp-ch/local-engine/Storages/SubstraitSource/ReadBufferBuilder.cpp
@@ -19,7 +19,6 @@
 
 #include <memory>
 #include <shared_mutex>
-#include <thread>
 #include <Core/Settings.h>
 #include <Disks/IO/AsynchronousBoundedReadBuffer.h>
 #include <Disks/IO/CachedOnDiskReadBufferFromFile.h>
@@ -29,6 +28,7 @@
 #include <IO/ReadBufferFromFile.h>
 #include <IO/ReadBufferFromS3.h>
 #include <IO/ReadSettings.h>
+#include <IO/SplittableBzip2ReadBuffer.h>
 #include <IO/S3/getObjectInfo.h>
 #include <IO/S3Common.h>
 #include <IO/SeekableReadBuffer.h>
@@ -84,6 +84,7 @@ namespace ErrorCodes
 namespace local_engine
 {
 
+using namespace DB;
 FileCacheConcurrentMap ReadBufferBuilder::files_cache_time_map;
 
 template <class key_type, class value_type>
@@ -115,9 +116,9 @@ private:
     std::shared_mutex rwLock;
 };
 
-static std::pair<size_t, size_t> 
adjustFileReadPosition(DB::ReadBufferFromFileBase & buffer, size_t 
read_start_pos, size_t read_end_pos)
+static std::pair<size_t, size_t> getAdjustedReadRange(SeekableReadBuffer & 
buffer, const std::pair<size_t, size_t> & start_end)
 {
-    auto get_next_line_pos = [&](DB::ReadBufferFromFileBase & buf) -> size_t
+    auto get_next_line_pos = [&](SeekableReadBuffer & buf) -> size_t
     {
         while (!buf.eof())
         {
@@ -151,6 +152,8 @@ static std::pair<size_t, size_t> 
adjustFileReadPosition(DB::ReadBufferFromFileBa
         return buf.getPosition();
     };
 
+    size_t read_start_pos = start_end.first;
+    size_t read_end_pos = start_end.second;
     std::pair<size_t, size_t> result;
 
     if (read_start_pos == 0)
@@ -172,24 +175,38 @@ static std::pair<size_t, size_t> 
adjustFileReadPosition(DB::ReadBufferFromFileBa
     return result;
 }
 
-static std::unique_ptr<DB::ReadBufferFromFileBase>
-resetOffset(std::unique_ptr<DB::ReadBufferFromFileBase> read_buffer, const 
substrait::ReadRel::LocalFiles::FileOrFiles & file_info)
+static std::unique_ptr<SeekableReadBuffer>
+adjustReadRangeIfNeeded(std::unique_ptr<SeekableReadBuffer> read_buffer, const 
substrait::ReadRel::LocalFiles::FileOrFiles & file_info)
 {
-    auto start_end_pos = adjustFileReadPosition(*read_buffer, 
file_info.start(), file_info.start() + file_info.length());
+    /// Skip formats in which rows are not seperated by newline characters.
+    if (!(file_info.has_text() || file_info.has_json()))
+        return std::move(read_buffer);
+
+    /// Skip text/json files with compression.
+    /// TODO implement adjustFileReadPosition when compression method is bzip2
+    Poco::URI file_uri(file_info.uri_file());
+    DB::CompressionMethod compression = 
DB::chooseCompressionMethod(file_uri.getPath(), "auto");
+    if (compression != CompressionMethod::None)
+        return std::move(read_buffer);
+
+    std::pair<size_t, size_t> start_end{file_info.start(), file_info.start() + 
file_info.length()};
+    start_end = getAdjustedReadRange(*read_buffer, start_end);
+
     LOG_DEBUG(
         &Poco::Logger::get("ReadBufferBuilder"),
         "File read start and end position adjusted from {},{} to {},{}",
         file_info.start(),
         file_info.start() + file_info.length(),
-        start_end_pos.first,
-        start_end_pos.second);
+        start_end.first,
+        start_end.second);
 
+    /// If read buffer doesn't support right bounded reads, wrap it with 
BoundedReadBuffer to enable right bounded reads.
     if (dynamic_cast<DB::ReadBufferFromHDFS *>(read_buffer.get()) || 
dynamic_cast<DB::ReadBufferFromFile *>(read_buffer.get()))
         read_buffer = 
std::make_unique<DB::BoundedReadBuffer>(std::move(read_buffer));
 
-    read_buffer->seek(start_end_pos.first, SEEK_SET);
-    read_buffer->setReadUntilPosition(start_end_pos.second);
-    return read_buffer;
+    read_buffer->seek(start_end.first, SEEK_SET);
+    read_buffer->setReadUntilPosition(start_end.second);
+    return std::move(read_buffer);
 }
 
 class LocalFileReadBufferBuilder : public ReadBufferBuilder
@@ -199,7 +216,7 @@ public:
     ~LocalFileReadBufferBuilder() override = default;
 
     std::unique_ptr<DB::ReadBuffer>
-    build(const substrait::ReadRel::LocalFiles::FileOrFiles & file_info, bool 
set_read_util_position) override
+    build(const substrait::ReadRel::LocalFiles::FileOrFiles & file_info) 
override
     {
         Poco::URI file_uri(file_info.uri_file());
         std::unique_ptr<DB::ReadBufferFromFileBase> read_buffer;
@@ -213,10 +230,7 @@ public:
         else
             read_buffer = std::make_unique<DB::ReadBufferFromFile>(file_path);
 
-        if (set_read_util_position)
-            return resetOffset(std::move(read_buffer), file_info);
-
-        return read_buffer;
+        return adjustReadRangeIfNeeded(std::move(read_buffer), file_info);
     }
 };
 
@@ -229,71 +243,89 @@ public:
     ~HDFSFileReadBufferBuilder() override = default;
 
     std::unique_ptr<DB::ReadBuffer>
-    build(const substrait::ReadRel::LocalFiles::FileOrFiles & file_info, bool 
set_read_util_position) override
+    build(const substrait::ReadRel::LocalFiles::FileOrFiles & file_info) 
override
     {
         DB::ReadSettings read_settings = getReadSettings();
-        auto & config = context->getConfigRef();
-        auto hdfs_config = HdfsConfig::loadFromContext(config, read_settings);
-        Poco::URI file_uri(file_info.uri_file());
-        std::string uri_path = "hdfs://" + file_uri.getHost();
-        if (file_uri.getPort())
-            uri_path += ":" + 
std::to_string(static_cast<unsigned>(file_uri.getPort()));
+        const auto & config = context->getConfigRef();
+
+        /// Get hdfs_uri
+        Poco::URI uri(file_info.uri_file());
+        auto hdfs_file_path = uri.getPath();
+        std::string hdfs_uri = "hdfs://" + uri.getHost();
+        if (uri.getPort())
+            hdfs_uri += ":" + std::to_string(uri.getPort());
 
-        size_t file_size = 0;
-        size_t modified_time = 0;
+        std::optional<size_t> file_size;
+        std::optional<size_t> modified_time;
         if (file_info.has_properties())
         {
-            file_size = file_info.properties().filesize();
+            if (file_info.properties().filesize() > 0)
+            {
+                /// filesize may be zero, under such condition we should not 
set file_size
+                file_size = file_info.properties().filesize();
+            }
+
             modified_time = file_info.properties().modificationtime();
         }
 
-        std::unique_ptr<DB::ReadBufferFromFileBase> read_buffer;
-
-        if (hdfs_config.hdfs_async)
+        std::unique_ptr<SeekableReadBuffer> read_buffer;
+        if (!read_settings.enable_filesystem_cache)
         {
-            std::optional<size_t> size = std::nullopt;
-            if (file_size)
-                size = file_size;
+            bool thread_pool_read = read_settings.remote_fs_method == 
DB::RemoteFSReadMethod::threadpool;
+            /// ORC and Parquet reader had already implemented async prefetch. 
They don't rely on AsynchronousReadBufferFromHDFS
+            bool use_async_prefetch
+                = read_settings.remote_fs_prefetch && thread_pool_read && 
(file_info.has_text() || file_info.has_json());
+            auto raw_read_buffer = std::make_unique<ReadBufferFromHDFS>(
+                    hdfs_uri,
+                    hdfs_file_path,
+                    config,
+                    read_settings,
+                    /* read_until_position */ 0,
+                    /* use_external_buffer */ true,
+                    file_size);
 
-            read_buffer
-                = std::make_unique<DB::ReadBufferFromHDFS>(uri_path, 
file_uri.getPath(), config, read_settings, file_size, true, size);
-            if (read_settings.remote_fs_prefetch)
-            {
-                auto & pool_reader = 
context->getThreadPoolReader(DB::FilesystemReaderType::ASYNCHRONOUS_REMOTE_FS_READER);
-                read_buffer = 
std::make_unique<DB::AsynchronousBoundedReadBuffer>(std::move(read_buffer), 
pool_reader, read_settings);
-            }
+            if (use_async_prefetch)
+                read_buffer = std::make_unique<AsynchronousReadBufferFromHDFS>(
+                    
getThreadPoolReader(FilesystemReaderType::ASYNCHRONOUS_REMOTE_FS_READER), 
read_settings, std::move(raw_read_buffer));
+            else
+                read_buffer = std::move(raw_read_buffer);
         }
         else
         {
-            if (!file_size)
+            if (!file_size.has_value())
             {
                 // only for spark3.2 file partition not contained file size
                 // so first compute file size first
-                auto read_buffer_impl = 
std::make_unique<DB::ReadBufferFromHDFS>(
-                    uri_path, file_uri.getPath(), config, read_settings, 0, 
true);
-                file_size = read_buffer_impl->getFileSize();
+                auto tmp_read_buffer = 
std::make_unique<DB::ReadBufferFromHDFS>(
+                    hdfs_uri,
+                    hdfs_file_path,
+                    config,
+                    read_settings,
+                    /* read_until_position */ 0,
+                    /* use_external_buffer */ true);
+                file_size = tmp_read_buffer->getFileSize();
             }
 
-            ReadBufferCreator hdfs_read_buffer_creator
-                = [this, hdfs_uri = uri_path, hdfs_file_path = 
file_uri.getPath(), read_settings, &config](
-                      bool /* restricted_seek */, const DB::StoredObject & 
object) -> std::unique_ptr<DB::ReadBufferFromHDFS>
-            {
+            if (!modified_time.has_value())
+                modified_time = 0;
+
+            ReadBufferCreator read_buffer_creator
+                = [this, hdfs_uri = hdfs_uri, hdfs_file_path = hdfs_file_path, 
read_settings, &config](
+                      bool /* restricted_seek */, const DB::StoredObject & 
object) -> std::unique_ptr<DB::ReadBufferFromHDFS> {
                 return std::make_unique<DB::ReadBufferFromHDFS>(
                     hdfs_uri, hdfs_file_path, config, read_settings, 0, true, 
object.bytes_size);
             };
 
-            auto remote_path = file_uri.getPath().substr(1);
-            DB::StoredObjects stored_objects{DB::StoredObject{remote_path, "", 
file_size}};
-            auto cache_creator = wrapWithCache(hdfs_read_buffer_creator, 
read_settings, remote_path, modified_time, file_size);
+            auto remote_path = uri.getPath().substr(1);
+            DB::StoredObjects stored_objects{DB::StoredObject{remote_path, "", 
*file_size}};
+            auto cache_creator = wrapWithCache(
+                read_buffer_creator, read_settings, remote_path, 
*modified_time, *file_size);
             auto cache_hdfs_read = 
std::make_unique<DB::ReadBufferFromRemoteFSGather>(
                 std::move(cache_creator), stored_objects, read_settings, 
nullptr, /* use_external_buffer */ false);
             read_buffer = std::move(cache_hdfs_read);
         }
 
-        if (set_read_util_position)
-            return resetOffset(std::move(read_buffer), file_info);
-
-        return read_buffer;
+        return adjustReadRangeIfNeeded(std::move(read_buffer), file_info);
     }
 
 private:
@@ -330,7 +362,7 @@ public:
     ~S3FileReadBufferBuilder() override = default;
 
     std::unique_ptr<DB::ReadBuffer>
-    build(const substrait::ReadRel::LocalFiles::FileOrFiles & file_info, bool 
set_read_util_position) override
+    build(const substrait::ReadRel::LocalFiles::FileOrFiles & file_info) 
override
     {
         DB::ReadSettings read_settings = getReadSettings();
         Poco::URI file_uri(file_info.uri_file());
@@ -382,10 +414,7 @@ public:
         if (read_settings.remote_fs_prefetch)
             async_reader->prefetch(Priority{});
 
-        if (set_read_util_position)
-            return resetOffset(std::move(async_reader), file_info);
-
-        return async_reader;
+        return adjustReadRangeIfNeeded(std::move(async_reader), file_info);
     }
 
 private:
@@ -570,7 +599,7 @@ public:
     explicit AzureBlobReadBuffer(DB::ContextPtr context_) : 
ReadBufferBuilder(context_) { }
     ~AzureBlobReadBuffer() override = default;
 
-    std::unique_ptr<DB::ReadBuffer> build(const 
substrait::ReadRel::LocalFiles::FileOrFiles & file_info, bool) override
+    std::unique_ptr<DB::ReadBuffer> build(const 
substrait::ReadRel::LocalFiles::FileOrFiles & file_info) override
     {
         Poco::URI file_uri(file_info.uri_file());
         std::unique_ptr<DB::ReadBuffer> read_buffer;
@@ -631,35 +660,110 @@ ReadBufferBuilder::ReadBufferBuilder(DB::ContextPtr 
context_) : context(context_
 DB::ReadSettings ReadBufferBuilder::getReadSettings() const
 {
     DB::ReadSettings read_settings = context->getReadSettings();
-    if (context->getConfigRef().getBool("gluten_cache.local.enabled", false))
-        read_settings.enable_filesystem_cache = true;
-    else
-        read_settings.enable_filesystem_cache = false;
+    const auto & config = context->getConfigRef();
+
+    /// Override enable_filesystem_cache with gluten config
+    read_settings.enable_filesystem_cache = 
config.getBool("gluten_cache.local.enabled", false);
+
+    /// Override remote_fs_prefetch with gluten config
+    read_settings.remote_fs_prefetch = config.getBool("hdfs.enable_async_io", 
false);
 
     return read_settings;
 }
 
 std::unique_ptr<DB::ReadBuffer>
-ReadBufferBuilder::buildWithCompressionWrapper(const 
substrait::ReadRel::LocalFiles::FileOrFiles & file_info, bool 
set_read_util_position)
+ReadBufferBuilder::wrapWithBzip2(std::unique_ptr<DB::ReadBuffer> in, const 
substrait::ReadRel::LocalFiles::FileOrFiles & file_info)
+{
+    /// Bzip2 compressed file is splittable and we need to adjust read range 
for each split
+    auto * seekable = dynamic_cast<SeekableReadBuffer *>(in.release());
+    if (!seekable)
+        throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "ReadBuffer 
underlying BZIP2 decompressor must be seekable");
+    std::unique_ptr<SeekableReadBuffer> seekable_in(seekable);
+
+    size_t file_size = getFileSizeFromReadBuffer(*seekable_in);
+    size_t start = file_info.start();
+    size_t end = file_info.start() + file_info.length();
+
+    /// No need to adjust start becuase it is already processed inside 
SplittableBzip2ReadBuffer
+    size_t new_start = start;
+
+    /// Extend end to the end of next block.
+    size_t new_end = end;
+    if (end < file_size)
+    {
+        Int64 bs_buff = 0;
+        Int64 bs_live = 0;
+
+        /// From end position skip to the second block delimiter.
+        seekable_in->seek(end, SEEK_SET);
+        for (size_t i = 0; i < 2; ++i)
+        {
+            size_t pos = seekable_in->getPosition();
+            bool ok = SplittableBzip2ReadBuffer::skipToNextMarker(
+                SplittableBzip2ReadBuffer::BLOCK_DELIMITER,
+                SplittableBzip2ReadBuffer::DELIMITER_BIT_LENGTH,
+                *seekable_in,
+                bs_buff,
+                bs_live);
+
+            if (seekable_in->eof())
+                break;
+
+            if (!ok)
+                throw Exception(ErrorCodes::LOGICAL_ERROR, "Can't find next 
block delimiter in after offset: {}", pos);
+        }
+        new_end = seekable->eof() ? file_size : seekable_in->getPosition() - 
SplittableBzip2ReadBuffer::DELIMITER_BIT_LENGTH / 8 + 1;
+    }
+
+    LOG_DEBUG(
+        &Poco::Logger::get("ReadBufferBuilder"),
+        "File read start and end position adjusted from {},{} to {},{}",
+        start,
+        end,
+        new_start,
+        new_end);
+
+    std::unique_ptr<SeekableReadBuffer> bounded_in;
+    if (dynamic_cast<DB::ReadBufferFromHDFS *>(seekable_in.get()) || 
dynamic_cast<DB::ReadBufferFromFile *>(seekable_in.get()))
+        bounded_in = 
std::make_unique<BoundedReadBuffer>(std::move(seekable_in));
+    else
+        bounded_in = std::move(seekable_in);
+
+    bounded_in->seek(new_start, SEEK_SET);
+    bounded_in->setReadUntilPosition(new_end);
+    bool first_block_need_special_process = (new_start > 0);
+    bool last_block_need_special_process = (new_end < file_size);
+    auto decompressed_in = std::make_unique<SplittableBzip2ReadBuffer>(
+        std::move(bounded_in), first_block_need_special_process, 
last_block_need_special_process);
+    return std::move(decompressed_in);
+}
+
+std::unique_ptr<DB::ReadBuffer>
+ReadBufferBuilder::buildWithCompressionWrapper(const 
substrait::ReadRel::LocalFiles::FileOrFiles & file_info)
 {
-    auto in = build(file_info, set_read_util_position);
+    auto in = build(file_info);
 
     /// Wrap the read buffer with compression method if exists
     Poco::URI file_uri(file_info.uri_file());
     DB::CompressionMethod compression = 
DB::chooseCompressionMethod(file_uri.getPath(), "auto");
-    return compression != DB::CompressionMethod::None ? 
DB::wrapReadBufferWithCompressionMethod(std::move(in), compression) : 
std::move(in);
+
+    if (compression == CompressionMethod::Bzip2)
+        return wrapWithBzip2(std::move(in), file_info);
+    else
+        return wrapReadBufferWithCompressionMethod(std::move(in), compression);
 }
 
 ReadBufferBuilder::ReadBufferCreator ReadBufferBuilder::wrapWithCache(
     ReadBufferCreator read_buffer_creator,
     DB::ReadSettings & read_settings,
     const String & key,
-    const size_t & last_modified_time,
-    const size_t & file_size)
+    size_t last_modified_time,
+    size_t file_size)
 {
     const auto & config = context->getConfigRef();
     if (!config.getBool("gluten_cache.local.enabled", false))
         return read_buffer_creator;
+
     read_settings.enable_filesystem_cache = true;
     if (!file_cache)
     {
@@ -709,7 +813,7 @@ ReadBufferBuilder::ReadBufferCreator 
ReadBufferBuilder::wrapWithCache(
     };
 }
 
-void ReadBufferBuilder::updateCaches(const String & key, const size_t & 
last_modified_time, const size_t & file_size) const
+void ReadBufferBuilder::updateCaches(const String & key, size_t 
last_modified_time, size_t file_size) const
 {
     if (!file_cache)
         return;
diff --git a/cpp-ch/local-engine/Storages/SubstraitSource/ReadBufferBuilder.h 
b/cpp-ch/local-engine/Storages/SubstraitSource/ReadBufferBuilder.h
index 5e63a93e3d..f2c1c10a6f 100644
--- a/cpp-ch/local-engine/Storages/SubstraitSource/ReadBufferBuilder.h
+++ b/cpp-ch/local-engine/Storages/SubstraitSource/ReadBufferBuilder.h
@@ -37,26 +37,29 @@ public:
 
     /// build a new read buffer
     virtual std::unique_ptr<DB::ReadBuffer>
-    build(const substrait::ReadRel::LocalFiles::FileOrFiles & file_info, bool 
set_read_util_position = false) = 0;
+    build(const substrait::ReadRel::LocalFiles::FileOrFiles & file_info) = 0;
 
     /// build a new read buffer, consider compression method
-    std::unique_ptr<DB::ReadBuffer> buildWithCompressionWrapper(const 
substrait::ReadRel::LocalFiles::FileOrFiles & file_info, bool 
set_read_util_position = false);
+    std::unique_ptr<DB::ReadBuffer> buildWithCompressionWrapper(const 
substrait::ReadRel::LocalFiles::FileOrFiles & file_info);
 
 protected:
     using ReadBufferCreator = 
std::function<std::unique_ptr<DB::ReadBufferFromFileBase>(bool restricted_seek, 
const DB::StoredObject & object)>;
 
+    std::unique_ptr<DB::ReadBuffer>
+    wrapWithBzip2(std::unique_ptr<DB::ReadBuffer> in, const 
substrait::ReadRel::LocalFiles::FileOrFiles & file_info);
+
     ReadBufferCreator wrapWithCache(
         ReadBufferCreator read_buffer_creator,
         DB::ReadSettings & read_settings,
         const String & key,
-        const size_t & last_modified_time,
-        const size_t & file_size);
+        size_t last_modified_time,
+        size_t file_size);
 
     DB::ReadSettings getReadSettings() const;
     DB::ContextPtr context;
 
 private:
-    void updateCaches(const String & key, const size_t & last_modified_time, 
const size_t & file_size) const;
+    void updateCaches(const String & key, size_t last_modified_time, size_t 
file_size) const;
 
 public:
     DB::FileCachePtr file_cache = nullptr;
diff --git a/cpp-ch/local-engine/Storages/SubstraitSource/TextFormatFile.cpp 
b/cpp-ch/local-engine/Storages/SubstraitSource/TextFormatFile.cpp
index b22f883f5d..71362c5b60 100644
--- a/cpp-ch/local-engine/Storages/SubstraitSource/TextFormatFile.cpp
+++ b/cpp-ch/local-engine/Storages/SubstraitSource/TextFormatFile.cpp
@@ -35,7 +35,7 @@ TextFormatFile::TextFormatFile(
 FormatFile::InputFormatPtr TextFormatFile::createInputFormat(const DB::Block & 
header)
 {
     auto res = std::make_shared<FormatFile::InputFormat>();
-    res->read_buffer = 
read_buffer_builder->buildWithCompressionWrapper(file_info, true);
+    res->read_buffer = 
read_buffer_builder->buildWithCompressionWrapper(file_info);
 
     /// Initialize format params
     size_t max_block_size = file_info.text().max_block_size();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to