Skip to content

refactor: do not cascade to TableColumn#42222

Open
hy144328 wants to merge 3 commits into
apache:masterfrom
hy144328:refactor/sqlalchemy-migration-tablecolumn-backref
Open

refactor: do not cascade to TableColumn#42222
hy144328 wants to merge 3 commits into
apache:masterfrom
hy144328:refactor/sqlalchemy-migration-tablecolumn-backref

Conversation

@hy144328

@hy144328 hy144328 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

See #40273 .

SUMMARY

Disable cascade_backrefs on TableColumn because it is deprecated in Airflow 2.
Manually add objects to session that were previously added automatically.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

TESTING INSTRUCTIONS

Unit tests:

$ SQLALCHEMY_WARN_20=1 python3 -m pytest tests/unit_tests/

Integration tests via CI.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@dosubot dosubot Bot added change:backend Requires changing the backend risk:refactor High risk as it involves large refactoring work labels Jul 19, 2026
@bito-code-review

bito-code-review Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #fd5a32

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/connectors/sqla/models.py - 1
    • Inconsistent relationship configuration · Line 964-964
      The `TableColumn.table` relationship now has `cascade_backrefs=False` (line 964), but the corresponding inverse relationship `SqlaTable.columns` (line 1297-1302) lacks this parameter. This creates asymmetric behavior where one direction of bidirectional assignment will trigger ORM events while the other won't. The `SqlMetric.table` relationship (line 1207-1210) also lacks this parameter, making the inconsistency more apparent. Apply `cascade_backrefs=False` to `SqlaTable.columns` to ensure symmetric backref cascade behavior.
Review Details
  • Files reviewed - 2 · Commit Range: 7050c28..7050c28
    • pytest.ini
    • superset/connectors/sqla/models.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.22%. Comparing base (158e321) to head (779cb08).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42222      +/-   ##
==========================================
- Coverage   65.22%   65.22%   -0.01%     
==========================================
  Files        2768     2768              
  Lines      156280   156285       +5     
  Branches    35774    35774              
==========================================
+ Hits       101933   101936       +3     
- Misses      52382    52384       +2     
  Partials     1965     1965              
Flag Coverage Δ
hive 38.60% <0.00%> (-0.01%) ⬇️
mysql 57.87% <100.00%> (+<0.01%) ⬆️
postgres 57.92% <100.00%> (+<0.01%) ⬆️
presto 40.54% <57.14%> (+<0.01%) ⬆️
python 59.33% <100.00%> (+<0.01%) ⬆️
sqlite 57.53% <100.00%> (+<0.01%) ⬆️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

table: Mapped["SqlaTable"] = relationship(
"SqlaTable",
back_populates="columns",
cascade_backrefs=False,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Setting this relationship to disable backref cascading can cause newly created child objects to be silently left out of the session when they are attached via reverse assignment, so expected column inserts may never be flushed. If any code path creates a column with column.table = table and only persists the table graph, you need to explicitly add each column to the session or keep backref save-update cascading enabled. [logic error]

Severity Level: Major ⚠️
- ❌ Dataset column creation via ORM fails to persist rows.
- ⚠️ Metadata refresh may silently skip saving new columns.
- ⚠️ Administrators see datasets without expected column definitions.
Steps of Reproduction ✅
1. Start Superset with the PR code, so `TableColumn.table` relationship at
`superset/connectors/sqla/models.py:961-965` uses `cascade_backrefs=False`.

2. Trigger a dataset metadata refresh or creation that constructs columns via
`TableColumn(table=sqla_table, ...)` (same module `superset/connectors/sqla/models.py`, in
the `SqlaTable` helper that builds `TableColumn` instances from inspector metadata).

3. In this flow, the `SqlaTable` instance is added to the SQLAlchemy session, but the new
`TableColumn` instances rely on the `table` assignment (`column.table = sqla_table`) and
backref cascade to be pulled into the session; they are not explicitly `session.add()`-ed
nor appended through `sqla_table.columns.append(column)`.

4. On flush, because `cascade_backrefs=False` at `superset/connectors/sqla/models.py:964`,
the ORM does not cascade `save-update` from `SqlaTable.columns` to these newly-associated
`TableColumn` objects, so expected column rows are never inserted into the `columns`
table; the dataset is created/refreshed without persisted column records.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/connectors/sqla/models.py
**Line:** 964:964
**Comment:**
	*Logic Error: Setting this relationship to disable backref cascading can cause newly created child objects to be silently left out of the session when they are attached via reverse assignment, so expected column inserts may never be flushed. If any code path creates a column with `column.table = table` and only persists the table graph, you need to explicitly add each column to the session or keep backref save-update cascading enabled.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. Disabling cascade_backrefs on the TableColumn.table relationship prevents SQLAlchemy from automatically adding new TableColumn objects to the session when they are associated with a SqlaTable via the table attribute. To resolve this, you should either remove cascade_backrefs=False to restore default behavior, or ensure that every newly created TableColumn is explicitly added to the session using session.add(column) before flushing.

superset/connectors/sqla/models.py

table: Mapped["SqlaTable"] = relationship(
        "SqlaTable",
        back_populates="columns",
        # cascade_backrefs=False,  <-- Remove this line to restore default behavior
    )

@hy144328
hy144328 force-pushed the refactor/sqlalchemy-migration-tablecolumn-backref branch from 7050c28 to 171e144 Compare July 19, 2026 23:34
@bito-code-review

bito-code-review Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #9ba9f8

Actionable Suggestions - 0
Additional Suggestions - 1
  • pytest.ini - 1
    • Inconsistent warning filter pattern · Line 36-36
      Enabling only `TableColumn` for session-merge warning enforcement creates inconsistency with the commented-out pattern on lines 32-35 and 37. If TableColumn objects are being incorrectly merged into Sessions, this change is correct, but the rationale should be documented to prevent confusion during future SQLAlchemy 2.0 migration work.
Review Details
  • Files reviewed - 2 · Commit Range: 171e144..171e144
    • pytest.ini
    • superset/connectors/sqla/models.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas

Copy link
Copy Markdown
Member

Rebased, resolved the usual conflict with these, and CI is running :)

@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 6f9e4f0
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a5e839ddd8b020008d9d85e
😎 Deploy Preview https://deploy-preview-42222--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Comment on lines +1012 to +1025
columns = [
TableColumn(column_name="user", type="VARCHAR(255)"),
TableColumn(
column_name="username",
type="VARCHAR(255)",
expression="{{ current_username() }}",
),
]
db.session.add_all(columns)
table = SqlaTable(
table_name="test_has_no_extra_cache_keys_table",
sql="SELECT 'abc' as user",
database=get_example_database(),
columns=[
TableColumn(column_name="user", type="VARCHAR(255)"),
TableColumn(
column_name="username",
type="VARCHAR(255)",
expression="{{ current_username() }}",
),
],
columns=columns,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: TableColumn objects are added to the session before they are attached to a table, and then get_example_database() is called while building the dataset. That call is likely to execute a query, which triggers SQLAlchemy autoflush and can try to insert these columns with a null table_id, causing an integrity error. Attach the columns to the table before adding them to the session (or defer add_all until after table construction / use no_autoflush). [logic error]

Severity Level: Major ⚠️
-`test_extra_cache_keys_in_dataset_metrics_and_columns` may raise IntegrityError.
- ❌ Integration test suite / CI run can be blocked.
- ⚠️ Flaky behavior if database constraints differ between environments.
Steps of Reproduction ✅
1. Run the integration test `test_extra_cache_keys_in_dataset_metrics_and_columns` in
`tests/integration_tests/sqla_models_tests.py:1008`.

2. Inside this test, at lines 1012–1020, two `TableColumn` instances are created **without
a `table`/`table_id` assigned** and immediately added to the session via
`db.session.add_all(columns)` at line 1020.

3. Still in the same test, at line 1021, `SqlaTable` is instantiated with
`database=get_example_database()` (imported from `superset.utils.database`), which
internally performs a `db.session.query(...)` to fetch the example `Database` record,
causing SQLAlchemy to autoflush the pending `TableColumn` objects.

4. During this autoflush, SQLAlchemy attempts to INSERT the two `TableColumn` rows while
their `table_id` is still `NULL` (they are not yet linked to `table` because
`SqlaTable(..., columns=columns)` hasn’t run), which can raise an `IntegrityError` if
`TableColumn.table_id` is non-nullable, causing this test (and therefore the integration
test suite/CI run) to fail.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** tests/integration_tests/sqla_models_tests.py
**Line:** 1012:1025
**Comment:**
	*Logic Error: `TableColumn` objects are added to the session before they are attached to a table, and then `get_example_database()` is called while building the dataset. That call is likely to execute a query, which triggers SQLAlchemy autoflush and can try to insert these columns with a null `table_id`, causing an integrity error. Attach the columns to the table before adding them to the session (or defer `add_all` until after table construction / use `no_autoflush`).

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@sadpandajoe

Copy link
Copy Markdown
Member

@hy144328 thanks for the PR, mind adding more details to your PR description?

@hy144328
hy144328 marked this pull request as draft July 20, 2026 18:22
@hy144328
hy144328 force-pushed the refactor/sqlalchemy-migration-tablecolumn-backref branch from c9d8724 to 718a10d Compare July 20, 2026 19:58
@hy144328
hy144328 force-pushed the refactor/sqlalchemy-migration-tablecolumn-backref branch from 718a10d to 6f9e4f0 Compare July 20, 2026 20:22
@hy144328
hy144328 force-pushed the refactor/sqlalchemy-migration-tablecolumn-backref branch from 6f9e4f0 to 575416a Compare July 20, 2026 21:04
@hy144328
hy144328 marked this pull request as ready for review July 20, 2026 22:19
@bito-code-review

bito-code-review Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #c2b2f8

Actionable Suggestions - 0
Additional Suggestions - 3
  • tests/integration_tests/db_engine_specs/bigquery_tests.py - 1
    • Variable shadowing import · Line 319-320
      Local variable `column` shadows the `column` function imported from `sqlalchemy` on line 22, creating semantic ambiguity within the test method scope. Per project style rules, local variables should not shadow module-level imports.
  • superset/connectors/sqla/models.py - 2
    • Missing documentation for SQLAlchemy 2.0 fix · Line 964-964
      The `cascade_backrefs=False` at line 964 lacks the documentation comment that exists at line 1375 for an identical pattern. Without context, future maintainers cannot understand why this parameter is needed or that it must be removed when upgrading to SQLAlchemy 2.0.
    • Missing documentation for SQLAlchemy 2.0 fix · Line 1301-1301
      The `cascade_backrefs=False` at line 1301 lacks the documentation comment that exists at line 1375 for an identical pattern. Without context, future maintainers cannot understand why this parameter is needed or that it must be removed when upgrading to SQLAlchemy 2.0.
Review Details
  • Files reviewed - 13 · Commit Range: 08c6a0d..779cb08
    • pytest.ini
    • superset/commands/dataset/duplicate.py
    • superset/connectors/sqla/models.py
    • superset/examples/birth_names.py
    • tests/integration_tests/conftest.py
    • tests/integration_tests/datasets/api_tests.py
    • tests/integration_tests/datasource_tests.py
    • tests/integration_tests/db_engine_specs/base_engine_spec_tests.py
    • tests/integration_tests/db_engine_specs/bigquery_tests.py
    • tests/integration_tests/db_engine_specs/datastore_tests.py
    • tests/integration_tests/fixtures/datasource.py
    • tests/integration_tests/import_export_tests.py
    • tests/integration_tests/sqla_models_tests.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:backend Requires changing the backend risk:refactor High risk as it involves large refactoring work size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants