Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion web/pgadmin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from pgadmin.model import db, Role, Server, SharedServer, ServerGroup, \
User, Keys, Version, SCHEMA_VERSION as CURRENT_SCHEMA_VERSION
from pgadmin.utils import PgAdminModule, driver, KeyManager, heartbeat
from pgadmin.utils.db_utils import normalize_database_uri
from pgadmin.utils.preferences import Preferences
from pgadmin.utils.session import create_session_interface, pga_unauthorised
from pgadmin.utils.versioned_template_loader import VersionedTemplateLoader
Expand Down Expand Up @@ -337,7 +338,8 @@ def get_locale():
##########################################################################
if config.CONFIG_DATABASE_URI is not None and \
len(config.CONFIG_DATABASE_URI) > 0:
app.config['SQLALCHEMY_DATABASE_URI'] = config.CONFIG_DATABASE_URI
app.config['SQLALCHEMY_DATABASE_URI'] = \
normalize_database_uri(config.CONFIG_DATABASE_URI)
else:
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///{0}?timeout={1}' \
.format(config.SQLITE_PATH.replace('\\', '/'),
Expand Down
9 changes: 5 additions & 4 deletions web/pgadmin/evaluate_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import keyring
import importlib.util

from pgadmin.utils.db_utils import normalize_database_uri

# User configs loaded from config_local, config_distro etc.
custom_config_settings = {}

Expand Down Expand Up @@ -114,10 +116,9 @@ def evaluate_and_patch_config(config: dict) -> dict:

# To use psycopg3 driver, need to specify +psycopg in conn URI
if 'CONFIG_DATABASE_URI' in custom_config_settings:
db_uri = custom_config_settings['CONFIG_DATABASE_URI']
if db_uri.startswith('postgresql:'):
custom_config_settings['CONFIG_DATABASE_URI'] = \
'postgresql+psycopg:{0}'.format(db_uri[db_uri.find(':') + 1:])
custom_config_settings['CONFIG_DATABASE_URI'] = \
normalize_database_uri(
custom_config_settings['CONFIG_DATABASE_URI'])

# Finally update config user configs
config.update(custom_config_settings)
Expand Down
7 changes: 7 additions & 0 deletions web/pgadmin/utils/check_external_config_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#
##########################################################################

import re

from sqlalchemy import create_engine, inspect


Expand All @@ -15,6 +17,11 @@ def check_external_config_db(database_uri):
Check if external config database exists if it
is being used.
"""
if database_uri.startswith(("postgresql://", "postgres://")):
database_uri = re.sub(
r"^postgres(ql)?://", "postgresql+psycopg://",
database_uri, count=1
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
engine = create_engine(database_uri)
try:
connection = engine.connect()
Expand Down
20 changes: 20 additions & 0 deletions web/pgadmin/utils/db_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2026, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################

import re


def normalize_database_uri(uri):
"""
Rewrite a bare postgres(ql):// URI to use the psycopg3 driver scheme
postgresql+psycopg://. URIs that already carry a driver specifier
(e.g. postgresql+psycopg://) are returned unchanged.
"""
return re.sub(r"^postgres(?:ql)?://",
"postgresql+psycopg://", uri, count=1)
58 changes: 58 additions & 0 deletions web/pgadmin/utils/tests/test_db_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2026, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################

"""Unit tests for pgadmin.utils.db_utils.normalize_database_uri."""

import unittest

from pgadmin.utils.db_utils import normalize_database_uri
from pgadmin.utils.route import BaseTestGenerator


class TestNormalizeDatabaseUri(BaseTestGenerator):
"""normalize_database_uri must rewrite bare postgres(ql):// to
postgresql+psycopg:// and leave everything else unchanged."""

# Bypass the server-connection setUp in BaseTestGenerator.
def setUp(self):
unittest.TestCase.setUp(self)

scenarios = [
(
'postgres:// is rewritten to postgresql+psycopg://',
{
'uri': 'postgres://u:p@h/db',
'expected': 'postgresql+psycopg://u:p@h/db',
}
),
(
'postgresql:// is rewritten to postgresql+psycopg://',
{
'uri': 'postgresql://u:p@h/db',
'expected': 'postgresql+psycopg://u:p@h/db',
}
),
(
'postgresql+psycopg:// is returned unchanged (idempotent)',
{
'uri': 'postgresql+psycopg://u:p@h/db',
'expected': 'postgresql+psycopg://u:p@h/db',
}
),
(
'sqlite:/// is returned unchanged',
{
'uri': 'sqlite:///path/to/db.sqlite3',
'expected': 'sqlite:///path/to/db.sqlite3',
}
),
]

def runTest(self):
self.assertEqual(normalize_database_uri(self.uri), self.expected)
Loading