Skip to content

Commit bfc6342

Browse files
committed
fix: figure out dbname when using pg_service.
See: https://issues.roundup-tracker.org/msg7890 where drop database is missing the dbname.
1 parent 73afe62 commit bfc6342

File tree

1 file changed

+52
-3
lines changed

1 file changed

+52
-3
lines changed

roundup/backends/back_postgresql.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ def connection_dict(config, dbnamestr=None):
5050

5151
def db_create(config):
5252
"""Clear all database contents and drop database itself"""
53-
command = "CREATE DATABASE \"%s\" WITH ENCODING='UNICODE'" % config.RDBMS_NAME
53+
command = ("CREATE DATABASE \"%s\" WITH ENCODING='UNICODE'" %
54+
get_database_name(config))
5455
if config.RDBMS_TEMPLATE:
5556
command = command + " TEMPLATE=%s" % config.RDBMS_TEMPLATE
5657
logging.getLogger('roundup.hyperdb').info(command)
@@ -59,14 +60,62 @@ def db_create(config):
5960

6061
def db_nuke(config):
6162
"""Clear all database contents and drop database itself"""
62-
command = 'DROP DATABASE "%s"' % config.RDBMS_NAME
63+
command = 'DROP DATABASE "%s"' % get_database_name(config)
64+
6365
logging.getLogger('roundup.hyperdb').info(command)
6466
db_command(config, command)
6567

6668
if os.path.exists(config.DATABASE):
6769
shutil.rmtree(config.DATABASE)
6870

6971

72+
def get_database_name(config):
73+
'''Get database name using config.RDBMS_NAME or config.RDBMS_SERVICE.
74+
75+
If database specifed using RDBMS_SERVICE does not exist,
76+
the error message is parsed for the database name. This
77+
will fail if the error message changes. The alternative is
78+
to try to find and parse the .pg_service .ini style file on
79+
unix/windows. This is less palatable.
80+
81+
If the database specified using RDBMS_SERVICE does exist, (i.e. we
82+
are doing a nuke operation), use psycopg.extenstion.ConnectionInfo
83+
to get the dbname. This requires psycopg2 > 2.8 from 2018.
84+
'''
85+
86+
if config.RDBMS_NAME:
87+
return config.RDBMS_NAME
88+
89+
template1 = connection_dict(config)
90+
try:
91+
conn = psycopg2.connect(**template1)
92+
except psycopg2.OperationalError as message:
93+
import re
94+
# extract db name from error:
95+
# 'connection to server at "127.0.0.1", port 5432 failed: \
96+
# FATAL: database "rounduptest" does not exist\n'
97+
# ugh.
98+
#
99+
# Database name is any character sequence not including a " or
100+
# whitespace. Arguably both are allowed by:
101+
#
102+
# https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
103+
#
104+
# with suitable quoting but ... really.
105+
search = re.search(
106+
'FATAL:\s+database\s+"([^"\s]*)"\s+does\s+not\s+exist',
107+
message.args[0])
108+
if search:
109+
dbname = search.groups()[0]
110+
return dbname
111+
112+
raise hyperdb.DatabaseError(
113+
"Unable to determine database from service: %s" % message)
114+
115+
dbname = psycopg2.extensions.ConnectionInfo(conn).dbname
116+
conn.close()
117+
return dbname
118+
70119
def db_command(config, command, database='postgres'):
71120
'''Perform some sort of database-level command. Retry 10 times if we
72121
fail by conflicting with another user.
@@ -93,7 +142,7 @@ def db_command(config, command, database='postgres'):
93142
return
94143
finally:
95144
conn.close()
96-
raise RuntimeError('10 attempts to create database failed')
145+
raise RuntimeError('10 attempts to create database failed when running: %s' % commandb)
97146

98147

99148
def pg_command(cursor, command):

0 commit comments

Comments
 (0)