| # 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. | |
| from collections.abc import Mapping | |
| from typing import Any, Dict, Iterable, List | |
| import pyarrow as pa | |
| from pypaimon.schema.data_types import DataField, PyarrowFieldParser, is_blob_file_field | |
| from pypaimon.table.row.blob import Blob, BlobView | |
| from pypaimon.table.row.internal_row import InternalRow | |
| from pypaimon.table.row.vector import Vector | |
| def row_to_named_values( | |
| row: InternalRow, table_fields: List[DataField]) -> Dict[str, Any]: | |
| if hasattr(row, 'fields') and getattr(row, 'fields') is not None: | |
| row_fields = getattr(row, 'fields') | |
| return { | |
| field.name: row.get_field(i) | |
| for i, field in enumerate(row_fields) | |
| } | |
| if len(row) != len(table_fields): | |
| raise ValueError( | |
| "Rows without field metadata must have the same arity as the " | |
| f"table schema: {len(row)} != {len(table_fields)}." | |
| ) | |
| return { | |
| field.name: row.get_field(i) | |
| for i, field in enumerate(table_fields) | |
| } | |
| def require_columns( | |
| values_by_name: Dict[str, Any], | |
| column_names: Iterable[str], | |
| context: str) -> None: | |
| missing = [ | |
| column_name | |
| for column_name in column_names | |
| if column_name not in values_by_name | |
| ] | |
| if missing: | |
| raise ValueError( | |
| f"{context} requires row field(s) {missing}, " | |
| f"but row only contains {list(values_by_name.keys())}." | |
| ) | |
| def inline_blob_value(value: Any, descriptor: bool, view: bool) -> Any: | |
| """Serialize row objects to inline references without opening payloads.""" | |
| if descriptor and isinstance(value, Blob): | |
| try: | |
| return value.to_descriptor().serialize() | |
| except Exception as error: | |
| raise ValueError( | |
| "blob-descriptor-field row values must be serialized " | |
| "BlobDescriptor bytes or a Blob with a descriptor." | |
| ) from error | |
| if view and isinstance(value, BlobView): | |
| return value.view_struct.serialize() | |
| return value | |
| def value_for_arrow(value: Any, field: DataField) -> Any: | |
| if isinstance(value, Vector): | |
| return value.to_list() | |
| if isinstance(value, Blob) or ( | |
| is_blob_file_field(field) | |
| and _contains_blob_value(value) | |
| ): | |
| raise ValueError( | |
| "Blob values cannot be converted to Arrow without materializing " | |
| "the stream. Use a Row-aware blob write path." | |
| ) | |
| return value | |
| def _contains_blob_value(value: Any) -> bool: | |
| if isinstance(value, Blob): | |
| return True | |
| if isinstance(value, Mapping): | |
| return any(_contains_blob_value(item) for item in value.values()) | |
| if isinstance(value, (list, tuple)): | |
| return any(_contains_blob_value(item) for item in value) | |
| return False | |
| def row_values_to_arrow_table( | |
| values_by_name: Dict[str, Any], | |
| fields: List[DataField], | |
| column_names: List[str]) -> pa.Table: | |
| field_by_name = {field.name: field for field in fields} | |
| selected_fields = [field_by_name[name] for name in column_names] | |
| schema = PyarrowFieldParser.from_paimon_schema(selected_fields) | |
| data = { | |
| name: [value_for_arrow(values_by_name[name], field_by_name[name])] | |
| for name in column_names | |
| } | |
| return pa.Table.from_pydict(data, schema=schema) |