-
-
Notifications
You must be signed in to change notification settings - Fork 279
Add support of IF [NOT] EXISTS for ADD/DROP COLUMN in Postgresql #1627
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
0a85a7f
to
c9a7bc2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hi -
while we can't go with any of the "postgresql_" hardcoding here outside of postgresql.py, there are plenty of other databases that support IF NOT EXISTS/IF EXISTS for alter table add/drop column, just googling shows there are a bunch like snowflake, etc.
So please just remove this from being specific to postgresql and implement in a similar way as if_exists/if_not_exists is done for create_index/drop_index/create_table/drop_table etc.
alembic/operations/ops.py
Outdated
@@ -2034,16 +2034,20 @@ def __init__( | |||
column: Column[Any], | |||
*, | |||
schema: Optional[str] = None, | |||
postgresql_if_not_exists: Optional[bool] = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can't have the name "postgresql" in this file, this is not part of the postgresql implementation. see other op-level dialect-specific arguments we have, which I think at the moment is just mssql_drop_check and mssql_drop_forsign_key, for how to pull dialect-specific arguments from kw.
that said it's not clear if this argument should be postgresql_if_not_exists
in the first place
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have precedent here, like postgresql_using https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.alter_column.params.postgresql_using
Overall if only postgresql suppots it maybe it makes sense to start with it then expand if there is a request for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the word "postgresql" does not appear in the ops.py file outside of a docstring
we cannot have dialect-level rules hardcoded in the core of alembic full stop
the use case for this user is they explicitly want rendering to render this keyword
so that will not work, because we do not render the kw
attribute of ops. They can make their own @render
function to override that but that's pretty inconvenient
in this specific case, "if exists / if not exists" is extremely common
mariadb - that alone is reason enough
snowflake
jooq has a whole list including bigquery, cockroachdb, databricks, teradata - and they actually have syntaxes for Oracle, SQL Server too using short procedures, so we could even do those too
so in short this flag should be added without any prefix, however postgresql and mysql/mariadb impls can handle the rendering functions, need tests in test_postgresql test_mysql
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, I guess the main difference here is that with postgresql_using we never expect it to be automatically rendered.
Regarding the availability in other db I missed that mariadb already had it.
alembic/autogenerate/render.py
Outdated
op.schema, | ||
op.table_name, | ||
op.column, | ||
op.postgresql_if_not_exists, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can't have postgresql-specific logic in render here. but also, the op should not have a .postgresql_if_not_exists
field anyway, it should be in .kw
.
while all of the ops have a .kw
field that stores additional dialect-specific keyword arguments, these are not included in renders right now. we can try adding these to renders but this would need tests across many
that said it's not clear if this argument should be called postgresql_if_not_exists
anyway
Hi, actually I went over the documentation and I couldn't find any list of officially supported engines, so I directed my search to the ones more obvious in the codebase: sqlite, oracle, mysql, mssql and postgresql. That being said, how should I handle dialects not supporting
I'd actually be glad to as it's way simpler 😄 |
Replied above regarding the prefix |
c9a7bc2
to
1cd9a39
Compare
@zzzeek |
I think it's best to register for both since use of the explicit "mariadb://" style URL is not common |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision 1cd9a39 of this pull request into gerrit so we can run tests and reviews and stuff
New Gerrit review created for change 1cd9a39: https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5879 |
1cd9a39
to
7702b01
Compare
There you go with an updated version. Let's hope it suits you now :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this is sqla-tester setting up my work on behalf of CaselIT to try to get revision 7702b01 of this pull request into gerrit so we can run tests and reviews and stuff
Patchset 7702b01 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5879 |
CI is getting failures that seem unrelated to my work, any help would be appreciated to help me go further |
that looks as a direct result of the patch here, the error is here: https://jenkins.sqlalchemy.org/job/alembic_gerrit/pyv=py313,sqla=sqlamain/1374/testReport/junit/tests.test_batch/BatchRoundTripPostgresqlTest_postgresql+psycopg2_16_8/test_drop_col_schematype/
that is code that was added by your patch on line 392: https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5879/2/alembic/ddl/impl.py#392 |
federico also made a comment on that code that maybe |
89773d4
to
7154331
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision 7154331 of this pull request into gerrit so we can run tests and reviews and stuff
Patchset 7154331 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5879 |
once the PR is working maybe I can go and see why kw isnt compatible there |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision 5391908 of this pull request into gerrit so we can run tests and reviews and stuff
Patchset 5391908 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5879 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, this is sqla-tester and I see you've pinged me for review. However, user lachaib is not authorized to initiate CI jobs. Please wait for a project member to do this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision 5391908 of this pull request into gerrit so we can run tests and reviews and stuff
Patchset 5391908 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5879 |
hi - we've requested changes here but I'm not sure they propagated to the PR. can you take a look at the comments listed at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5879?tab=comments and make sure all those changes are made here? thanks |
5391908
to
82d6319
Compare
@zzzeek thanks for pointing me to gerrit I'm not used to it so I missed them, now all points there should be fixed, let me know |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision 82d6319 of this pull request into gerrit so we can run tests and reviews and stuff
Patchset 82d6319 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5879 |
Michael Bayer (zzzeek) wrote: recheck View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5879 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
close. can we simplify the rendering implementation? it looks like it added different kinds of methods to postgresql, mysql, mssql, seems like it should be more consolidated
alembic/ddl/mssql.py
Outdated
@@ -222,7 +223,9 @@ def drop_column( | |||
drop_fks = kw.pop("mssql_drop_foreign_key", False) | |||
if drop_fks: | |||
self._exec(_ExecDropFKConstraint(table_name, column, schema)) | |||
super().drop_column(table_name, column, schema=schema, **kw) | |||
super().drop_column( | |||
table_name, column, schema=schema, if_exists=if_exists, **kw |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does mssql support DROP COLUMN IF EXISTS ? there's no test and wondering why this is here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it doesn't, but I had a failure with mypy without it (signature of override differing from parent class)
several options come to my mind:
- leave it so; mypy and linters happy, but can be misleading
- leave if_exists in signature, but don't pass it through; mypy is happy other linters may complain
- leave if_exists in signature, log a warning if not none, don't pass it through; linters are happy, users have a proper feedback of incorrect use
- remove it from signature and make add
# type: ignore
comment; may silently impact any future change on this API - update parent signature to make the
if_exist
argument implicitly taken from**kw
; mypy and linters happy, users and maintainer are not
My preferred option is the 3rd and I'm gonna submit an additional commit in this direction, we'll amend eventually if you select another one
alembic/ddl/mysql.py
Outdated
element: AddColumn, compiler: MySQLDDLCompiler, **kw | ||
) -> str: | ||
|
||
return "%s ADD COLUMN %s%s" % ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how come this is done so differently vs. the postgresql version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Main element of explanation: I privileged local style consistency, and the implem files are way different one from the other. Also I did postgresql way before and worked on this file at another moment, hence privileging local style over daring to harmonise the contribution and make this change seem odd compared to the rest of the file
alembic/ddl/postgresql.py
Outdated
def visit_add_column(element: AddColumn, compiler: PGDDLCompiler, **kw) -> str: | ||
return "%s %s" % ( | ||
alter_table(compiler, element.table_name, element.schema), | ||
add_column( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just pass through if_exists / if_not_exists in base.py? it's just an argument, the compiler can still ignore it if it chooses. i dont think we need new AddColumn / DropColumn @compiles here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that alembic can be used to apply migration on several servers, my primary concern was that the automated generation of if_exist
would possibly break for someone using a combination of postgresql and something else.
However you master way more than me the different usages of alembic and if you feel confident that we can move it all to base.py
and remove it from implementations, I'd be happy to do so.
(It'll also solve most of the other questions)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think let's start by doing this with the least code possible with base.py supporting "IF [NOT] EXISTS" completely, then let's look for some way in base.py / impl.py to simply block the "if_[not]_exists" parameter from being propagated by default, with something in mysql.py /postgresql.py that re-enables its propagation.
this will after all make it easier for third party dialects to do this same step
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but to be clear, just do the first part of that paragraph unless you have a great idea for the second part. otherwise Ill take the succinct version of the code and figure something out.
alembic/ddl/postgresql.py
Outdated
text = "ADD COLUMN " | ||
if if_not_exists: | ||
text += "IF NOT EXISTS " | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not put this in base.py? if I have a migration that is adding / dropping columns that may or may not exist already, can't it be assumed I'm using a database that supports this syntax? would I want if_exists / if_not_exists to be silently ignored if I passed it?
even if we dont want it to render we should likely keep this in base.py anyway and then control for when we actually render IF [NOT] EXISTS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pushed the move to base.py as a distinct commit for you to validate, I'll squash changes prior to merging if you confirm it's ok for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision c503b04 of this pull request into gerrit so we can run tests and reviews and stuff
Patchset c503b04 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5879 |
Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/alembic/+/5879 has been merged. Congratulations! :) |
Thanks a lot for your patience and your help with this contribution |
Fixes #1626
Description
Enable support of IF NOT EXISTS for AddColumn/DropColumn so that the flow from auto-generation to execution on PostgreSQL will work.
Took the opportunity to mention the
if_exists/if_not_exists
in a cookbook entry also for CreateTable/CreateIndexDisclaimer: I'm still not fully proficient with
alembic
's code so don't hesitate to challenge the implementation.Checklist
This pull request is:
must include a complete example of the issue. one line code fixes without an
issue and demonstration will not be accepted.
Fixes: #<issue number>
in the commit messageinclude a complete example of how the feature would look.
Fixes: #<issue number>
in the commit messageHave a nice day!