blob: ff15365d60043fd3f5203e5d71916f4a5c88de34 [file]
# 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.
"""Physical format output owned by the selected Python execution backend.
FileIO supplies streams and locations; it does not construct table codecs.
"""
import os
import pyarrow as pa
from pypaimon.execution.python.write.blob_format_writer import BlobFormatWriter
from pypaimon.execution.python.write.writer.format_row_writer import FormatRowWriter
from pypaimon.filesystem.vortex_utils import to_vortex_specified
from pypaimon.schema.arrow_schema import parquet_storage_schema
from pypaimon.schema.data_types import AtomicType, DataField, PyarrowFieldParser
def parquet_write_schema(schema):
"""Keep vector dimensions in Paimon rather than Parquet's Arrow metadata.
PyArrow cannot restore nullable fixed-size lists from Parquet reliably.
Use physical variable-size lists, including for streaming writers whose
later batches may contain NULLs. Preserve unrelated field/schema metadata.
"""
return parquet_storage_schema(schema)
def parquet_write_data(data, schema=None):
"""Convert vectors to their Parquet representation without dropping metadata."""
schema = parquet_write_schema(data.schema) if schema is None else schema
return data if data.schema.equals(schema, check_metadata=True) else data.cast(schema)
def write_row_file(file_io, path, data, fields=None, zstd_level=1):
try:
if fields is None:
fields = PyarrowFieldParser.to_paimon_schema(data.schema)
with file_io.new_output_stream(path) as output_stream:
writer = FormatRowWriter(output_stream, fields, zstd_level=zstd_level)
writer.write_table(data)
writer.close()
except Exception as error:
file_io.delete_quietly(path)
raise RuntimeError("Failed to write row file {}: {}".format(path, error)) from error
def write_blob_file(file_io, path, data):
try:
if data.num_columns != 1:
raise RuntimeError(
"Blob format only supports a single column, got {} columns".format(data.num_columns))
field = data.schema[0]
if pa.types.is_large_binary(field.type):
field_type = AtomicType("BLOB")
else:
field_type = PyarrowFieldParser.to_paimon_type(field.type, field.nullable)
fields = [DataField(0, field.name, field_type)]
values = data.column(0).to_pylist()
uri_reader_factory = file_io.uri_reader_factory
with file_io.new_output_stream(path) as output_stream:
writer = BlobFormatWriter(output_stream)
for value in values:
writer.write_value(value, fields, uri_reader_factory)
writer.close()
except Exception as error:
file_io.delete_quietly(path)
raise RuntimeError("Failed to write blob file {}: {}".format(path, error)) from error
def write_vortex_file(file_io, path, data):
try:
import vortex
file_path, store_kwargs = to_vortex_specified(file_io, path)
scheme, _, _ = file_io.parse_location(path)
if scheme in {'file', None} or not scheme:
os.makedirs(os.path.dirname(file_path), exist_ok=True)
if store_kwargs:
from vortex import store
store.from_url(file_path, **store_kwargs).write(vortex.array(data))
else:
from vortex._lib.io import write
write(vortex.array(data), file_path)
except Exception as error:
file_io.delete_quietly(path)
raise RuntimeError("Failed to write Vortex file {}: {}".format(path, error)) from error