Repository: flow-php/flow (PHP)

Issue:
[Proposal]: Allow to chose encoding for parquet columns
### Describe the Proposal

Currently Parquet library support following encodings: 

- Plain
- RLEDictionary
- Delta (work in progress) 

But more will come. 

Right now Plain is used for everything, RLEDictionary is unused, and Delta is going to be used for Int32 and Int64. 

However it would be good to let end user to decide what encoding for what type of column to use.

### API Adjustments

We need to add new option, ColumnsEncodings that would accept `array<string, string>` where `key` is a column and `value` is an Encoding enum. 

Whenever this option is provided, it should override default encodings. 

### Are you intenting to also work on proposed change?


Yes

### Are you interested in sponsoring this change? 


None

### Integration & Dependencies

_No response_



Fix (patch):
diff --git a/documentation/components/libs/parquet.md b/documentation/components/libs/parquet.md
index 61e994344..1589e4c41 100644
--- a/documentation/components/libs/parquet.md
+++ b/documentation/components/libs/parquet.md
@@ -307,3 +307,217 @@ If you want to achieve the best compression, you should use `GZIP` or `SNAPPY` w
 
 For not yet supported algorithms, please check our [Roadmap](https://github.com/orgs/flow-php/projects/1) to understand when they will be supported.
 
+## Column Encodings
+
+Parquet supports various column encoding algorithms that can significantly impact file size and query performance. 
+You can specify custom encodings for individual columns using flat path notation.
+
+### Available Encodings
+
+#### PLAIN
+The default encoding that stores values as-is without any compression scheme.
+
+**When to use:**
+- Small datasets where compression overhead isn't justified
+- Columns with high cardinality and random distribution
+- When you need maximum compatibility with other Parquet readers
+
+**Supported types:** All column types
+
+```php
+use Flow\Parquet\Options;
+use Flow\Parquet\Option;
+
+$options = Options::default()->set(Option::COLUMNS_ENCODINGS, [
+    'description' => 'PLAIN',
+    'uuid' => 'PLAIN'
+]);
+```
+
+#### RLE_DICTIONARY
+Run Length Encoding with Dictionary compression. Values are stored in a dictionary and replaced with indices.
+
+**When to use:**
+- Columns with low cardinality (many repeated values)
+- String columns with repeated categories (status, country, department)
+- Enumeration-like data
+- Significant file size reduction (often 50-90% smaller)
+
+**Supported types:** All types except `FIXED_LEN_BYTE_ARRAY`
+
+```php
+$options = Options::default()->set(Option::COLUMNS_ENCODINGS, [
+    'status' => 'RLE_DICTIONARY',          // 'active', 'inactive', 'pending'  
+    'country_code' => 'RLE_DICTIONARY',    // 'US', 'UK', 'DE', 'FR'
+    'department' => 'RLE_DICTIONARY'       // 'engineering', 'sales', 'marketing'
+]);
+```
+
+#### DELTA_BINARY_PACKED
+Delta encoding with binary packing for integer columns. Stores differences between consecutive values.
+
+**When to use:**
+- Sequential or monotonic integer data (IDs, timestamps, counters)
+- Time series data with incremental values
+- Can achieve 70-95% compression for sequential data
+
+**Supported types:** Only `INT32` and `INT64`
+
+```php
+$options = Options::default()->set(Option::COLUMNS_ENCODINGS, [
+    'user_id' => 'DELTA_BINARY_PACKED',     // 1, 2, 3, 4, 5...
+    'timestamp_ms' => 'DELTA_BINARY_PACKED', // 1634567890123, 1634567890124...
+    'order_number' => 'DELTA_BINARY_PACKED'  // Sequential order IDs
+]);
+```
+
+### Using Custom Encodings
+
+#### Basic Column Encoding
+
+```php
+use Flow\Parquet\{Writer, Options, Option};
+use Flow\Parquet\ParquetFile\Schema;
+use Flow\Parquet\ParquetFile\Schema\FlatColumn;
+
+$schema = Schema::with(
+    FlatColumn::int64('user_id'),
+    FlatColumn::string('status'),
+    FlatColumn::string('description')
+);
+
+$options = Options::default()->set(Option::COLUMNS_ENCODINGS, [
+    'user_id' => 'DELTA_BINARY_PACKED',  // Sequential IDs
+    'status' => 'RLE_DICTIONARY',        // Limited set of values
+    'description' => 'PLAIN'             // High variance text
+]);
+
+$writer = new Writer(compressions: Compressions::SNAPPY, options: $options);
+```
+
+#### Nested Column Encoding (Flat Path Notation)
+
+For nested structures, use dot notation to specify the exact column path. 
+The flat path follows Parquet's internal structure conventions:
+
+**Flat Path Patterns:**
+- **Struct fields**: `parent.field_name` 
+- **List elements**: `list_name.list.element`
+- **Map keys**: `map_name.key_value.key`
+- **Map values**: `map_name.key_value.value`
+
+```php
+use Flow\Parquet\ParquetFile\Schema\{NestedColumn, ListElement, MapKey, MapValue};
+
+$schema = Schema::with(
+    NestedColumn::struct('user', [
+        FlatColumn::int64('id'),
+        FlatColumn::string('name'),
+        NestedColumn::struct('address', [
+            FlatColumn::string('street'),
+            FlatColumn::string('city'),
+            FlatColumn::string('country')
+        ])
+    ]),
+    NestedColumn::list('tags', ListElement::string()),
+    NestedColumn::map('metadata', MapKey::string(), MapValue::string())
+);
+```
+
+**Understanding Flat Paths:**
+
+```php
+// STRUCT: Direct field access with dot notation
+'user.id'              // user struct → id field
+'user.name'            // user struct → name field  
+'user.address.street'  // user struct → address struct → street field
+'user.address.city'    // user struct → address struct → city field
+'user.address.country' // user struct → address struct → country field
+
+// LIST: Always includes intermediate '.list.element' structure
+'tags.list.element'    // tags list → list wrapper → element (the actual string values)
+
+// MAP: Always includes intermediate '.key_value' structure  
+'metadata.key_value.key'   // metadata map → key_value wrapper → key (string keys)
+'metadata.key_value.value' // metadata map → key_value wrapper → value (string values)
+```
+
+**Applying Custom Encodings:**
+
+```php
+$options = Options::default()->set(Option::COLUMNS_ENCODINGS, [
+    // Struct fields - direct access
+    'user.id' => 'DELTA_BINARY_PACKED',
+    'user.name' => 'RLE_DICTIONARY', 
+    'user.address.street' => 'PLAIN',
+    'user.address.city' => 'RLE_DICTIONARY',
+    'user.address.country' => 'RLE_DICTIONARY',
+    
+    // List elements - note the '.list.element' suffix
+    'tags.list.element' => 'RLE_DICTIONARY',
+    
+    // Map key/value pairs - note the '.key_value.key/value' suffix
+    'metadata.key_value.key' => 'RLE_DICTIONARY',
+    'metadata.key_value.value' => 'PLAIN'
+]);
+```
+
+**Complex Nested Example:**
+
+```php
+// Complex nested structure with lists of structs and maps
+$schema = Schema::with(
+    NestedColumn::list('orders', ListElement::structure([
+        FlatColumn::int64('order_id'),
+        FlatColumn::string('status'),
+        NestedColumn::map('attributes', MapKey::string(), MapValue::string())
+    ]))
+);
+
+$options = Options::default()->set(Option::COLUMNS_ENCODINGS, [
+    // List of structs: list_name.list.element.field_name
+    'orders.list.element.order_id' => 'DELTA_BINARY_PACKED',
+    'orders.list.element.status' => 'RLE_DICTIONARY',
+    
+    // Map inside list element: list_name.list.element.map_name.key_value.key/value
+    'orders.list.element.attributes.key_value.key' => 'RLE_DICTIONARY',
+    'orders.list.element.attributes.key_value.value' => 'PLAIN'
+]);
+```
+
+#### Mixed Encoding Strategy
+
+```php
+$options = Options::default()->set(Option::COLUMNS_ENCODINGS, [
+    // High cardinality sequential data
+    'order_id' => 'DELTA_BINARY_PACKED',
+    'created_timestamp' => 'DELTA_BINARY_PACKED',
+    
+    // Low cardinality categorical data  
+    'order_status' => 'RLE_DICTIONARY',
+    'payment_method' => 'RLE_DICTIONARY',
+    'shipping_country' => 'RLE_DICTIONARY',
+    
+    // High variance descriptive data
+    'customer_notes' => 'PLAIN',
+    'product_description' => 'PLAIN'
+]);
+```
+
+### Encoding Compatibility
+
+| Encoding | INT32/INT64 | BYTE_ARRAY | BOOLEAN | FLOAT/DOUBLE | FIXED_LEN_BYTE_ARRAY |
+|----------|-------------|------------|---------|--------------|----------------------|
+| PLAIN | ✅ | ✅ | ✅ | ✅ | ✅ |
+| RLE_DICTIONARY | ✅ | ✅ | ✅ | ✅ | ❌ |
+| DELTA_BINARY_PACKED | ✅ | ❌ | ❌ | ❌ | ❌ |
+
+### Performance Guidelines
+
+1. **Analyze your data first** - Check cardinality and distribution patterns
+2. **Use RLE_DICTIONARY for categorical data** - Countries, statuses, departments
+3. **Use DELTA_BINARY_PACKED for sequential integers** - IDs, timestamps, counters  
+4. **Use PLAIN for high-variance data** - Descriptions, UUIDs, random data
+5. **Test different combinations** - Measure file size and query performance
+6. **Consider query patterns** - Frequently filtered columns benefit from dictionary encoding
+
diff --git a/src/lib/parquet/src/Flow/Parquet/Option.php b/src/lib/parquet/src/Flow/Parquet/Option.php
index c47c33b0b..7b4652bf3 100644
--- a/src/lib/parquet/src/Flow/Parquet/Option.php
+++ b/src/lib/parquet/src/Flow/Parquet/Option.php
@@ -21,6 +21,22 @@ enum Option
      */
     case BYTE_ARRAY_TO_STRING;
 
+    /**
+     * Per-column encoding configuration using flat path notation.
+     * Accepts array<string, string> where key is column flat path and value is encoding name.
+     *
+     * Flat path examples:
+     * - Simple columns: 'column_name'
+     * - Nested structs: 'user.address.street'
+     * - List elements: 'items.list.element'
+     * - Map keys/values: 'metadata.key_value.key', 'metadata.key_value.value'
+     *
+     * Supported encodings: PLAIN, RLE_DICTIONARY, DELTA_BINARY_PACKED
+     *
+     * Default: null (use automatic encoding selection)
+     */
+    case COLUMNS_ENCODINGS;
+
     /**
      * Whenever cardinality ratio of the dictionary goes below this value, PagesBuilders is going to fallback to PLAIN encoding.
      * Cardinality ration is calculated as distinct values / total values.
diff --git a/src/lib/parquet/src/Flow/Parquet/Options.php b/src/lib/parquet/src/Flow/Parquet/Options.php
index 494e470c8..e62a11cd4 100644
--- a/src/lib/parquet/src/Flow/Parquet/Options.php
+++ b/src/lib/parquet/src/Flow/Parquet/Options.php
@@ -6,11 +6,13 @@
 
 use Flow\Filesystem\SizeUnits;
 use Flow\Parquet\Exception\InvalidArgumentException;
+use Flow\Parquet\Options\ColumnsEncodings;
+use Flow\Parquet\ParquetFile\Encodings;
 
 final class Options
 {
     /**
-     * @var array<string, bool|float|int>
+     * @var array<string, null|array<mixed>|bool|ColumnsEncodings|float|int>
      */
     private array $options;
 
@@ -33,6 +35,7 @@ public function __construct()
             Option::ZSTD_COMPRESSION_LEVEL->name => 3,
             Option::WRITER_VERSION->name => 1,
             Option::VALIDATE_DATA->name => true,
+            Option::COLUMNS_ENCODINGS->name => null,
         ];
     }
 
@@ -41,7 +44,10 @@ public static function default() : self
         return new self;
     }
 
-    public function get(Option $option) : bool|int|float
+    /**
+     * @return null|array<mixed>|bool|ColumnsEncodings|float|int
+     */
+    public function get(Option $option) : bool|int|float|array|ColumnsEncodings|null
     {
         return $this->options[$option->name];
     }
@@ -57,6 +63,21 @@ public function getBool(Option $option) : bool
         return $value;
     }
 
+    public function getColumnsEncodings() : ?ColumnsEncodings
+    {
+        $value = $this->options[Option::COLUMNS_ENCODINGS->name] ?? null;
+
+        if ($value === null) {
+            return null;
+        }
+
+        if ($value instanceof ColumnsEncodings) {
+            return $value;
+        }
+
+        throw new InvalidArgumentException('Option COLUMNS_ENCODINGS is not a ColumnsEncodings instance, but: ' . \gettype($value));
+    }
+
     public function getInt(Option $option) : int
     {
         $value = $this->options[$option->name];
@@ -68,9 +89,32 @@ public function getInt(Option $option) : int
         return $value;
     }
 
-    public function set(Option $option, bool|int|float $value) : self
+    public function has(Option $option) : bool
+    {
+        $value = $this->options[$option->name] ?? null;
+
+        return $value !== null;
+    }
+
+    /**
+     * @param null|array<mixed>|bool|ColumnsEncodings|float|int $value
+     */
+    public function set(Option $option, bool|int|float|array|ColumnsEncodings|null $value) : self
     {
-        $this->options[$option->name] = $value;
+        if ($option === Option::COLUMNS_ENCODINGS) {
+            if ($value === null) {
+                $this->options[$option->name] = null;
+            } elseif ($value instanceof ColumnsEncodings) {
+                $this->options[$option->name] = $value;
+            } elseif (\is_array($value)) {
+                /** @var array<string, Encodings|string> $value */
+                $this->options[$option->name] = ColumnsEncodings::fromArray($value);
+            } else {
+                throw new InvalidArgumentException('Option COLUMNS_ENCODINGS must be an array, ColumnsEncodings instance, or null, got: ' . \gettype($value));
+            }
+        } else {
+            $this->options[$option->name] = $value;
+        }
 
         return $this;
     }
diff --git a/src/lib/parquet/src/Flow/Parquet/Options/ColumnsEncodings.php b/src/lib/parquet/src/Flow/Parquet/Options/ColumnsEncodings.php
new file mode 100644
index 000000000..e778c0ea9
--- /dev/null
+++ b/src/lib/parquet/src/Flow/Parquet/Options/ColumnsEncodings.php
@@ -0,0 +1,126 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Flow\Parquet\Options;
+
+use Flow\Parquet\Exception\InvalidArgumentException;
+use Flow\Parquet\ParquetFile\Encodings;
+
+final class ColumnsEncodings
+{
+    /**
+     * @param array<string, Encodings> $encodings
+     */
+    private function __construct(private array $encodings)
+    {
+    }
+
+    /**
+     * @param array<string, Encodings> $flatPathToEncodingMap
+     */
+    public static function create(array $flatPathToEncodingMap) : self
+    {
+        $encodings = [];
+
+        foreach ($flatPathToEncodingMap as $flatPath => $encoding) {
+            if (!\is_string($flatPath)) {
+                throw new InvalidArgumentException('Column flat path must be a string, got: ' . \gettype($flatPath));
+            }
+
+            if ($flatPath === '') {
+                throw new InvalidArgumentException('Column flat path cannot be empty');
+            }
+
+            if (!$encoding instanceof Encodings) {
+                throw new InvalidArgumentException('Encoding must be an Encodings enum, got: ' . \gettype($encoding));
+            }
+
+            $encodings[$flatPath] = $encoding;
+        }
+
+        return new self($encodings);
+    }
+
+    /**
+     * @param array<string, Encodings|string> $columnEncodings
+     */
+    public static function fromArray(array $columnEncodings) : self
+    {
+        $encodings = [];
+
+        foreach ($columnEncodings as $flatPath => $encoding) {
+            if (!\is_string($flatPath)) {
+                throw new InvalidArgumentException('Column flat path must be a string, got: ' . \gettype($flatPath));
+            }
+
+            if ($flatPath === '') {
+                throw new InvalidArgumentException('Column flat path cannot be empty');
+            }
+
+            if ($encoding instanceof Encodings) {
+                $encodings[$flatPath] = $encoding;
+            } elseif (\is_string($encoding)) {
+                $encodings[$flatPath] = self::parseEncodingName($encoding);
+            } else {
+                throw new InvalidArgumentException('Encoding must be an Encodings enum or string, got: ' . \gettype($encoding));
+            }
+        }
+
+        return new self($encodings);
+    }
+
+    public function count() : int
+    {
+        return \count($this->encodings);
+    }
+
+    public function getEncodingForFlatPath(string $flatPath) : ?Encodings
+    {
+        return $this->encodings[$flatPath] ?? null;
+    }
+
+    /**
+     * @return array<string>
+     */
+    public function getFlatPaths() : array
+    {
+        return \array_keys($this->encodings);
+    }
+
+    public function hasFlatPath(string $flatPath) : bool
+    {
+        return \array_key_exists($flatPath, $this->encodings);
+    }
+
+    public function isEmpty() : bool
+    {
+        return empty($this->encodings);
+    }
+
+    /**
+     * @return array<string, string>
+     */
+    public function toArray() : array
+    {
+        $result = [];
+
+        foreach ($this->encodings as $flatPath => $encoding) {
+            $result[$flatPath] = $encoding->name;
+        }
+
+        return $result;
+    }
+
+    private static function parseEncodingName(string $encodingName) : Encodings
+    {
+        return match (\strtoupper(\trim($encodingName))) {
+            'PLAIN' => Encodings::PLAIN,
+            'RLE_DICTIONARY' => Encodings::RLE_DICTIONARY,
+            'DELTA_BINARY_PACKED' => Encodings::DELTA_BINARY_PACKED,
+            default => throw new InvalidArgumentException(
+                "Unsupported encoding: '{$encodingName}'. Supported encodings: PLAIN, RLE_DICTIONARY, DELTA_BINARY_PACKED"
+            ),
+        };
+    }
+}
diff --git a/src/lib/parquet/src/Flow/Parquet/Writer.php b/src/lib/parquet/src/Flow/Parquet/Writer.php
index 611899271..40d2cd695 100644
--- a/src/lib/parquet/src/Flow/Parquet/Writer.php
+++ b/src/lib/parquet/src/Flow/Parquet/Writer.php
@@ -4,6 +4,7 @@
 
 namespace Flow\Parquet;
 
+use function Flow\Types\DSL\type_integer;
 use Composer\InstalledVersions;
 use Flow\Filesystem\{DestinationStream, Path};
 use Flow\Filesystem\Stream\{NativeLocalDestinationStream};
@@ -177,7 +178,7 @@ public function writeBatch(iterable $rows) : void
     public function writeRow(array $row) : void
     {
         $this->rowGroupBuilder()->addRow($row);
-        $interval = (int) $this->options->get(Option::ROW_GROUP_SIZE_CHECK_INTERVAL);
+        $interval = type_integer()->assert($this->options->get(Option::ROW_GROUP_SIZE_CHECK_INTERVAL));
 
         if (($this->rowGroupBuilder()->rowsCount() % $interval === 0) && $this->rowGroupBuilder()->isFull()) {
             $rowGroupContainer = $this->rowGroupBuilder()->flush($this->fileOffset);
diff --git a/src/lib/parquet/src/Flow/Parquet/Writer/ColumnChunkBuilderFactory.php b/src/lib/parquet/src/Flow/Parquet/Writer/ColumnChunkBuilderFactory.php
new file mode 100644
index 000000000..9e4d3405f
--- /dev/null
+++ b/src/lib/parquet/src/Flow/Parquet/Writer/ColumnChunkBuilderFactory.php
@@ -0,0 +1,86 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Flow\Parquet\Writer;
+
+use Flow\Parquet\Exception\InvalidArgumentException;
+use Flow\Parquet\{Option, Options};
+use Flow\Parquet\ParquetFile\{Compressions, Encodings};
+use Flow\Parquet\ParquetFile\Schema\{FlatColumn, PhysicalType};
+use Flow\Parquet\Writer\ColumnChunkBuilder\{DeltaBinaryPackedColumnChunkBuilder, PlainFlatColumnChunkBuilder, RLEDictionaryChunkBuilder};
+
+final class ColumnChunkBuilderFactory
+{
+    public static function createBuilder(FlatColumn $column, Options $options, Compressions $compressions) : ColumnChunkBuilder
+    {
+        if ($options->has(Option::COLUMNS_ENCODINGS)) {
+            $columnsEncodings = $options->getColumnsEncodings();
+            $flatPath = $column->flatPath();
+
+            if ($columnsEncodings !== null && $columnsEncodings->hasFlatPath($flatPath)) {
+                $encoding = $columnsEncodings->getEncodingForFlatPath($flatPath);
+
+                if ($encoding !== null) {
+                    return self::createForEncoding($column, $encoding, $options, $compressions);
+                }
+            }
+        }
+
+        if (($column->type() === PhysicalType::INT32 || $column->type() === PhysicalType::INT64) && $options->getInt(Option::WRITER_VERSION) === 2) {
+            return new DeltaBinaryPackedColumnChunkBuilder($column, $options, $compressions);
+        }
+
+        return new PlainFlatColumnChunkBuilder($column, $options, $compressions);
+    }
+
+    private static function createForEncoding(FlatColumn $column, Encodings $encoding, Options $options, Compressions $compressions) : ColumnChunkBuilder
+    {
+        self::validateEncodingForColumn($column, $encoding);
+
+        return match ($encoding) {
+            Encodings::PLAIN => new PlainFlatColumnChunkBuilder($column, $options, $compressions),
+            Encodings::RLE_DICTIONARY => new RLEDictionaryChunkBuilder($column, $options, $compressions),
+            Encodings::DELTA_BINARY_PACKED => new DeltaBinaryPackedColumnChunkBuilder($column, $options, $compressions),
+            default => throw new InvalidArgumentException("Unsupported encoding for column builder: {$encoding->name}"),
+        };
+    }
+
+    private static function validateEncodingForColumn(FlatColumn $column, Encodings $encoding) : void
+    {
+        $columnType = $column->type();
+        $encodingName = $encoding->name;
+        $flatPath = $column->flatPath();
+
+        switch ($encoding) {
+            case Encodings::DELTA_BINARY_PACKED:
+                if ($columnType !== PhysicalType::INT32 && $columnType !== PhysicalType::INT64) {
+                    throw new InvalidArgumentException(
+                        'DELTA_BINARY_PACKED encoding is only supported for INT32 and INT64 columns. ' .
+                        "Column '{$flatPath}' has type: {$columnType->name}"
+                    );
+                }
+
+                break;
+
+            case Encodings::RLE_DICTIONARY:
+                if ($columnType === PhysicalType::FIXED_LEN_BYTE_ARRAY) {
+                    throw new InvalidArgumentException(
+                        'RLE_DICTIONARY encoding is not supported for FIXED_LEN_BYTE_ARRAY columns. ' .
+                        "Column '{$flatPath}' has type: {$columnType->name}"
+                    );
+                }
+
+                break;
+
+            case Encodings::PLAIN:
+                break;
+
+            default:
+                throw new InvalidArgumentException(
+                    "Encoding '{$encodingName}' is not implemented. " .
+                    'Supported encodings: PLAIN, RLE_DICTIONARY, DELTA_BINARY_PACKED'
+                );
+        }
+    }
+}
diff --git a/src/lib/parquet/src/Flow/Parquet/Writer/ColumnChunkBuilders.php b/src/lib/parquet/src/Flow/Parquet/Writer/ColumnChunkBuilders.php
index 8ce9c143a..4c3dbe883 100644
--- a/src/lib/parquet/src/Flow/Parquet/Writer/ColumnChunkBuilders.php
+++ b/src/lib/parquet/src/Flow/Parquet/Writer/ColumnChunkBuilders.php
@@ -4,14 +4,10 @@
 
 namespace Flow\Parquet\Writer;
 
-use Flow\Parquet\{Dremel\WriteColumnData,
-    Option,
-    Options,
-    Writer\ColumnChunkBuilder\DeltaBinaryPackedColumnChunkBuilder,
-    Writer\ColumnChunkBuilder\NestedColumnChunkBuilder,
-    Writer\ColumnChunkBuilder\PlainFlatColumnChunkBuilder};
+use Flow\Parquet\{Dremel\WriteColumnData, Options};
 use Flow\Parquet\ParquetFile\{Compressions, Schema};
-use Flow\Parquet\ParquetFile\Schema\{FlatColumn, NestedColumn, PhysicalType};
+use Flow\Parquet\ParquetFile\Schema\{FlatColumn, NestedColumn};
+use Flow\Parquet\Writer\{ColumnChunkBuilder\NestedColumnChunkBuilder};
 
 final class ColumnChunkBuilders
 {
@@ -32,13 +28,13 @@ public static function initialize(Schema $schema, Options $options, Compressions
                 $builders[$column->name()] = new NestedColumnChunkBuilder(
                     $column,
                     array_map(
-                        fn (FlatColumn $childColumn) => self::createFlatColumnBuilder($childColumn, $options, $compressions),
+                        fn (FlatColumn $childColumn) => ColumnChunkBuilderFactory::createBuilder($childColumn, $options, $compressions),
                         $column->childrenFlat()
                     )
                 );
             } else {
                 /** @var FlatColumn $column */
-                $builders[$column->name()] = self::createFlatColumnBuilder($column, $options, $compressions);
+                $builders[$column->name()] = ColumnChunkBuilderFactory::createBuilder($column, $options, $compressions);
             }
         }
 
@@ -102,13 +98,4 @@ public function uncompressedSize() : int
 
         return $size;
     }
-
-    private static function createFlatColumnBuilder(FlatColumn $column, Options $options, Compressions $compressions) : ColumnChunkBuilder
-    {
-        if (($column->type() === PhysicalType::INT32 || $column->type() === PhysicalType::INT64) && $options->getInt(Option::WRITER_VERSION) === 2) {
-            return new DeltaBinaryPackedColumnChunkBuilder($column, $options, $compressions);
-        }
-
-        return new PlainFlatColumnChunkBuilder($column, $options, $compressions);
-    }
 }