Skip to content

Commit b51c445

Browse files
Allow adding and connecting the server using pgbouncer database aliases.#3564
1 parent adcf089 commit b51c445

File tree

7 files changed

+86
-12
lines changed

7 files changed

+86
-12
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
##########################################################################
2+
#
3+
# pgAdmin 4 - PostgreSQL Tools
4+
#
5+
# Copyright (C) 2013 - 2025, The pgAdmin Development Team
6+
# This software is released under the PostgreSQL Licence
7+
#
8+
##########################################################################
9+
10+
"""Update DB to version 43
11+
12+
Add new column 'db_alias' to server table.
13+
14+
Revision ID: 320114f59d9c
15+
Revises: 255e2842e4d7
16+
Create Date: 2025-02-21 16:25:50.934530
17+
18+
"""
19+
import sqlalchemy as sa
20+
from alembic import op
21+
22+
# revision identifiers, used by Alembic.
23+
revision = '320114f59d9c'
24+
down_revision = '255e2842e4d7'
25+
branch_labels = None
26+
depends_on = None
27+
28+
29+
def upgrade():
30+
op.add_column('server', sa.Column('db_alias', sa.String(length=256)))
31+
32+
33+
def downgrade():
34+
# pgAdmin only upgrades, downgrade not implemented.
35+
pass

web/pgadmin/browser/server_groups/servers/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,7 @@ def update(self, gid, sid):
738738
'host': 'host',
739739
'port': 'port',
740740
'db': 'maintenance_db',
741+
'db_alias': 'db_alias',
741742
'username': 'username',
742743
'gid': 'servergroup_id',
743744
'comment': 'comment',
@@ -767,6 +768,7 @@ def update(self, gid, sid):
767768
'name': gettext('name'),
768769
'port': gettext('Port'),
769770
'db': gettext('Maintenance database'),
771+
'db_alias': gettext('Database Alias '),
770772
'username': gettext('Username'),
771773
'comment': gettext('Comments'),
772774
'role': gettext('Role')
@@ -1014,6 +1016,7 @@ def properties(self, gid, sid):
10141016
'host': server.host,
10151017
'port': server.port,
10161018
'db': server.maintenance_db,
1019+
'db_alias': server.db_alias,
10171020
'shared': server.shared if config.SERVER_MODE else None,
10181021
'shared_username': server.shared_username
10191022
if config.SERVER_MODE else None,
@@ -1141,6 +1144,7 @@ def create(self, gid):
11411144
host=data.get('host', None),
11421145
port=data.get('port'),
11431146
maintenance_db=data.get('db', None),
1147+
db_alias=data.get('db_alias', None),
11441148
username=data.get('username'),
11451149
save_password=1 if data.get('save_password', False) and
11461150
config.ALLOW_SAVE_PASSWORD else 0,

web/pgadmin/browser/server_groups/servers/databases/__init__.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -360,18 +360,29 @@ def get_nodes(self, gid, sid, is_schema_diff=False):
360360
['%s'] * len(server_node_res.db_res.split(','))
361361
)
362362
params = tuple(server_node_res.db_res.split(','))
363-
SQL = render_template(
364-
"/".join([self.template_path, self._NODES_SQL]),
365-
last_system_oid=last_system_oid,
366-
db_restrictions=db_disp_res,
367-
)
363+
# if db_alias present, server is using pg_bouncer connection,
364+
# hence pass the did. It will retrieve current database
365+
if server_node_res and server_node_res.db_alias:
366+
SQL = render_template(
367+
"/".join([self.template_path, self._NODES_SQL]),
368+
last_system_oid=last_system_oid,
369+
db_restrictions=db_disp_res,
370+
did=server_node_res.did)
371+
else:
372+
SQL = render_template(
373+
"/".join([self.template_path, self._NODES_SQL]),
374+
last_system_oid=last_system_oid,
375+
db_restrictions=db_disp_res)
368376
status, rset = self.conn.execute_dict(SQL, params)
369377

370378
if not status:
371379
return internal_server_error(errormsg=rset)
372380

373381
for row in rset['rows']:
374-
dbname = row['name']
382+
# if db_alias then update db name to alias db_alias
383+
# instead of actual
384+
dbname = server_node_res.db_alias if server_node_res.db_alias \
385+
else row['name']
375386
row['is_sys_obj'] = (
376387
row['did'] <= self._DATABASE_LAST_SYSTEM_OID or
377388
self.datistemplate)
@@ -392,7 +403,7 @@ def get_nodes(self, gid, sid, is_schema_diff=False):
392403
self.blueprint.generate_browser_node(
393404
row['did'],
394405
sid,
395-
row['name'],
406+
label=dbname,
396407
icon=icon,
397408
connected=connected,
398409
tablespace=row['spcname'],

web/pgadmin/browser/server_groups/servers/static/js/server.ui.js

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export default class ServerSchema extends BaseUISchema {
148148
host: '',
149149
port: 5432,
150150
db: 'postgres',
151+
db_alias: undefined,
151152
username: current_user.name,
152153
role: null,
153154
connect_now: true,
@@ -315,8 +316,16 @@ export default class ServerSchema extends BaseUISchema {
315316
}
316317
},{
317318
id: 'db', label: gettext('Maintenance database'), type: 'text', group: gettext('Connection'),
318-
mode: ['properties', 'edit', 'create'], readonly: obj.isConnected, disabled: obj.isShared,
319-
noEmpty: true,
319+
mode: ['properties', 'edit', 'create'], readonly: obj.isConnected,
320+
disabled: function(state) {
321+
return !(isEmptyString(state.db_alias) && obj.isShared);
322+
},
323+
},{
324+
id: 'db_alias', label: gettext('Database alias'), type: 'text', group: gettext('Connection'),
325+
mode: ['properties', 'edit', 'create'], readonly: obj.isConnected,
326+
disabled: function(state) {
327+
return !isEmptyString(state.db);
328+
},
320329
},{
321330
id: 'username', label: gettext('Username'), type: 'text', group: gettext('Connection'),
322331
mode: ['properties', 'edit', 'create'],
@@ -545,8 +554,20 @@ export default class ServerSchema extends BaseUISchema {
545554
} else {
546555
setError('port', null);
547556
}
557+
558+
if(isEmptyString(state.db)) {
559+
errmsg = gettext('Maintainence database or Database alias must be specified.');
560+
if(isEmptyString(state.db_alias)) {
561+
setError('db', errmsg);
562+
return true;
563+
} else {
564+
setError('db', null);
565+
}
566+
} else {
567+
setError('db', null);
568+
}
548569
} else {
549-
_.each(['host', 'db', 'username', 'port'], (item) => {
570+
_.each(['host', 'db', 'db_alias', 'username', 'port'], (item) => {
550571
setError(item, null);
551572
});
552573
}

web/pgadmin/model/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
#
3434
##########################################################################
3535

36-
SCHEMA_VERSION = 42
36+
SCHEMA_VERSION = 43
3737

3838
##########################################################################
3939
#
@@ -162,6 +162,7 @@ class Server(db.Model):
162162
db.CheckConstraint('port >= 1 AND port <= 65534'),
163163
nullable=False)
164164
maintenance_db = db.Column(db.String(64), nullable=True)
165+
db_alias = db.Column(db.String(64), nullable=True)
165166
username = db.Column(db.String(64), nullable=False)
166167
password = db.Column(PgAdminDbBinaryString())
167168
save_password = db.Column(

web/pgadmin/utils/driver/psycopg3/server_manager.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def update(self, server):
7272
self.host = server.host
7373
self.port = server.port
7474
self.db = server.maintenance_db
75+
self.db_alias = server.db_alias
7576
self.shared = server.shared
7677
self.did = None
7778
self.user = server.username
@@ -209,7 +210,7 @@ def connection(self, **kwargs):
209210
else:
210211
conn_str = CONN_STRING.format(conn_id)
211212
if did is None:
212-
database = self.db
213+
database = self.db if self.db else self.db_alias
213214
elif did in self.db_info:
214215
database = self.db_info[did]['datname']
215216
elif conn_id and conn_str in self.connections:

web/regression/javascript/schema_ui_files/server.ui.spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ describe('ServerSchema', ()=>{
5555
expect(setError).toHaveBeenCalledWith('username', 'Username must be specified.');
5656

5757
state.username = 'postgres';
58+
state.db = 'postgres';
5859
schemaObj.validate(state, setError);
5960
expect(setError).toHaveBeenCalledWith('port', 'Port must be specified.');
6061

0 commit comments

Comments
 (0)