| ################################################################################ |
| # 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. |
| ################################################################################ |
| |
| """Per-field aggregator abstraction used by the ``aggregation`` merge |
| engine. |
| |
| Each non-PK field is reduced across rows sharing the same primary key |
| by one ``FieldAggregator`` instance picked per field. The |
| ``AggregateMergeFunction`` drives the lifecycle: ``reset()`` at the |
| start of each key group, ``agg()`` per input value, and the final |
| accumulator is read out to build the merged row. |
| """ |
| |
| from abc import ABC, abstractmethod |
| from typing import Any |
| |
| from pypaimon.schema.data_types import DataType |
| |
| |
| class FieldAggregator(ABC): |
| """Per-field aggregator base class. |
| |
| Concrete subclasses implement :meth:`agg` and may override |
| :meth:`reset`. :meth:`retract` is intentionally left as the default |
| "refuse" implementation: pypaimon's ``AggregateMergeFunction`` |
| rejects ``DELETE`` / ``UPDATE_BEFORE`` rows up-front, so no |
| aggregator's ``retract`` is reachable from the read path. The hook |
| is kept so a future PR can add retract semantics without changing |
| every subclass. |
| """ |
| |
| def __init__(self, name: str, field_type: DataType): |
| self.name = name |
| self.field_type = field_type |
| |
| @abstractmethod |
| def agg(self, accumulator: Any, input_field: Any) -> Any: |
| """Combine ``accumulator`` with ``input_field`` and return the |
| new accumulator. Called once per row in the key group, in |
| arrival order (sequence-number ascending). ``accumulator`` is |
| ``None`` before the first add. |
| """ |
| |
| def reset(self) -> None: |
| """Reset internal state at the start of a new key group. |
| |
| Default is a no-op. Aggregators that carry per-group bookkeeping |
| beyond the externally-passed accumulator (e.g. ``first_value``'s |
| "have we seen any row yet?" flag) must override this. |
| """ |
| |
| def retract(self, accumulator: Any, retract_field: Any) -> Any: |
| """Refuse the retract operation by default. |
| |
| ``AggregateMergeFunction`` rejects retract rows at :meth:`add` |
| time, so this path is currently unreachable from the read |
| pipeline. The hook is kept for forward-compatibility: a future |
| PR that wires retract through the merge function can override |
| this on the aggregators that actually support it (sum, product, |
| last_value, ...). |
| """ |
| raise NotImplementedError( |
| "Aggregator '{}' does not support retract; the aggregation " |
| "merge engine does not implement DELETE / UPDATE_BEFORE " |
| "handling.".format(self.name) |
| ) |