Skip to content

fix(sqlalchemy-spanner): support spanner_storing in Alembic op.create_index() and compare_indexes() - #18840

Open
rocksnow1942 wants to merge 2 commits into
googleapis:mainfrom
rocksnow1942:fix-18835-alembic-spanner-storing
Open

rocksnow1942 wants to merge 2 commits into
googleapis:mainfrom
rocksnow1942:fix-18835-alembic-spanner-storing

Conversation

@rocksnow1942

Copy link
Copy Markdown

Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:

  • Make sure to open an issue as a bug/issue before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea
  • Ensure the tests and linter pass
  • Code coverage does not decrease (if any source code was changed)
  • Appropriate docs were updated (if necessary)

Fixes #18835 🦕

Summary

This PR resolves two issues when using sa.Index(..., spanner_storing=[...]) with Alembic:

  1. op.create_index(..., spanner_storing=["col"]) raising KeyError on string column names:

    • When op.create_index("ix", "tbl", ["c1"], spanner_storing=["c2"]) runs inside an Alembic migration, alembic.operations.schemaobj.SchemaObjects.index constructs an ad-hoc Table("tbl") containing only the indexed key columns (["c1"]).
    • SpannerDDLCompiler.visit_create_index previously resolved string column names via index.table.c[col], raising KeyError: 'c2'. It now extracts the column name directly (c if isinstance(c, str) else c.name).
    • Additionally, SpannerImpl.create_index appends missing storing columns to index.table.c as NullType columns (matching Alembic's PostgresqlImpl.create_index and MSSQLImpl.create_index).
  2. Alembic --autogenerate ignoring spanner_storing changes:

    • Registers SpannerImpl(DefaultImpl) for "spanner+spanner" when Alembic is installed, overriding compare_indexes to compare spanner_storing column sets (set(m_storing) != set(r_storing)). Comparing as sets detects additions, removals, and modifications of STORING columns while avoiding false-positive diffs when Spanner reflection returns STORING columns in table-column order.

…_index() and compare_indexes()

1. In SpannerDDLCompiler.visit_create_index, support string column names
   in spanner_storing without requiring them in index.table.c. When Alembic
   executes op.create_index(), the dummy Table only contains the indexed
   key columns; looking up storing columns in index.table.c raises KeyError.

2. Register SpannerImpl subclassing alembic.ddl.impl.DefaultImpl for
   dialect 'spanner+spanner' when Alembic is installed:
   - Override compare_indexes to compare spanner_storing as sets,
     avoiding spurious autogenerate diffs when reflected columns
     are reordered.
   - Override create_index to ensure string storing columns are appended
     to index.table.c as NullType columns if missing.

Fixes googleapis#18835
@rocksnow1942
rocksnow1942 requested a review from a team as a code owner October 24, 2026 05:08

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request introduces Alembic DDL support for Google Cloud Spanner, specifically implementing index comparison and creation with STORING columns. It adds SpannerImpl (inheriting from Alembic's DefaultImpl) to handle comparing indexes with storing columns and dynamically appending missing storing columns to the table schema during index creation. Unit tests are added to verify these behaviors and edge cases. Feedback was provided to simplify column name extraction in SpannerImpl.create_index by directly accessing col.name instead of using getattr, ensuring consistency with other parts of the codebase.

Comment thread packages/sqlalchemy-spanner/google/cloud/sqlalchemy_spanner/sqlalchemy_spanner.py Outdated

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[spanner-sqlalchemy]: support spanner_storing in Alembic op.create_index() and autogenerate compare_indexes()

1 participant