| # 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 typing import List | |
| from ..schema.data_types import AtomicType, DataField, SYSTEM_FIELD_ID_START | |
| class SpecialFields: | |
| """ | |
| Special fields in a RowType with specific field ids. | |
| """ | |
| SEQUENCE_NUMBER = DataField(2147483646, "_SEQUENCE_NUMBER", AtomicType("BIGINT", nullable=False)) | |
| VALUE_KIND = DataField(2147483645, "_VALUE_KIND", AtomicType("TINYINT", nullable=False)) | |
| ROW_ID = DataField(2147483642, "_ROW_ID", AtomicType("BIGINT", nullable=False)) | |
| SYSTEM_FIELD_NAMES = { | |
| '_SEQUENCE_NUMBER', | |
| '_VALUE_KIND', | |
| '_ROW_ID' | |
| } | |
| SYSTEM_FIELD_ID_START = SYSTEM_FIELD_ID_START | |
| @staticmethod | |
| def is_system_field(field_name: str) -> bool: | |
| """Check if a field is a system field.""" | |
| return field_name in SpecialFields.SYSTEM_FIELD_NAMES | |
| @staticmethod | |
| def is_system_field_id(field_id: int) -> bool: | |
| """Check if a field id is reserved for system fields.""" | |
| return field_id >= SYSTEM_FIELD_ID_START | |
| @staticmethod | |
| def find_system_fields(read_fields: List[DataField]) -> dict: | |
| """Find system fields in read fields and return a mapping of field name to index.""" | |
| system_fields = {} | |
| for i, field in enumerate(read_fields): | |
| if SpecialFields.is_system_field(field.name): | |
| system_fields[field.name] = i | |
| return system_fields | |
| @staticmethod | |
| def row_type_with_row_tracking(table_fields: List[DataField], | |
| sequence_number_nullable: bool = False) -> List[DataField]: | |
| """ | |
| Add row tracking fields. | |
| Args: | |
| table_fields: The original table fields | |
| sequence_number_nullable: Whether sequence number should be nullable | |
| """ | |
| fields_with_row_tracking = list(table_fields) | |
| for field in fields_with_row_tracking: | |
| if (SpecialFields.ROW_ID.name == field.name | |
| or SpecialFields.SEQUENCE_NUMBER.name == field.name): | |
| raise ValueError( | |
| f"Row tracking field name '{field.name}' conflicts with existing field names." | |
| ) | |
| fields_with_row_tracking.append(SpecialFields.ROW_ID) | |
| if sequence_number_nullable: | |
| seq_num_field = DataField( | |
| id=SpecialFields.SEQUENCE_NUMBER.id, | |
| name=SpecialFields.SEQUENCE_NUMBER.name, | |
| type=AtomicType("BIGINT", nullable=True) # Make it nullable | |
| ) | |
| fields_with_row_tracking.append(seq_num_field) | |
| else: | |
| fields_with_row_tracking.append(SpecialFields.SEQUENCE_NUMBER) | |
| return fields_with_row_tracking | |
| @staticmethod | |
| def row_type_with_row_id(table_fields: List[DataField]) -> List[DataField]: | |
| """Add ROW_ID field to the given fields list. | |
| Args: | |
| table_fields: The original table fields | |
| """ | |
| fields_with_row_id = list(table_fields) | |
| for field in fields_with_row_id: | |
| if SpecialFields.ROW_ID.name == field.name: | |
| raise ValueError( | |
| "Row tracking field name '{}' conflicts with existing field names." | |
| .format(field.name) | |
| ) | |
| fields_with_row_id.append(SpecialFields.ROW_ID) | |
| return fields_with_row_id |