Skip to content

fix: avoid NameError in check_external_config_db when the DB is unreachable#10052

Open
dpage wants to merge 1 commit into
pgadmin-org:masterfrom
dpage:fix/check-external-config-db-finally
Open

fix: avoid NameError in check_external_config_db when the DB is unreachable#10052
dpage wants to merge 1 commit into
pgadmin-org:masterfrom
dpage:fix/check-external-config-db-finally

Conversation

@dpage

@dpage dpage commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Problem

check_external_config_db() (used by the Docker entrypoint to decide whether to run first-launch user setup) closes its connection in a finally block:

try:
    connection = engine.connect()
    ...
except Exception:
    return False
finally:
    connection.close()

When engine.connect() itself fails — e.g. the external config database is unreachable — the connection local is never bound, so the finally raises UnboundLocalError, which propagates out and masks the intended return False.

Surfaced while reviewing #10009 (the entrypoint now tolerates a non-zero exit from this script, but the function should still behave correctly on its own).

Fix

Use the connection as a context manager (closed on every path, no unbound-name reference) and dispose the engine in finally:

with engine.connect():
    return inspect(engine).has_table("server")

Verification (live PostgreSQL 18)

Case Before After
Reachable, no server table False False
Reachable, server table present True True
Unreachable (bad port) UnboundLocalError False
  • pycodestyle clean.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed a crash that occurred during first-launch setup when attempting to check an unreachable external configuration database, allowing the setup process to proceed successfully.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR fixes a crash that occurs when checking an unreachable external configuration database during pgAdmin startup. The fix replaces manual connection handling with a context manager to prevent a secondary NameError that could mask proper exception handling. A release note documents the issue fix.

Changes

Connection Lifecycle Safety Refactoring

Layer / File(s) Summary
Connection lifecycle safety improvement and release notes
web/pgadmin/utils/check_external_config_db.py, docs/en_US/release_notes_9_16.rst
The check_external_config_db function replaces manual engine.connect() with a with context manager to prevent NameError when connection fails. Engine disposal remains in the outer finally block. Release notes document the crash fix for unreachable external database checks (Issue #10052).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main fix: avoiding a NameError in check_external_config_db when the database is unreachable, which aligns perfectly with the primary change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…chable

check_external_config_db() closed the connection in a finally block, but
when engine.connect() itself failed (e.g. an unreachable external config
database) the `connection` local was never bound, so the finally raised
UnboundLocalError and masked the intended `return False`.

Use the connection as a context manager so it is closed on every path
without referencing an unbound name, and dispose the engine in finally.
The function now returns False cleanly on connection failure.
@dpage dpage force-pushed the fix/check-external-config-db-finally branch from 412dda1 to 52016df Compare June 9, 2026 13:46

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
web/pgadmin/utils/check_external_config_db.py (1)

20-25: ⚡ Quick win

Consider binding and using the connection explicitly.

The context manager opens a connection but doesn't bind it to a variable, then inspect(engine) is called which may obtain a different connection from the pool. While the current code works correctly, it would be clearer to bind the connection and pass it directly to inspect().

♻️ Proposed refactor for clarity
-        # The context manager closes the connection on every path. The
-        # previous "finally: connection.close()" raised NameError when
-        # engine.connect() itself failed (e.g. an unreachable database),
-        # masking the intended "return False".
-        with engine.connect():
-            return inspect(engine).has_table("server")
+        # The context manager closes the connection on every path. The
+        # previous "finally: connection.close()" raised NameError when
+        # engine.connect() itself failed (e.g. an unreachable database),
+        # masking the intended "return False".
+        with engine.connect() as conn:
+            return inspect(conn).has_table("server")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/pgadmin/utils/check_external_config_db.py` around lines 20 - 25, Bind the
connection returned by engine.connect() and pass that connection into inspect()
instead of inspecting the engine directly; specifically, replace the unbound
context manager use with a bound one (e.g. with engine.connect() as conn:) and
call inspect(conn).has_table("server") so the same connection is used and closed
by the context manager (refer to engine.connect() and inspect()).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@web/pgadmin/utils/check_external_config_db.py`:
- Around line 20-25: Bind the connection returned by engine.connect() and pass
that connection into inspect() instead of inspecting the engine directly;
specifically, replace the unbound context manager use with a bound one (e.g.
with engine.connect() as conn:) and call inspect(conn).has_table("server") so
the same connection is used and closed by the context manager (refer to
engine.connect() and inspect()).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7faddb6b-25a0-4c62-94b6-e0194900ba51

📥 Commits

Reviewing files that changed from the base of the PR and between c88c8f6 and 52016df.

📒 Files selected for processing (2)
  • docs/en_US/release_notes_9_16.rst
  • web/pgadmin/utils/check_external_config_db.py

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.

1 participant