Skip to content
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

Update the type comparison code used for schema autogeneration. Compare #619

Closed
wants to merge 1 commit into from

Conversation

pbecotte
Copy link
Contributor

@pbecotte pbecotte commented Nov 8, 2019

the output text for the type to look for changes. In addition, allow
schemas to define sets of types that are functionally equivalent, such
as BOOL and TINYINT(1).
#605

Copy link
Collaborator

@sqla-tester sqla-tester left a 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 to try to get revision e4aeb65 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

New Gerrit review created for change e4aeb65: https://gerrit.sqlalchemy.org/#/c/sqlalchemy/alembic/+/1561

@zzzeek
Copy link
Member

zzzeek commented Nov 8, 2019

the jenkins runner will run some openstack suites which in the case of neutron might exercise the type comparison stuff a bit. let's see what happens.

@sqla-tester
Copy link
Collaborator

mike bayer (zzzeek) wrote:

Let me start off with, thank you very much for this work so far, it looks amazing! This is a really critical area where we get a lot of complaints and I am really excited if we can finally make it work for real.

so there's a bunch of failures but they look to be all in the realm of unexpected positives for the comparison logic.

in the main suite, we have for example, an unexpected change in type between VARCHAR and String (this is likely because on Oracle, it's rendered as VARCHAR2 ? )

Traceback (most recent call last):
File "/home/jenkins/workspace/alembic_gerrit/1d95c17e/tests/test_autogen_comments.py", line 101, in test_alter_table_comment
eq_(diffs[0][0], "add_table_comment")
File "/home/jenkins/workspace/alembic_gerrit/1d95c17e/.tox/py37-sqlamaster-sqlite-postgresql-mysql-oracle/lib/python3.7/site-packages/sqlalchemy/testing/assertions.py", line 237, in eq_
assert a == b, msg or "%r != %r" % (a, b)
AssertionError: ('modify_type', None, 'some_table', 'test', {'existing_nullable': None, 'existing_server_default': False, 'existing_comment': None}, VARCHAR(length=10), String(length=10)) != 'add_table_comment'

over in openstack nova, where they run tests that compare schemas to be the same on MySQL, where the VARCHAR datatype has a lot of flags, we see:

AssertionError: Models and migration scripts aren't in sync:
[ [ ( 'modify_type',
None,
'instance_type_extra_specs',
'key',
{ 'existing_comment': None,
'existing_nullable': True,
'existing_server_default': False},
VARCHAR(collation='utf8_bin', length=255),
String(length=255))],
[ ( 'modify_type',
None,
'instance_type_extra_specs',
'value',
{ 'existing_comment': None,
'existing_nullable': True,
'existing_server_default': False},
VARCHAR(collation='utf8_bin', length=255),
String(length=255))],
[ ( 'modify_type',
None,
'resource_providers',
'name',
{ 'existing_comment': None,
'existing_nullable': True,
'existing_server_default': False},
VARCHAR(charset='utf8', length=200),
Unicode(length=200))]]

those are all false positives; Unicode / String render VARCHAR on MySQL and there are default values for collation and charset. I think a good rule here might be if the DB-reflected column has some flags that are set, and the model-side has no flag set for that value at all, that means no change.

so this opens up a new subject area which is that when we add new comparison features to autogenerate, especially when we added indexes/unique constraints, the vast majority of issues reported were related to false positives. The index/unique thing, I must have had 20 issues over the course of several years with that. So, we might want to build this so that it's being very conservative about reporting a change based on keyword arguments that are different.

as always, let me know how you are doing and if you have motivation / resources to keep going. I can always pick up sooner rather than later if need be but I am glad if you'd like to keep going!

@pbecotte
Copy link
Contributor Author

pbecotte commented Nov 9, 2019

That is a WAY better idea. Felt pretty ugly adding in all those default keywords into the synonyms list, and I knew I would miss a bunch. But only comparing keywords if they BOTH have them is exactly the right thing to do!

I can add {"string", "varchar2"} to the oracle impl... but I don't know how to test against oracle. they don't seem to have an easily used docker container? I haven't touched oracle since about 2012 :)

I pushed up an update. I have been testing with postgresql/mysql/sqlite. I think this version of the code is a little easier to follow as well... but still feels too complicated?

@pbecotte
Copy link
Contributor Author

pbecotte commented Nov 9, 2019

Oh, question- I made the assumption that ( is always the split between the type and any keywords- does that hold true across all the databases/types?

@zzzeek
Copy link
Member

zzzeek commented Nov 9, 2019

Oh, question- I made the assumption that ( is always the split between the type and any keywords- does that hold true across all the databases/types?

as far as I know, sure. I would think that all the logic here would be overridable by a subclass impl if they needed to get in there and change things.

@pbecotte pbecotte force-pushed the ALEMBIC-605 branch 3 times, most recently from a453c9e to 94e7ad0 Compare November 11, 2019 12:31
Copy link
Collaborator

@sqla-tester sqla-tester left a 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 to try to get revision 94e7ad0 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset 94e7ad0 added to existing Gerrit review https://gerrit.sqlalchemy.org/#/c/sqlalchemy/alembic/+/1561

@pbecotte
Copy link
Contributor Author

Added the VARCHAR/VARCHAR2 synonym to oracle

Copy link
Collaborator

@sqla-tester sqla-tester left a 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 to try to get revision 8963755 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset 8963755 added to existing Gerrit review https://gerrit.sqlalchemy.org/#/c/sqlalchemy/alembic/+/1561

Copy link
Collaborator

@sqla-tester sqla-tester left a 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 to try to get revision 8963755 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset 8963755 added to existing Gerrit review https://gerrit.sqlalchemy.org/#/c/sqlalchemy/alembic/+/1561

Copy link
Collaborator

@sqla-tester sqla-tester left a 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 to try to get revision 17a39d6 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset 17a39d6 added to existing Gerrit review https://gerrit.sqlalchemy.org/#/c/sqlalchemy/alembic/+/1561

Copy link
Collaborator

@sqla-tester sqla-tester left a 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 to try to get revision 2edf464 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset 2edf464 added to existing Gerrit review https://gerrit.sqlalchemy.org/#/c/sqlalchemy/alembic/+/1561

Copy link
Collaborator

@sqla-tester sqla-tester left a 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 to try to get revision 2c9faf6 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset 2c9faf6 added to existing Gerrit review https://gerrit.sqlalchemy.org/#/c/sqlalchemy/alembic/+/1561

Copy link
Collaborator

@sqla-tester sqla-tester left a 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 to try to get revision b80f225 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset b80f225 added to existing Gerrit review https://gerrit.sqlalchemy.org/#/c/sqlalchemy/alembic/+/1561

Copy link
Collaborator

@sqla-tester sqla-tester left a 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 to try to get revision 97da348 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset 97da348 added to existing Gerrit review https://gerrit.sqlalchemy.org/#/c/sqlalchemy/alembic/+/1561

@@ -50,6 +52,30 @@ def pytest_pycollect_makeitem(collector, name, obj):
return []


def vendored_exclusions(compound_object):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As you mentioned, the testing construct isn't in sqlalchemy before 1.3. So I did this janky thing- though I think it may make more sense to just drop the support for older versions as you mentioned.

Copy link
Collaborator

@sqla-tester sqla-tester left a comment

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

(4 comments)

haven't reviewed the doc changes yet, just a few quickies...

@@ -39,6 +44,7 @@ class DefaultImpl(with_metaclass(ImplMeta)):

transactional_ddl = False
command_terminator = ";"
type_synonyms = [{"NUMERIC", "DECIMAL"}]
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

I tend to use tuples for things like this to make sure nothing is mutated...

@@ -50,6 +52,30 @@ def pytest_pycollect_makeitem(collector, name, obj):
return []


def vendored_exclusions(compound_object):
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

I've re-vendored this into alembic again in alembic/testing/exclusions.py so we can remove this

@@ -5,6 +5,10 @@


class DefaultRequirements(SuiteRequirements):
@property
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

the def name should be stated in terms of whatever it is that oracle is not doing, not the DB name itself

from alembic.util import CommandError
from ._autogen_fixtures import AutogenFixtureTest
from ._autogen_fixtures import AutogenTest
from ._autogen_fixtures import _default_object_filters, AutogenFixtureTest, \
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

this is all going to reformat w/ black...if you install the .pre-commit-config.yml hook for your next commit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

is the project switching to black? I had it in black format and undid it in some places to match the older style haha. I approve! will do

@sqla-tester
Copy link
Collaborator

mike bayer (zzzeek) wrote:

(1 comment)

pbecotte@github wrote:

alembic has been on black for many months now, actually the lines here would reformat because of the "zimports" tool I wrote first and foremost. Just turn on the pre-commit hooks and it will all be taken care of :).

Copy link
Collaborator

@sqla-tester sqla-tester left a comment

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

Code-Review-1

(9 comments)

OK I have a full review on here now.

)
if comparison is not None:
return not comparison
for meta, inspect in zip(inspected_params.args, meta_params.args):
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

do we know that these two .args are the same length? What if a VARCHAR has a default length that gets set up on the database side but isn't in the metadata? i think perhaps if len(meta_params.args) == 0, we skip this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason I picked zip was in case was in case the lists weren't of equal length. We only compare up to the length of the shorter list, ignoring differences in "extra" arguments (that may have been added by the server defaults). If the shorter list is len 0, this loop will just be skipped.

Copy link
Member

Choose a reason for hiding this comment

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

do we have any scenarios that exercise these cases? I';m not sure our datatypes have so much positional arguments for this to matter. I'm fine with it for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I do

if inspected_params.args and meta_params.args and inspected_params.args != meta_params.args:
    return False

AutogenerateVariantCompareTest_oracle+cx_oracle_11_2_0_2_0::test_variant_no_issue fails comparing BigInteger to BigInteger, while test_autogen_diffs.py::CompareMetadataToInspectorTest_oracle+cx_oracle_11_2_0_2_0::test_introspected_columns_match_metadata_columns[cola10] fails comparing Boolean to Boolean.
This is because the Oracle dialect prints those as NUMERIC(19) ... but the server adds precision to the returned value.

I added an extra test to be a bit more explicit with DECIMAL(10, 2) compared to NUMERIC(10)

specified (such as lengths, precisions, or enumeration members), they will be
compared as well. However, if keywords are only specified for one,
changes in these will be ignored. The type comparison logic is extensible to
work around these limitations, see :ref:`compare_types` for details.
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

add:

.. versionchanged:: 1.4 type comparison code has been enhanced to compare column types more deeply as well as to take arguments into account.

@@ -405,18 +407,24 @@ is set to True::
.. note::

The default type comparison logic (which is end-user extensible) currently
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

"as of Alembic version 1.4" just to make sure people see this

or ``TEXT``. Dialect implementations can have synonyms that are considered
equivalent- this is because some databases support types by converting them
to another type. For example, in ``NUMERIC`` and ``DECIMAL`` are considered
equivalent.
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

"for example, NUMERIC and DECIMAL are considered equivalent on all backends, while on the Oracle backend the additional synonyms BIGINT, INTEGER, NUMBER, SMALLINT are added to this list of equivalents"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good one!

* Next, the arguments within the type, such as the lengths of
strings, precision values for numerics, the elements inside of an
enumeration are compared. If BOTH columns have arguments AND they are
different, a change will be detected. If one column is jsut set to the
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

typo "just"

strings, precision values for numerics, the elements inside of an
enumeration are compared. If BOTH columns have arguments AND they are
different, a change will be detected. If one column is jsut set to the
default and the other has arguments, we don't currently detect this. The
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

"and the other has arguments, Alembic will pass on attempting to compare these. The rationale is that it is difficult to detect what a database backend sets as a default value without generating false positives".

reason here is that it can be hard to know what the database backend sets
as the default values, meaning that INTEGER() and INTEGER(10) could
actually be the same thing, and we don't want to regenerate a diff every
time.

Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

add a .. versionchanged:: 1.4 note here also

@@ -488,6 +496,13 @@ then a basic check for type equivalence is run.
.. versionadded:: 0.7.6 - added support for the ``compare_against_backend()``
method.

For a custom dialect, you could also specify ``impl.type_synonyms``. This
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

this is getting into people making their own "impls" which I think is out of scope for this document, so I would take this blurb out.

Integer(),
Numeric(8, 0),
True,
config.requirements.integer_subtype_comparisons,
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

this requirement apparently refers to just "is oracle", where we couldn't compare BigInteger to Integer. however, now that we compare the "scale" value, this should work now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately not with the other rules. Integer and Numeric come through as synonyms on Oracle, and the "metadata" column has no arguments...so we ignore the arguments on the "inspect" column.

…re the output text for the type to look for changes. In addition, allow

schemas to define sets of types that are functionally equivalent, such
as BOOL and TINYINT(1).
@pbecotte
Copy link
Contributor Author

mike bayer (zzzeek) wrote:

(1 comment)
pbecotte@github wrote:

alembic has been on black for many months now, actually the lines here would reformat because of the "zimports" tool I wrote first and foremost. Just turn on the pre-commit hooks and it will all be taken care of 👍

Ah, I see- only new lines are formatted, didn't realize!

Copy link
Collaborator

@sqla-tester sqla-tester left a 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 to try to get revision 1a6a386 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset 1a6a386 added to existing Gerrit review https://gerrit.sqlalchemy.org/#/c/sqlalchemy/alembic/+/1561

@sqla-tester
Copy link
Collaborator

mike bayer (zzzeek) wrote:

next thing this needs is a file docs/build/unreleased/619.rst that describes the change.

View this in Gerrit at https://gerrit.sqlalchemy.org/1561

@sqla-tester
Copy link
Collaborator

mike bayer (zzzeek) wrote:

Code-Review+2 Workflow+1

View this in Gerrit at https://gerrit.sqlalchemy.org/1561

@sqla-tester
Copy link
Collaborator

Gerrit review https://gerrit.sqlalchemy.org/1561 has been merged. Congratulations! :)

@CaselIT
Copy link
Member

CaselIT commented Aug 11, 2023

This PR has completely removed support for compare_against_backend, but it's still documented and supposedly tested (the test is clearly broken.)

@zzzeek For me we can just drop it since no one noticed

@CaselIT
Copy link
Member

CaselIT commented Aug 11, 2023

for reference it was originally added in dabc7f0

@zzzeek
Copy link
Member

zzzeek commented Aug 12, 2023

how come tests did not fail?

@CaselIT
Copy link
Member

CaselIT commented Aug 12, 2023

I haven't checked it. I'll open issue and fix this, also rewriting the tests

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.

4 participants