fix(versioning): flag `description instead of uuid` in force-parent-dirty

flag_modified(parent, "uuid") was producing FK integrity failures via
the column's BLOB/BINARY round-trip: SQLAlchemy logs the param as
``<memory at 0x…>`` and the UUID round-trip doesn't always match the
in-memory value byte-for-byte. Symptom: in scenarios where the parent
is already going to flush (Reverter applying historical state during
restore, RLS test triggering autoflush during a query), our added
``uuid`` UPDATE column tripped the FK check.

Pick ``description`` instead — plain Text column on all three
versioned parent classes (Dashboard, Slice, SqlaTable), no
TypeDecorator, no marshaling layer. Flagging it round-trips its
current value safely. Fallback chain ``description → uuid → col_keys[0]``
keeps the original deterministic-pick property for forks/subclasses
that excluded ``description``.

Should unblock test_restore_applies_scalar_field and the
test_rls_filter_alters_no_role_user_birth_names_query autoflush
error.
This commit is contained in:
Mike Bridge
2026-05-21 09:33:12 -06:00
parent 2731b099f1
commit 9ff18b3e48

View File

@@ -134,22 +134,34 @@ def _force_parent_dirty_on_child_change(session: Session) -> None:
col_keys = [prop.key for prop in versioned_column_properties(parent)]
if not col_keys:
continue
# ``uuid`` is on all three versioned parent classes (Dashboard,
# Slice, SqlaTable) and is in none of their ``__versioned__``
# excludes — pick it deterministically so the flagged attribute
# is stable across SQLAlchemy versions / mapper-configuration
# orders. Falls back to the first available column for forks or
# subclasses that excluded ``uuid``.
flag_col = "uuid" if "uuid" in col_keys else col_keys[0]
# ``description`` is a plain ``Text`` column on all three versioned
# parent classes (Dashboard, Slice, SqlaTable) and is in none of
# their ``__versioned__`` excludes — pick it deterministically so
# the flagged attribute is stable across SQLAlchemy versions /
# mapper-configuration orders. We deliberately avoid ``uuid``
# here: when a versioned-parent UPDATE goes through with ``uuid``
# flagged, the column's ``UUIDType``/BLOB round-trip produces a
# memoryview that fails an FK integrity check on some dialects
# (observed in ``test_rls_filter_alters_no_role_user_birth_names_query``
# and ``test_restore_applies_scalar_field``). ``description`` is
# a plain text column with no marshaling layer, so flagging it
# safely round-trips its current value. Falls back to ``uuid``
# then ``col_keys[0]`` for forks that excluded ``description``.
if "description" in col_keys:
flag_col = "description"
elif "uuid" in col_keys:
flag_col = "uuid"
else:
flag_col = col_keys[0]
try:
attributes.flag_modified(parent, flag_col)
except InvalidRequestError:
# The parent is a freshly-constructed ``session.new`` instance
# whose ``uuid`` default (``default=uuid4``) hasn't fired yet
# — the attribute is unloaded in instance state, so
# ``flag_modified`` rejects it. The parent will INSERT in this
# flush regardless, so the flag was redundant; safely skip.
# Hit by ``test_create_dataset_item`` (POST /api/v1/dataset/).
# whose attribute defaults haven't fired yet — the attribute
# is unloaded in instance state, so ``flag_modified`` rejects
# it. The parent will INSERT in this flush regardless, so the
# flag was redundant; safely skip. Hit by
# ``test_create_dataset_item`` (POST /api/v1/dataset/).
continue