Skip to content

Commit 303f6a2

Browse files
asheshvdpage
authored andcommitted
Use resolve_fks=False in migration reflect() so stale FKs don't crash startup (pgadmin-org#9976)
An old configuration database (e.g. a pre-9.0 pgadmin4.db lingering in %appdata%) can carry a stale foreign key such as server.user_id -> user_old, a table removed long ago. SQLAlchemy's MetaData.reflect() defaults to resolve_fks=True, which auto-follows the reflected table's foreign keys and reflects their targets too; the orphan target trips NoSuchTableError and aborts the startup migration, surfacing in the GUI as the misleading "Server could not be contacted". None of the migrations use the FK-target tables -- each operates only on the explicitly requested table(s). Pass resolve_fks=False at all 14 meta.reflect() call sites across the 12 migration files so reflection no longer cascades into broken FK targets. Behaviour is unchanged for healthy databases. Adds a 9.16 release note.
1 parent 8afe14e commit 303f6a2

13 files changed

Lines changed: 20 additions & 15 deletions

docs/en_US/release_notes_9_16.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,4 @@ Bug fixes
3737
| `Issue #9935 <https://github.com/pgadmin-org/pgadmin4/issues/9935>`_ - Fix "Illegal instruction" crash on startup of the Linux DEB and RPM packages on older x86_64 CPUs by pinning the psycopg C extension build to the x86-64 baseline.
3838
| `Issue #9936 <https://github.com/pgadmin-org/pgadmin4/issues/9936>`_ - Fix the AI panel silently falling back to the default provider when a custom LLM API URL or key file was set, and allow self-hosted LLM endpoints on any loopback port.
3939
| `Issue #9939 <https://github.com/pgadmin-org/pgadmin4/issues/9939>`_ - Fix saving a newly-added row in the Query Tool failing when the result set includes expression or alias columns that are not real columns of the underlying table.
40+
| `Issue #9976 <https://github.com/pgadmin-org/pgadmin4/issues/9976>`_ - Fix a startup migration crash (NoSuchTableError) when an old configuration database contains a stale foreign-key reference.

web/migrations/versions/09d53fca90c7_.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def upgrade():
125125
# get metadata from current connection
126126
meta = sa.MetaData()
127127
# define table representation
128-
meta.reflect(op.get_bind(), only=('role',))
128+
meta.reflect(op.get_bind(), only=('role',), resolve_fks=False)
129129
role_table = sa.Table('role', meta)
130130

131131
op.execute(
@@ -168,7 +168,7 @@ def upgrade():
168168
# get metadata from current connection
169169
meta = sa.MetaData()
170170
# define table representation
171-
meta.reflect(op.get_bind(), only=('version',))
171+
meta.reflect(op.get_bind(), only=('version',), resolve_fks=False)
172172
version_table = sa.Table('version', meta)
173173

174174
op.execute(

web/migrations/versions/1f0eddc8fc79_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def upgrade():
3636
# get metadata from current connection
3737
meta = sa.MetaData()
3838
# define table representation
39-
meta.reflect(op.get_bind(), only=('role',))
39+
meta.reflect(op.get_bind(), only=('role',), resolve_fks=False)
4040
role_table = sa.Table('role', meta)
4141

4242
from pgadmin.tools.user_management.PgAdminPermissions import (

web/migrations/versions/44b9ce549393_.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ def upgrade():
3939
# get metadata from current connection
4040
meta = sa.MetaData()
4141
# define table representation
42-
meta.reflect(op.get_bind(), only=('server', 'sharedserver'))
42+
meta.reflect(op.get_bind(),
43+
only=('server', 'sharedserver'),
44+
resolve_fks=False)
4345
table = sa.Table('server', meta)
4446
op.execute(
4547
table.update().values(prepare_threshold=5)

web/migrations/versions/7fedf8531802_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def upgrade():
3737
# For internal email is a user name, so update the existing records.
3838
meta = sa.MetaData()
3939
# define table representation
40-
meta.reflect(op.get_bind(), only=('user',))
40+
meta.reflect(op.get_bind(), only=('user',), resolve_fks=False)
4141
user_table = sa.Table('user', meta)
4242

4343
op.execute(

web/migrations/versions/ac2c2e27dc2d_.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def upgrade():
3333
session.commit()
3434

3535
meta = sa.MetaData()
36-
meta.reflect(op.get_bind(), only=('user_macros',))
36+
meta.reflect(op.get_bind(), only=('user_macros',), resolve_fks=False)
3737
user_macros_table = sa.Table('user_macros', meta)
3838

3939
# Create a select statement
@@ -61,7 +61,7 @@ def upgrade():
6161
sa.PrimaryKeyConstraint('id',))
6262

6363
# Reflect the new table structure
64-
meta.reflect(op.get_bind(), only=('user_macros',))
64+
meta.reflect(op.get_bind(), only=('user_macros',), resolve_fks=False)
6565
new_user_macros_table = sa.Table('user_macros', meta)
6666

6767
# Bulk insert the fetched data into the new user_macros table

web/migrations/versions/add_tools_ai_perm_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
def upgrade():
2828
# Get metadata from current connection
2929
meta = sa.MetaData()
30-
meta.reflect(op.get_bind(), only=('role',))
30+
meta.reflect(op.get_bind(), only=('role',), resolve_fks=False)
3131
role_table = sa.Table('role', meta)
3232

3333
# Get all roles with permissions

web/migrations/versions/aff1436e3c8c_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def upgrade():
2929
# get metadata from current connection
3030
meta = sa.MetaData()
3131
# define table representation
32-
meta.reflect(op.get_bind(), only=('server',))
32+
meta.reflect(op.get_bind(), only=('server',), resolve_fks=False)
3333
server_table = sa.Table('server', meta)
3434
op.execute(
3535
server_table.update().where(server_table.c.connect_timeout == 0 or

web/migrations/versions/c465fee44968_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def upgrade():
3131

3232
meta = sa.MetaData()
3333
# define table representation
34-
meta.reflect(op.get_bind(), only=('user',))
34+
meta.reflect(op.get_bind(), only=('user',), resolve_fks=False)
3535
user_table = sa.Table('user', meta)
3636

3737
op.execute(

web/migrations/versions/c62bcc14c3d6_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
def upgrade():
2828
# Add 'change_password' permission to all roles except 'Administrator'.
2929
meta = sa.MetaData()
30-
meta.reflect(op.get_bind(), only=('role', 'setting'))
30+
meta.reflect(op.get_bind(), only=('role', 'setting'), resolve_fks=False)
3131
role_table = sa.Table('role', meta)
3232

3333
perm = role_table.c.permissions

0 commit comments

Comments
 (0)