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

1.3.20 breaks certain Alembic patterns with ArgumentError: column object already assigned to table #5669

Closed
dtantsur opened this issue Oct 23, 2020 · 7 comments
Labels
question issue where a "fix" on the SQLAlchemy side is unlikely, hence more of a usage question

Comments

@dtantsur
Copy link

Describe the bug
The ironic-inspector project is using two instances of a similar pattern in its alembic migrations: add a column, then load a table, then do some data manipulations. Here is a concise example. This has worked up to and including 1.3.19 and fails in 1.3.20 with ArgumentError.

Expected behavior
I would be happy if it continued to work. Maybe we're doing something exceptionally stupid? Then some guidance is welcome. We are working around the problem by switching to reflection for Table.

To Reproduce
You can do it using the ironic-inspector code tree:

pip3 install --user tox
git clone https://opendev.org/openstack/ironic-inspector
cd ironic-inspector
git checkout 2e6f5ed25b738d186981ad21d13ecc1fbf2828e4 # before the workaround
tox -epy3 -- migrations

Downgrade SQLAlchemy to see it working again:

./.tox/py3/bin/pip install 'SQLAlchemy===1.3.19'
tox -epy3 -- migrations

Error

2020-10-23 12:28:44.148029 | ubuntu-focal |         "  File \"/opt/stack/bifrost/lib/python3.8/site-packages/ironic_inspector/migrations/versions/d00d6e3f38c4_change_created_finished_at_type.py\", line 46, in upgrade",
2020-10-23 12:28:44.148057 | ubuntu-focal |         "    t = sa.table('nodes', started_at, finished_at,",
2020-10-23 12:28:44.148094 | ubuntu-focal |         "  File \"<string>\", line 2, in table",
2020-10-23 12:28:44.148121 | ubuntu-focal |         "  File \"/opt/stack/bifrost/lib/python3.8/site-packages/sqlalchemy/sql/selectable.py\", line 1963, in __init__",
2020-10-23 12:28:44.148149 | ubuntu-focal |         "    self.append_column(c)",
2020-10-23 12:28:44.148210 | ubuntu-focal |         "  File \"/opt/stack/bifrost/lib/python3.8/site-packages/sqlalchemy/sql/selectable.py\", line 1984, in append_column",
2020-10-23 12:28:44.148243 | ubuntu-focal |         "    raise exc.ArgumentError(",
2020-10-23 12:28:44.148278 | ubuntu-focal |         "sqlalchemy.exc.ArgumentError: column object 'temp_started_at' already assigned to table 'nodes'"

Versions.

  • OS: Fedora 32, Ubuntu Focal
  • Python: 3.8.6, likely other.
  • SQLAlchemy: 1.3.20
  • Database: N/A
  • DBAPI: N/A

Have a nice day!

@dtantsur dtantsur added the requires triage New issue that requires categorization label Oct 23, 2020
@zzzeek zzzeek added question issue where a "fix" on the SQLAlchemy side is unlikely, hence more of a usage question and removed requires triage New issue that requires categorization labels Oct 23, 2020
@zzzeek
Copy link
Member

zzzeek commented Oct 23, 2020

Hi Dmitry -

first off I'm not sure if you're aware, I work on Openstack all day and this is an ironic-inspector bug, so normally what you can do here is make the launchpad bug and then find me on openstack freenode and I can help propose a new gerrit to fix.

the error here is that ironic-inspector is relying upon previously undefined and quite broken behavior which is that column objects allowed themselves to be attached to multiple tables, causing them to behave incorrectly. ironic-inspector will need to modify its code to ensure that each Column object used only against a single table or operation; the alembic op.add_column() operation internally attaches the given column to a table object in order to render the appropriate SQL. so these columns can't be repurposed after the fact.

@zzzeek
Copy link
Member

zzzeek commented Oct 23, 2020

here's a fix to the referenced code:

temp_started_at = lambda: sa.Column("temp_started_at", sa.types.DateTime,
                            nullable=True)
temp_finished_at = lambda: sa.Column("temp_finished_at", sa.types.DateTime,
                             nullable=True)

uuid = sa.Column("uuid", sa.String(36), primary_key=True)

op.add_column("nodes", temp_started_at())
op.add_column("nodes", temp_finished_at())

t = sa.table('nodes', started_at, finished_at,
             temp_started_at(), temp_finished_at(), uuid)

@zzzeek
Copy link
Member

zzzeek commented Oct 23, 2020

also at best this would be an Alembic bug, where alembic could perhaps copy the column before using it for op.add_column().

@dtantsur
Copy link
Author

@zzzeek I have indeed entirely forgotten that you work on openstack. Busy time..

Thank you for your explanation, I've applied it to https://review.opendev.org/759420.

openstack-mirroring pushed a commit to openstack/ironic-inspector that referenced this issue Oct 26, 2020
The pattern of adding a column and then reading a table with it
no longer works in SQLAlchemy 1.3.20. This has been reported
upstream [1] and apparently we're doing the wrong thing by reusing
a column object twice.

We need to disable the non-standalone job since it's really broken
now, and this fix is blocking bifrost.

[1] sqlalchemy/sqlalchemy#5669

Change-Id: I2fb07413e8f421f39b24acf1272771ee2097b195
openstack-mirroring pushed a commit to openstack/openstack that referenced this issue Oct 26, 2020
* Update ironic-inspector from branch 'master'
  - Fix database migrations and disable the non-standalone job
    
    The pattern of adding a column and then reading a table with it
    no longer works in SQLAlchemy 1.3.20. This has been reported
    upstream [1] and apparently we're doing the wrong thing by reusing
    a column object twice.
    
    We need to disable the non-standalone job since it's really broken
    now, and this fix is blocking bifrost.
    
    [1] sqlalchemy/sqlalchemy#5669
    
    Change-Id: I2fb07413e8f421f39b24acf1272771ee2097b195
openstack-mirroring pushed a commit to openstack/ironic-inspector that referenced this issue Oct 27, 2020
The pattern of adding a column and then reading a table with it
no longer works in SQLAlchemy 1.3.20. This has been reported
upstream [1] and apparently we're doing the wrong thing by reusing
a column object twice.

We need to disable the non-standalone job since it's really broken
now, and this fix is blocking bifrost.

[1] sqlalchemy/sqlalchemy#5669

Change-Id: I2fb07413e8f421f39b24acf1272771ee2097b195
(cherry picked from commit 5678f21)
@unexceptable
Copy link

@zzzeek Hey! We're actually hitting the same error in Gnocchi, but it's kinda unclear as to what the actual cause is, because we don't seem to be reusing things as far as I can find in the code.

This is the issue:
gnocchixyz/gnocchi#1080

And the two migrations that are throw errors are:
sqlalchemy.exc.ArgumentError: column object 'started_at_ts' already assigned to table 'resource' at: https://github.com/gnocchixyz/gnocchi/blob/master/gnocchi/indexer/alembic/versions/5c4f93e5bb4_mysql_float_to_timestamp.py
and
sqlalchemy.exc.ArgumentError: column object 'creator' already assigned to table 'resource' at https://github.com/gnocchixyz/gnocchi/blob/master/gnocchi/indexer/alembic/versions/aba5a217ca9b_merge_created_in_creator.py

@zzzeek
Copy link
Member

zzzeek commented Nov 3, 2020

@zzzeek Hey! We're actually hitting the same error in Gnocchi, but it's kinda unclear as to what the actual cause is, because we don't seem to be reusing things as far as I can find in the code.

This is the issue:
gnocchixyz/gnocchi#1080

And the two migrations that are throw errors are:
sqlalchemy.exc.ArgumentError: column object 'started_at_ts' already assigned to table 'resource' at: https://github.com/gnocchixyz/gnocchi/blob/master/gnocchi/indexer/alembic/versions/5c4f93e5bb4_mysql_float_to_timestamp.py

so just look for combinations of table() and add_column(), for that one it's this:

https://github.com/gnocchixyz/gnocchi/blob/master/gnocchi/indexer/alembic/versions/5c4f93e5bb4_mysql_float_to_timestamp.py#L66

    op.add_column(table_name, temp_col)
    t = sa.sql.table(table_name, existing_col, temp_col)

use a lambda for temp_col:

temp_col = lambda: sa.Column(
                column_name + "_ts",
                sqlalchemy_types.TimestampUTC(),
                nullable=True)
            op.add_column(table_name, temp_col())
            t = sa.sql.table(table_name, existing_col, temp_col())

and
sqlalchemy.exc.ArgumentError: column object 'creator' already assigned to table 'resource' at https://github.com/gnocchixyz/gnocchi/blob/master/gnocchi/indexer/alembic/versions/aba5a217ca9b_merge_created_in_creator.py

right here:

https://github.com/gnocchixyz/gnocchi/blob/master/gnocchi/indexer/alembic/versions/aba5a217ca9b_merge_created_in_creator.py#L42

basically anytime you are assigning a Column to a variable:

  my_col = Column(...)
  op.add_column(..., my_col)
  t = table(..., my_col)

change to a lambda:

  my_col = lambda: Column(...)
  op.add_column(..., my_col())
  t = table(..., my_col())

unexceptable added a commit to unexceptable/gnocchi that referenced this issue Nov 3, 2020
unexceptable added a commit to unexceptable/gnocchi that referenced this issue Nov 3, 2020
mergify bot pushed a commit to gnocchixyz/gnocchi that referenced this issue Nov 4, 2020
openstack-mirroring pushed a commit to openstack/magnum that referenced this issue Nov 5, 2020
The pattern of adding a column and then reading a table with it
no longer works in SQLAlchemy 1.3.20. This has been reported
upstream [1].

[1] sqlalchemy/sqlalchemy#5669

Change-Id: I5fd1deeef9cf70794bc61c101e1d7d4379d4b96b
openstack-mirroring pushed a commit to openstack/openstack that referenced this issue Nov 5, 2020
* Update magnum from branch 'master'
  - Fix database migrations
    
    The pattern of adding a column and then reading a table with it
    no longer works in SQLAlchemy 1.3.20. This has been reported
    upstream [1].
    
    [1] sqlalchemy/sqlalchemy#5669
    
    Change-Id: I5fd1deeef9cf70794bc61c101e1d7d4379d4b96b
@zzzeek
Copy link
Member

zzzeek commented Nov 5, 2020

we're going to fix this on the Alembic side as well as it's hitting me now too. see sqlalchemy/alembic#753

openstack-mirroring pushed a commit to openstack/ironic-inspector that referenced this issue Nov 17, 2020
The pattern of adding a column and then reading a table with it
no longer works in SQLAlchemy 1.3.20. This has been reported
upstream [1] and apparently we're doing the wrong thing by reusing
a column object twice.

We need to disable the non-standalone job since it's really broken
now, and this fix is blocking bifrost.

[1] sqlalchemy/sqlalchemy#5669

Change-Id: I2fb07413e8f421f39b24acf1272771ee2097b195
(cherry picked from commit 5678f21)
tobias-urdin pushed a commit to tobias-urdin/gnocchi that referenced this issue Dec 7, 2020
tobias-urdin pushed a commit to tobias-urdin/gnocchi that referenced this issue Dec 8, 2020
mergify bot pushed a commit to gnocchixyz/gnocchi that referenced this issue Dec 16, 2020
Closes: #1080

Related to: sqlalchemy/sqlalchemy#5669

(cherry picked from commit 5eeb3f0)
openstack-mirroring pushed a commit to openstack/ironic-inspector that referenced this issue Jan 20, 2021
The pattern of adding a column and then reading a table with it
no longer works in SQLAlchemy 1.3.20. This has been reported
upstream [1] and apparently we're doing the wrong thing by reusing
a column object twice.

We need to disable the non-standalone job since it's really broken
now, and this fix is blocking bifrost.

Also remove lower-constraints job.

Plus increase memory for grenade job.

And since tinyipa is not that tiny anymore, we increase the memory
for the base job to 512.

[1] sqlalchemy/sqlalchemy#5669

Change-Id: I2fb07413e8f421f39b24acf1272771ee2097b195
(cherry picked from commit 5678f21)
(cherry picked from commit c6fdf25)
(cherry picked from commit 1884ba81fd3d6166c626afd008beddaf0a33801e)
brtkwr pushed a commit to stackhpc/magnum that referenced this issue Feb 11, 2021
The pattern of adding a column and then reading a table with it
no longer works in SQLAlchemy 1.3.20. This has been reported
upstream [1].

[1] sqlalchemy/sqlalchemy#5669

Change-Id: I5fd1deeef9cf70794bc61c101e1d7d4379d4b96b
(cherry picked from commit f5cf6b9)
brtkwr pushed a commit to stackhpc/magnum that referenced this issue Feb 11, 2021
The pattern of adding a column and then reading a table with it
no longer works in SQLAlchemy 1.3.20. This has been reported
upstream [1].

[1] sqlalchemy/sqlalchemy#5669

Change-Id: I5fd1deeef9cf70794bc61c101e1d7d4379d4b96b
(cherry picked from commit f5cf6b9)
openstack-mirroring pushed a commit to openstack/magnum that referenced this issue Mar 1, 2021
On mysql 8, Boolean fields create constraints which later
make it impossible to alter the name of the column.
See: sqlalchemy/alembic#699

Per upstream alembic recommendation, do not create constraints
explicitly.
sqlalchemy/alembic#699 (comment)

story: 2008488
task: 41537

Signed-off-by: Spyros Trigazis <spyridon.trigazis@cern.ch>
(cherry picked from commit bcf771b)

Fix database migrations

The pattern of adding a column and then reading a table with it
no longer works in SQLAlchemy 1.3.20. This has been reported
upstream [1].

[1] sqlalchemy/sqlalchemy#5669

squashed with: I5fd1deeef9cf70794bc61c101e1d7d4379d4b96b
(cherry picked from commit f5cf6b9)

Change-Id: I51659c6e179d7e4e2cfc5be46348fac483d76e3b
openstack-mirroring pushed a commit to openstack/magnum that referenced this issue Mar 2, 2021
The pattern of adding a column and then reading a table with it
no longer works in SQLAlchemy 1.3.20. This has been reported
upstream [1].

[1] sqlalchemy/sqlalchemy#5669

Change-Id: I5fd1deeef9cf70794bc61c101e1d7d4379d4b96b
(cherry picked from commit f5cf6b9)
mergify bot pushed a commit to gnocchixyz/gnocchi that referenced this issue Mar 25, 2021
Closes: #1080

Related to: sqlalchemy/sqlalchemy#5669

(cherry picked from commit 5eeb3f0)
mergify bot pushed a commit to gnocchixyz/gnocchi that referenced this issue Mar 29, 2021
Closes: #1080

Related to: sqlalchemy/sqlalchemy#5669

(cherry picked from commit 5eeb3f0)
mergify bot pushed a commit to gnocchixyz/gnocchi that referenced this issue Mar 29, 2021
Closes: #1080

Related to: sqlalchemy/sqlalchemy#5669

(cherry picked from commit 5eeb3f0)
openstack-mirroring pushed a commit to openstack/magnum that referenced this issue Apr 9, 2021
On mysql 8, Boolean fields create constraints which later
make it impossible to alter the name of the column.
See: sqlalchemy/alembic#699

Per upstream alembic recommendation, do not create constraints
explicitly.
sqlalchemy/alembic#699 (comment)

story: 2008488
task: 41537

Signed-off-by: Spyros Trigazis <spyridon.trigazis@cern.ch>
(cherry picked from commit bcf771b)

Fix database migrations

The pattern of adding a column and then reading a table with it
no longer works in SQLAlchemy 1.3.20. This has been reported
upstream [1].

[1] sqlalchemy/sqlalchemy#5669

squashed with: I5fd1deeef9cf70794bc61c101e1d7d4379d4b96b
(cherry picked from commit f5cf6b9)

Change-Id: I51659c6e179d7e4e2cfc5be46348fac483d76e3b
(cherry picked from commit 210984f)

Conflicts:
magnum/db/sqlalchemy/alembic/versions/1d045384b966_add_insecure_baymodel_attr.py

Change-Id: Iba6f68822e4c7219f21ab2da252802dc7c3ca933
tanaypf9 pushed a commit to tanaypf9/pf9-requirements that referenced this issue May 20, 2024
Abandoned

Work around found, upstream bug report posted sqlalchemy/sqlalchemy#5669 but it's unclear if it's actually a bug.

Patch-set: 1
Status: abandoned
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question issue where a "fix" on the SQLAlchemy side is unlikely, hence more of a usage question
Projects
None yet
Development

No branches or pull requests

3 participants