```py
## writing ArrowStream file from python
import pyarrow as pa
data = [
    pa.array([1, 2, 3, 4, 5]),
    pa.array(["one", "two", "three", "four", "five"]).dictionary_encode(),
    pa.array([1, 2, 3, 4, 5]).dictionary_encode(),
    pa.array([True, False, True, True, True])
]
batch = pa.record_batch(data, names=['id', 'lc_nullable', 'lc_int_nullable', 'bool_nullable'])
writer = pa.ipc.new_stream("test4.arrows", batch.schema)
writer.write_batch(batch)
writer.close()
```

```
clickhouse-local --query='SELECT * FROM table FORMAT TSVWithNamesAndTypes' --stacktrace --input-format=ArrowStream < test4.arrows
id	lc_nullable	lc_int_nullable	bool_nullable
Nullable(Int64)	LowCardinality(Nullable(String))	LowCardinality(Nullable(Int64))	Nullable(UInt8)
1		0	1
2	one	1	0
3	two	2	1
4	three	3	1
5	four	4	1
```

0 in LowCardianality dictionary in ClickHouse have special meaning (null/default), while in Arrow it's a regular values.

Probably remapping needed... :|
std::shared_ptr<arrow::ChunkedArray> & arrow_column,
    const std::string & column_name,
    const std::string & format_name,
    bool is_nullable,
    std::unordered_map<String, std::shared_ptr<ColumnWithTypeAndName>> & dictionary_values,
    bool read_ints_as_dates,
    bool allow_null_type,
    bool skip_columns_with_unsupported_types,
    bool & skipped)
{
    if (!is_nullable && arrow_column->null_count() && arrow_column->type()->id() != arrow::Type::LIST
        && arrow_column->type()->id() != arrow::Type::MAP && arrow_column->type()->id() != arrow::Type::STRUCT)
    {
        auto nested_column = readColumnFromArrowColumn(arrow_column, column_name, format_name, true, dictionary_values, read_ints_as_dates, allow_null_type, skip_columns_with_unsupported_types, skipped);
        if (skipped)
            return {};
        auto nullmap_column = readByteMapFromArrowColumn(arrow_column);
        auto nullable_type = std::make_shared<DataTypeNullable>(std::move(nested_column.type));
        auto nullable_column = ColumnNullable::create(nested_column.column, nullmap_column);
        return {std::move(nullable_column), std::move(nullable_type), column_name};
    }
    switch (arrow_column->type()->id())
    {
        case arrow::Type::STRING:
        case arrow::Type::BINARY:
            
            return readColumnWithStringData(arrow_column, column_name);