blob: 378cbfbdc08fc179e34d2576e8886b3d9885e761 [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.
"""Authorization-based pruning of the Python file scanner."""
import json as _json
from pypaimon.common.predicate import Predicate
from pypaimon.common.predicate_builder import PredicateBuilder
from pypaimon.read.push_down_utils import exclude_predicate_with_fields, predicate_field_names
def prune_scanner_by_auth(table, scanner, auth_result):
masked = set(auth_result.column_masking or {}) if auth_result is not None else set()
predicate = scanner._user_predicate
effective = exclude_predicate_with_fields(predicate, masked)
if effective != scanner.predicate:
partition_key_predicate = scanner.partition_key_predicate
scanner._set_predicate(effective)
if scanner._explicit_partition_filter:
scanner.partition_key_predicate = partition_key_predicate
scanner.auth_partition_predicate = None
scanner.auth_has_non_partition_filter = bool(
predicate is not None and masked & predicate_field_names(predicate))
if auth_result is None or not auth_result.filter:
return
partition_preds, has_non_partition = __split_auth_filter(table, auth_result)
if partition_preds:
combined = PredicateBuilder.and_predicates(partition_preds)
scanner.auth_partition_predicate = combined
scanner.auth_has_non_partition_filter |= has_non_partition
def __split_auth_filter(table, auth_result):
partition_keys = list(table.partition_keys or [])
if not partition_keys:
return [], bool(auth_result.filter)
partition_preds = []
has_non_partition = False
partition_key_set = set(partition_keys)
partition_index_map = {name: i for i, name in enumerate(partition_keys)}
for json_str in (auth_result.filter or []):
pred = __try_parse_partition_predicate(table, json_str, partition_key_set, partition_index_map)
if pred is not None:
partition_preds.append(pred)
else:
has_non_partition = True
return partition_preds, has_non_partition
def __try_parse_partition_predicate(table, json_str, partition_keys, partition_index_map):
data = _json.loads(json_str)
if data is None or data.get("kind") != "LEAF":
return None
transform = data.get("transform", {})
if transform.get("name") != "FIELD_REF":
return None
field_name = transform.get("fieldRef", {}).get("name")
if field_name is None or field_name not in partition_keys:
return None
field_index = partition_index_map.get(field_name)
if field_index is None:
return None
partition_field_type = None
for f in table.fields:
if f.name == field_name:
partition_field_type = getattr(f.type, 'type', '')
break
base_type = partition_field_type.split('(')[0] if partition_field_type else ''
safe_types = {'INT', 'BIGINT', 'SMALLINT', 'TINYINT', 'STRING', 'VARCHAR', 'CHAR'}
if base_type not in safe_types:
return None
function = data.get("function", "")
literals = data.get("literals", [])
method_map = {
"EQUAL": "equal", "NOT_EQUAL": "notEqual",
"LESS_THAN": "lessThan", "LESS_OR_EQUAL": "lessOrEqual",
"GREATER_THAN": "greaterThan", "GREATER_OR_EQUAL": "greaterOrEqual",
"IS_NULL": "isNull", "IS_NOT_NULL": "isNotNull",
"IN": "in", "NOT_IN": "notIn",
}
method = method_map.get(function)
if method is None:
return None
return Predicate(method=method, index=field_index, field=field_name, literals=literals)