Mike Bayer [Mon, 22 Dec 2025 15:40:06 +0000 (10:40 -0500)]
new pre-caching architecture for autogenerate
Autogenerate reflection sweeps now use the "bulk" inspector methods
introduced in SQLAlchemy 2.0, which for selected dialects including
PostgreSQL and Oracle use batched queries to reflect whole collections of
tables using O(1) queries rather than O(N).
This is the original proposed version that uses the Inspector
entirely with its public API, with the exception of reflect_table()
which makes a _ReflectionInfo on a per-table basis. Other than
that, no private API assumptions are made.
If SQLAlchemy needed to add new fields to _ReflectionInfo, it just
needs to make sure they have default functions (which all the current fields
should have anyway, since there is even a ReflectionDefaults
constant that already provides these!)
This version is the one that does not imply any particular
changes in SQLAlchemy and does not have any sqla_compat logic,
so that we may have alembic using the new performance enhancements
allowing for SQLAlchemy to potentially improve its API for a later
release.
Other than that, typing of reflection functions is improved.
Mike Bayer [Mon, 5 Jan 2026 19:07:38 +0000 (14:07 -0500)]
dont compare server defaults if user FN returns non-None
fixed regression caused by e532a7e39cb6b0e91fbe045778f3
where we started continuing default server default comparison
even if user defined function returned False.
Mike Bayer [Fri, 2 Jan 2026 19:19:46 +0000 (14:19 -0500)]
filter out ::regclass for sequences
Fixed issue where PostgreSQL sequence defaults on non-primary key columns
were incorrectly detected as changed on every autogenerate run. Server
default comparison logic is adjusted to filter out the ``::regclass``
expression added by the server which interferes with the comparison.
Mike Bayer [Fri, 26 Dec 2025 17:14:49 +0000 (12:14 -0500)]
support paths in file_template
The ``file_template`` configuration option now supports directory paths,
allowing migration files to be organized into subdirectories. When using
directory separators in ``file_template`` (e.g.,
``%(year)d/%(month).2d/%(day).2d_%(rev)s_%(slug)s``), Alembic will
automatically create the necessary directory structure. The
``recursive_version_locations`` setting must be set to ``true`` when using
this feature in order for the revision files to be located for subsequent
commands.
Mike Bayer [Thu, 11 Dec 2025 22:20:21 +0000 (17:20 -0500)]
organize into a "plugin" directory structure
we attempt to move autogen functions into independent units
that are more obviously pluggable, and we add support for
arbitrary "plugin" entrypoints that could add more pluggable
units into autogenerate or anywhere else
Federico Caselli [Tue, 16 Dec 2025 20:30:47 +0000 (21:30 +0100)]
Avoid deprecation warning in add/drop constraint
Ensure that alembic is compatible with the changes added in
https://github.com/sqlalchemy/sqlalchemy/issues/13006
by explicitly setting isolate_from_table=True in sqlalchemy 2.1
Mike Bayer [Tue, 16 Dec 2025 14:53:13 +0000 (09:53 -0500)]
get write_pyi to support lowercase types with pipes
The specific form of `tuple[] | None` produces a `types.UnionType`
in some way that seems to not be what it has ever been previously
(an explicit Union will give you `<class 'typing._UnionGenericAlias'>`,
apparently). so repr() this specific case so we can move to newer
typing formats.
As a test, this moves the type of partial_reordering to the newer
format.
Also, write output file using shutil.move from tempfile, so that
crashes of write_pyi dont corrupt the file.
Add version checks for black, python version
bump minimum python version to 3.12 as 3.11 seems to have problems
we dont need to fix
Daniël [Tue, 16 Dec 2025 13:27:05 +0000 (08:27 -0500)]
Fix typing of `partial_reordering`
<!-- Provide a general summary of your proposed changes in the Title field above -->
The typing of the `partial_reordering` argument of `batch_alter_table` is not correct.
<!-- Describe your changes in detail -->
As stated in the documentation and docstring, the `batch_alter_table()` function expects a list of tuples for the `partial_reordering` argument. The current typing suggests the user to insert a tuple containing Any into the argument, which doesn't align with the docstring.
<!-- go over following points. check them with an `x` if they do apply, (they turn into clickable checkboxes once the PR is submitted, so no need to do everything at once)
-->
This pull request is:
- [ x] A documentation / typographical error fix
- Good to go, no issue or tests are needed
- [ ] A short code fix
- please include the issue number, and create an issue if none exists, which
must include a complete example of the issue. one line code fixes without an
issue and demonstration will not be accepted.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests. one line code fixes without tests will not be accepted.
- [ ] A new feature implementation
- please include the issue number, and create an issue if none exists, which must
include a complete example of how the feature would look.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests.
Mike Bayer [Tue, 16 Dec 2025 03:01:06 +0000 (22:01 -0500)]
force the driver level on the bulk insert PG tests
this changes to use the psycopg2 dialect in all cases
even if the default for postgresql changes.
this is to prepare for SQLAlchemy moving to psycopg as the
default dialect. I had hoped to move these tests to use psycopg
and test for bind casts, however, we are still running tests against
SQLAlchemy 1.4 which does not have the psycopg dialect. so stick with
psycopg2 for now.
also adds an rmtree for build/ as apparently pip install is re-using
this directory if it's there from a previous build, and the
artfacts left over from the gerrit for "plugins" is interfering with
the stubs generation. We really should find a way to make the stub
generation / testing more robust as it breaks much too easily and
without an easy way to tell why.
qu3vipon [Wed, 19 Nov 2025 20:47:39 +0000 (15:47 -0500)]
Add logging for config load source in verbose mode
### Description
I added a log message to indicate where the Alembic configuration is being loaded from (file vs in-memory), which helps when verbose mode is enabled.
I also wrote tests for both branches. Each test passes when run individually, but they fail when running the entire test suite. It seems to be related to how Alembic’s logging hierarchy interacts with the global test environment, and I'm having difficulty diagnosing the issue with my current understanding of the logging system.
I'd appreciate any guidance or suggestions from maintainers on how the logging should be captured or how tests in this area are expected to be structured.
### Checklist
This pull request is:
- [ ] A documentation / typographical error fix
- Good to go, no issue or tests are needed
- [x] A short code fix
- please include the issue number, and create an issue if none exists, which
must include a complete example of the issue. one line code fixes without an
issue and demonstration will not be accepted.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests. one line code fixes without tests will not be accepted.
- [ ] A new feature implementation
- please include the issue number, and create an issue if none exists, which must
include a complete example of how the feature would look.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests.
Mike Bayer [Thu, 20 Nov 2025 15:45:37 +0000 (10:45 -0500)]
implement ColumnComment compiles for SQL Server
Implemented DDL for column comment add/update/delete when using the
:paramref:`.Operations.alter_column.comment` parameter with
:meth:`.Operations.alter_column` on Microsoft SQL Server. Previously,
these functions were not implemented for SQL Server and would raise
``UnsupportedCompilationError``.
Mike Bayer [Sat, 15 Nov 2025 16:46:35 +0000 (11:46 -0500)]
bump pytest to 9; add pyv to file template; use = for all custom args
adding "test/" to pytest doesnt work because then we can't indicate
a specific set of test files. use = for all sqlalchemy-custom
parameters instead to avoid [1]
Added :paramref:`.Operations.implementation_for.replace` parameter to
:meth:`.Operations.implementation_for`, allowing replacement of existing
operation implementations. This allows for existing operations such as
:class:`.CreateTableOp` to be extended directly. Pull request courtesy
justanothercatgirl.
Mike Bayer [Wed, 5 Nov 2025 14:19:57 +0000 (09:19 -0500)]
run drop default constraint ahead of type
Fixed issue in SQL Server dialect where the DROP that's automatically
emitted for existing default constraints during an ALTER COLUMN needs to
take place before not just the modification of the column's default, but
also before the column's type is changed.
also vendor sqlalchemy's metadata fixture which otherwise does not
integrate with alembic's version of the connection fixture
Mike Bayer [Sat, 1 Nov 2025 02:35:49 +0000 (22:35 -0400)]
add test for issue 1743
however this issue looks to be simple sqlite reflection issue
on sqlalchemy side
also fixes the incorrect exclusions code from 20c108720d7127ca.
just skip entire test if SQLite fk reflection is not there.
this exclusion should not have been added originally, it looks
like this was mistaken for a limitation when it really was a bug
in sqlite reflection
Mike Bayer [Sat, 1 Nov 2025 03:39:37 +0000 (23:39 -0400)]
update the fk_names req
This is not a limitation this is just a bug in sqlalchemy,
being fixed by #12954. we dont have a system in place
to cross-check gerrits right now so just merge this and then
merge the SQLAlchemy fix
Mike Bayer [Wed, 29 Oct 2025 13:33:42 +0000 (09:33 -0400)]
remove DB-based skips on non-backend tests
for tests that are testing for SQL compilation only,
there's no need to limit based on DB backend since we aren't
using it. this avoids the awkward situation where tests like
test_sqlite.py -> test_create_table_with_comment_ignored would
be skipped when running the test suite against sqlite, but would
run just fine when running against any other DB (because those DBs
dont have a comment limitation and the test is just a compilation
test).
Kim Wooseok [Tue, 28 Oct 2025 12:05:24 +0000 (08:05 -0400)]
Add colon (:) to invalid char for revision name add unittest
Disallow ':' character in custom revision identifiers. Previously, using a
colon in a revision ID (e.g., 'REV:1') would create the revision, however
revisions with colons in them are not correctly interpreted by other
commands, as it overlaps with the revision range syntax. Pull request
courtesy Kim Wooseok with original implementation by Hrushikesh Patil.
Stefan Scherfke [Tue, 28 Oct 2025 12:06:31 +0000 (08:06 -0400)]
Add "--check-heads" option to "current" command
Added :paramref:`.command.current.check_heads` parameter to
:func:`.command.current` command, available from the command line via the
``--check-heads`` option to ``alembic current``. This tests if all head
revisions are applied to the database and raises :class:`.DatabaseNotAtHead`
(or from the command line, exits with a non-zero exit code) if this is not
the case. The parameter operates equvialently to the cookbook recipe
:ref:`cookbook_check_heads`. Pull request courtesy Stefan Scherfke.
Mike Bayer [Sat, 11 Oct 2025 17:30:07 +0000 (13:30 -0400)]
more nox
* add support for backend-only
* main runner only runs one database at a time, there is no multiple
db for one pytest run feature here
* use match/case!
Mike Bayer [Thu, 9 Oct 2025 13:21:16 +0000 (09:21 -0400)]
remove suite name changes
the junit plugin doesnt need suites to have distinct names.
it is actually merging correctly, the reason for lots of noise
is that the different suites have lots of skips for the
"backend" marked suites. will fix this in pytestplugin
Mike Bayer [Mon, 6 Oct 2025 20:01:03 +0000 (16:01 -0400)]
test updates
* remove .coveragerc since this produces warnings vs. pytest-cov
* use junitparser to rename the suite inside each junit file. I think
jenkins junit reads multiple files but they need to have distinct
suite names to show up (or distinct test names, but we dont have that
here).
Fixed issue where new pyproject.toml config would fail to parse the integer
value used for the ``truncate_slug_length`` parameter. Pull request
courtesy Luís Henrique Allebrandt Schunemann.
Fixed Python-side autogenerate rendering of index expressions in MySQL
dialect by aligning it with SQLAlchemy's MySQL index expression rules. Pull
request courtesy david-fed.
Mike Bayer [Wed, 9 Jul 2025 18:57:11 +0000 (14:57 -0400)]
add boolean interpretation to config
Fixed issue in new ``pyproject.toml`` support where boolean values, such as
those used for the ``recursive_version_locations`` and ``sourceless``
configuration parameters, would not be accepted.
This hook type is almost identical to the console_scripts hook, except
it's running `python -m black` instead of using black's console_script.
It is mainly useful for tools without console scripts (e.g. ruff), but
has semantics closer to the console_scripts hook in that it finds the
ruff module available to the running interpreter instead of finding
an executable by path.
Fixed the rendering of ``server_default=FetchedValue()`` to ensure it is
preceded by the ``sa.`` prefix in the migration script. Pull request
courtesy david-fed.
Mike Bayer [Tue, 8 Jul 2025 14:05:56 +0000 (10:05 -0400)]
fix metadata requirement in fk render; remove column.copy()
Fixed autogenerate rendering bug which failed to render foreign key
constraints local to a :class:`.CreateTableOp` object if it did not refer
to a ``MetaData`` collection via a private constructor argument that would
not ordinarily be passed in user-defined rewriter recipes, including ones
in the Alembic cookbook section of the docs.
Also removed the use of column.copy() from the CreateTableOp rewriter
recipe and added a new test for this recipe which includes a table with
an FK. it's not clear why copy() was needed here and if we get more
reports of issues, we can add to this test suite and adjust.
Mike Bayer [Tue, 24 Jun 2025 18:04:56 +0000 (14:04 -0400)]
try flake8-import-order 0.19.2
the big new thang is that it is doing import order checks inside of
TYPE_CHECKING blocks. Introduces some new codes that we
enthusiastically add to our ignore list.
Justin Malin [Thu, 12 Jun 2025 19:56:36 +0000 (15:56 -0400)]
[Fixes #1671] Passthrough `dialect_kwargs` to `ops.create_foreign_key()`
<!-- Provide a general summary of your proposed changes in the Title field above -->
### Description
<!-- Describe your changes in detail -->
Fixes #1671
SqlAlchemy supports adding dialect kwargs for foreign keys, as does `op.create_foreign_key()`, but the renderer for `ops.CreateForeignKeyOp` does not pass through `dialect_kwargs`. An example of this is `postgresql_not_valid`.
### Checklist
<!-- go over following points. check them with an `x` if they do apply, (they turn into clickable checkboxes once the PR is submitted, so no need to do everything at once)
-->
This pull request is:
- [ ] A documentation / typographical error fix
- Good to go, no issue or tests are needed
- [x] A short code fix
- please include the issue number, and create an issue if none exists, which
must include a complete example of the issue. one line code fixes without an
issue and demonstration will not be accepted.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests. one line code fixes without tests will not be accepted.
- [ ] A new feature implementation
- please include the issue number, and create an issue if none exists, which must
include a complete example of how the feature would look.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests.
Mike Bayer [Sun, 8 Jun 2025 20:14:19 +0000 (16:14 -0400)]
updates for mypy 1.16
some errors go away, others come in. basically moving
ignores around. need to pin to 1.16 at the lowest as these
would now fail on earlier mypy versions
Simon Tas [Thu, 29 May 2025 00:11:58 +0000 (20:11 -0400)]
allow tuple type in down_revision in migration templates
<!-- Provide a general summary of your proposed changes in the Title field above -->
FIXES #1665
Currently the type of `down_revision` in migration templates is `Union[str, None]`. However, when you generate a migration using `alembic merge heads` it will generate a migration with a tuple of strings in `down_revision` which causes a type error.
### Description
f.e. when you have 2 heads and run `alembic merge heads` you will get a migration that looks like this:
which will give a type error with type checkers like mypy and pyright. This change fixes this. From the code it seems like just like `branch_labels` and `depends_on` the `down_revision` is first casted to a tuple if needed so typing it as `Sequence[str]` and not `Tuple[str, ...]` should be safe.
### Checklist
<!-- go over following points. check them with an `x` if they do apply, (they turn into clickable checkboxes once the PR is submitted, so no need to do everything at once)
-->
This pull request is:
- [ ] A documentation / typographical error fix
- Good to go, no issue or tests are needed
- [x] A short code fix
- please include the issue number, and create an issue if none exists, which
must include a complete example of the issue. one line code fixes without an
issue and demonstration will not be accepted.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests. one line code fixes without tests will not be accepted.
- [ ] A new feature implementation
- please include the issue number, and create an issue if none exists, which must
include a complete example of how the feature would look.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests.
Mike Bayer [Wed, 21 May 2025 21:14:37 +0000 (17:14 -0400)]
honor get_templates_path() first
Fixed regression caused by the ``pathlib`` refactoring that removed the use
of :meth:`.Config.get_template_directory` as the canonical source of
templates; the method is still present however it no longer would be
consulted for a custom config subclass, as was the case with flask-migrate.
Fixed regression caused by the ``pathlib`` refactoring where the "missing
template" error message failed to render the name of the template that
could not be found.