blob: 150b6af5814e331683760cecf25a36cfdb27e0bd [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.
from collections.abc import Generator
from tempfile import TemporaryDirectory
from typing import (
Any,
)
import pytest
from pydantic_core import to_json
from pyiceberg.partitioning import PartitionField, PartitionSpec
from pyiceberg.schema import Schema
from pyiceberg.transforms import DayTransform, IdentityTransform
from pyiceberg.types import (
IntegerType,
NestedField,
TimestampType,
)
from pyiceberg.utils.config import Config
@pytest.fixture(scope="session", autouse=True)
def isolate_pyiceberg_config() -> Generator[None, None, None]:
monkeypatch = pytest.MonkeyPatch()
with TemporaryDirectory() as empty_home_dir:
monkeypatch.setenv("HOME", empty_home_dir)
monkeypatch.setenv("PYICEBERG_HOME", empty_home_dir)
from pyiceberg import catalog
monkeypatch.setattr(catalog, "_ENV_CONFIG", Config())
yield
monkeypatch.undo()
@pytest.fixture(scope="session")
def avro_schema_manifest_entry() -> dict[str, Any]:
return {
"type": "record",
"name": "manifest_entry",
"fields": [
{"name": "status", "type": "int", "field-id": 0},
{
"name": "snapshot_id",
"type": ["null", "long"],
"default": None,
"field-id": 1,
},
{
"name": "data_file",
"type": {
"type": "record",
"name": "r2",
"fields": [
{
"name": "file_path",
"type": "string",
"doc": "Location URI with FS scheme",
"field-id": 100,
},
{
"name": "file_format",
"type": "string",
"doc": "File format name: avro, orc, or parquet",
"field-id": 101,
},
{
"name": "partition",
"type": {
"type": "record",
"name": "r102",
"fields": [
{
"field-id": 1000,
"default": None,
"name": "VendorID",
"type": ["null", "int"],
},
{
"field-id": 1001,
"default": None,
"name": "tpep_pickup_datetime",
"type": [
"null",
{"type": "int", "logicalType": "date"},
],
},
],
},
"field-id": 102,
},
{
"name": "record_count",
"type": "long",
"doc": "Number of records in the file",
"field-id": 103,
},
{
"name": "file_size_in_bytes",
"type": "long",
"doc": "Total file size in bytes",
"field-id": 104,
},
{
"name": "block_size_in_bytes",
"type": "long",
"field-id": 105,
},
{
"name": "column_sizes",
"type": [
"null",
{
"type": "array",
"items": {
"type": "record",
"name": "k117_v118",
"fields": [
{
"name": "key",
"type": "int",
"field-id": 117,
},
{
"name": "value",
"type": "long",
"field-id": 118,
},
],
},
"logicalType": "map",
},
],
"doc": "Map of column id to total size on disk",
"default": None,
"field-id": 108,
},
{
"name": "value_counts",
"type": [
"null",
{
"type": "array",
"items": {
"type": "record",
"name": "k119_v120",
"fields": [
{
"name": "key",
"type": "int",
"field-id": 119,
},
{
"name": "value",
"type": "long",
"field-id": 120,
},
],
},
"logicalType": "map",
},
],
"doc": "Map of column id to total count, including null and NaN",
"default": None,
"field-id": 109,
},
{
"name": "null_value_counts",
"type": [
"null",
{
"type": "array",
"items": {
"type": "record",
"name": "k121_v122",
"fields": [
{
"name": "key",
"type": "int",
"field-id": 121,
},
{
"name": "value",
"type": "long",
"field-id": 122,
},
],
},
"logicalType": "map",
},
],
"doc": "Map of column id to null value count",
"default": None,
"field-id": 110,
},
{
"name": "nan_value_counts",
"type": [
"null",
{
"type": "array",
"items": {
"type": "record",
"name": "k138_v139",
"fields": [
{
"name": "key",
"type": "int",
"field-id": 138,
},
{
"name": "value",
"type": "long",
"field-id": 139,
},
],
},
"logicalType": "map",
},
],
"doc": "Map of column id to number of NaN values in the column",
"default": None,
"field-id": 137,
},
{
"name": "lower_bounds",
"type": [
"null",
{
"type": "array",
"items": {
"type": "record",
"name": "k126_v127",
"fields": [
{
"name": "key",
"type": "int",
"field-id": 126,
},
{
"name": "value",
"type": "bytes",
"field-id": 127,
},
],
},
"logicalType": "map",
},
],
"doc": "Map of column id to lower bound",
"default": None,
"field-id": 125,
},
{
"name": "upper_bounds",
"type": [
"null",
{
"type": "array",
"items": {
"type": "record",
"name": "k129_v130",
"fields": [
{
"name": "key",
"type": "int",
"field-id": 129,
},
{
"name": "value",
"type": "bytes",
"field-id": 130,
},
],
},
"logicalType": "map",
},
],
"doc": "Map of column id to upper bound",
"default": None,
"field-id": 128,
},
{
"name": "key_metadata",
"type": ["null", "bytes"],
"doc": "Encryption key metadata blob",
"default": None,
"field-id": 131,
},
{
"name": "split_offsets",
"type": [
"null",
{"type": "array", "items": "long", "element-id": 133},
],
"doc": "Splittable offsets",
"default": None,
"field-id": 132,
},
{
"name": "sort_order_id",
"type": ["null", "int"],
"doc": "Sort order ID",
"default": None,
"field-id": 140,
},
],
},
"field-id": 2,
},
],
}
manifest_entry_records = [
{
"status": 1,
"snapshot_id": 8744736658442914487,
"data_file": {
"file_path": "/home/iceberg/warehouse/nyc/taxis_partitioned/data/VendorID=null/00000-633-d8a4223e-dc97-45a1-86e1-adaba6e8abd7-00001.parquet",
"file_format": "PARQUET",
"partition": {"VendorID": 1, "tpep_pickup_datetime": 1925},
"record_count": 19513,
"file_size_in_bytes": 388872,
"block_size_in_bytes": 67108864,
"column_sizes": [
{"key": 1, "value": 53},
{"key": 2, "value": 98153},
],
"value_counts": [
{"key": 1, "value": 19513},
{"key": 2, "value": 19513},
],
"null_value_counts": [
{"key": 1, "value": 19513},
{"key": 2, "value": 0},
],
"nan_value_counts": [],
"lower_bounds": [
{"key": 1, "value": b"\x00\x00\x00\x00"},
{"key": 2, "value": b"\x00\x00\x00\x00\x00\x00\x00\x00"},
],
"upper_bounds": [
{"key": 1, "value": b"\x00\x00\xe0\xbf"},
{"key": 2, "value": b"\x00\x00\x00\x00\x00\x00\xe0\xbf"},
],
"key_metadata": None,
"split_offsets": [4],
"sort_order_id": 0,
},
},
{
"status": 1,
"snapshot_id": 8744736658442914487,
"data_file": {
"file_path": "/home/iceberg/warehouse/nyc/taxis_partitioned/data/VendorID=1/00000-633-d8a4223e-dc97-45a1-86e1-adaba6e8abd7-00002.parquet",
"file_format": "PARQUET",
"partition": {"VendorID": 1, "tpep_pickup_datetime": None},
"record_count": 95050,
"file_size_in_bytes": 1265950,
"block_size_in_bytes": 67108864,
"column_sizes": [
{"key": 1, "value": 318},
{"key": 2, "value": 329806},
],
"value_counts": [
{"key": 1, "value": 95050},
{"key": 2, "value": 95050},
],
"null_value_counts": [
{"key": 1, "value": 0},
{"key": 2, "value": 0},
],
"nan_value_counts": [],
"lower_bounds": [
{"key": 1, "value": b"\x00\x00\x00\x00"},
{"key": 2, "value": b"\x00\x00\x00\x00\x00\x00\x00\x00"},
],
"upper_bounds": [
{"key": 1, "value": b"\x00\x00\xe0\xbf"},
{"key": 2, "value": b"\x00\x00\x00\x00\x00\x00\xe0\xbf"},
],
"key_metadata": None,
"split_offsets": [4],
"sort_order_id": 0,
},
},
]
@pytest.fixture(scope="session")
def test_schema() -> Schema:
return Schema(
NestedField(1, "VendorID", IntegerType(), False),
NestedField(2, "tpep_pickup_datetime", TimestampType(), False),
)
@pytest.fixture(scope="session")
def test_partition_spec() -> Schema:
return PartitionSpec(
PartitionField(1, 1000, IdentityTransform(), "VendorID"),
PartitionField(2, 1001, DayTransform(), "tpep_pickup_day"),
)
@pytest.fixture(scope="session")
def generated_manifest_entry_file(
avro_schema_manifest_entry: dict[str, Any],
test_schema: Schema,
test_partition_spec: PartitionSpec,
) -> Generator[str, None, None]:
from fastavro import parse_schema, writer
parsed_schema = parse_schema(avro_schema_manifest_entry)
with TemporaryDirectory() as tmpdir:
tmp_avro_file = tmpdir + "/manifest.avro"
with open(tmp_avro_file, "wb") as out:
writer(
out,
parsed_schema,
manifest_entry_records,
metadata={
"schema": test_schema.model_dump_json(),
"partition-spec": to_json(test_partition_spec.fields).decode(
"utf-8"
),
},
)
yield tmp_avro_file