mirror of
https://github.com/apache/superset.git
synced 2026-07-12 09:45:43 +00:00
Co-authored-by: Mike Bridge <michael.bridge@ext.preset.io> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
150 lines
5.8 KiB
Python
150 lines
5.8 KiB
Python
# 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.
|
|
"""Parent baseline insertion + child-handler dispatch.
|
|
|
|
Two complementary helpers:
|
|
|
|
* :func:`insert_baseline_and_children` — top-level glue called by
|
|
the listener. Wraps the work in ``session.no_autoflush`` (so
|
|
``session.connection()`` doesn't trigger a flush of Continuum's
|
|
pending Transaction object before our direct-SQL insert claims its
|
|
tx_id) and logs any failures as listener-boundary errors.
|
|
* :func:`_insert_baseline_row` — actually writes the
|
|
``version_transaction`` row and the parent shadow row. Returns the
|
|
allocated ``transaction_id``.
|
|
* :func:`_baseline_children_for_parent` — dispatches to the per-
|
|
entity handler in :mod:`.children` under the same tx_id.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.orm import Session
|
|
|
|
from superset.versioning.baseline.children import CHILD_BASELINE_HANDLERS
|
|
from superset.versioning.baseline.shadow import insert_baseline_shadow_row
|
|
from superset.versioning.utils import read_row_outside_flush
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def insert_baseline_and_children(
|
|
session: Session, obj: Any, version_table: Any
|
|
) -> None:
|
|
"""Insert the parent baseline row, then baseline the parent's child
|
|
collections under the same transaction id.
|
|
|
|
Wrapped in ``no_autoflush`` so ``session.connection()`` inside
|
|
``_insert_baseline_row`` does not trigger a flush of Continuum's
|
|
pending Transaction object before our direct-SQL insert claims its
|
|
tx_id.
|
|
"""
|
|
try:
|
|
with session.no_autoflush:
|
|
tx_id = _insert_baseline_row(session, obj, version_table)
|
|
if tx_id is None:
|
|
return
|
|
_baseline_children_for_parent(session, obj, tx_id)
|
|
logger.debug(
|
|
"baseline_listener: inserted baseline tx_id=%s for %s id=%s",
|
|
tx_id,
|
|
type(obj).__name__,
|
|
getattr(obj, "id", None),
|
|
)
|
|
except Exception: # pylint: disable=broad-except
|
|
logger.exception(
|
|
"baseline_listener: failed to insert baseline for %s id=%s",
|
|
type(obj).__name__,
|
|
getattr(obj, "id", None),
|
|
)
|
|
|
|
|
|
def _insert_baseline_row(
|
|
session: Session, obj: Any, version_table: sa.Table
|
|
) -> int | None:
|
|
"""Insert a synthetic baseline row capturing the pre-edit DB state of *obj*.
|
|
|
|
Creates a version_transaction entry and an operation_type=0 version row.
|
|
All writes use the session's existing connection so they share the same
|
|
database transaction as the triggering flush.
|
|
|
|
Returns the allocated ``transaction_id`` so the caller can baseline child
|
|
collections under the same tx (see
|
|
:func:`~superset.versioning.baseline.children._insert_child_baseline_rows`),
|
|
or ``None`` when the entity has no live row.
|
|
"""
|
|
# pylint: disable=import-outside-toplevel
|
|
from sqlalchemy_continuum import versioning_manager
|
|
|
|
main_table = type(obj).__table__
|
|
row = read_row_outside_flush(session, main_table, obj.id)
|
|
if row is None:
|
|
return None
|
|
|
|
conn = session.connection()
|
|
|
|
# Insert a version_transaction row for the baseline.
|
|
#
|
|
# ``issued_at`` and ``user_id`` are sourced from the entity's audit fields
|
|
# (``changed_on`` / ``changed_by_fk``, falling back to ``created_on`` /
|
|
# ``created_by_fk`` if the row was never edited), so the baseline reads
|
|
# in the version-history UI as "this is the state at the time of the
|
|
# last pre-versioning edit, by that user." Using ``now()`` and the
|
|
# current user would have made the baseline look chronologically newer
|
|
# than subsequent edits and attributed historical content to the user
|
|
# who happened to trigger the first save under versioning.
|
|
baseline_issued_at = row.get("changed_on") or row.get("created_on") or sa.func.now()
|
|
baseline_user_id = row.get("changed_by_fk") or row.get("created_by_fk")
|
|
tx_table = versioning_manager.transaction_cls.__table__
|
|
result = conn.execute(
|
|
tx_table.insert().values(
|
|
issued_at=baseline_issued_at,
|
|
user_id=baseline_user_id,
|
|
remote_addr=None,
|
|
)
|
|
)
|
|
tx_id = result.inserted_primary_key[0]
|
|
insert_baseline_shadow_row(conn, version_table, row, tx_id)
|
|
return tx_id
|
|
|
|
|
|
def _baseline_children_for_parent(
|
|
session: Session, parent_obj: Any, tx_id: int
|
|
) -> None:
|
|
"""Baseline a parent's child collections under the parent's baseline tx.
|
|
|
|
Dispatches via the
|
|
:data:`~superset.versioning.baseline.children.CHILD_BASELINE_HANDLERS`
|
|
table to per-entity handlers. A handler failure is logged but does
|
|
not block the parent baseline.
|
|
"""
|
|
parent_name = type(parent_obj).__name__
|
|
handler = CHILD_BASELINE_HANDLERS.get(parent_name)
|
|
if handler is None:
|
|
return
|
|
try:
|
|
handler(session, parent_obj, tx_id)
|
|
except Exception: # pylint: disable=broad-except
|
|
logger.exception(
|
|
"baseline_listener: failed to baseline children of %s id=%s",
|
|
parent_name,
|
|
getattr(parent_obj, "id", None),
|
|
)
|