@@ -50,7 +50,8 @@ def connection_dict(config, dbnamestr=None):
50
50
51
51
def db_create (config ):
52
52
"""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 ))
54
55
if config .RDBMS_TEMPLATE :
55
56
command = command + " TEMPLATE=%s" % config .RDBMS_TEMPLATE
56
57
logging .getLogger ('roundup.hyperdb' ).info (command )
@@ -59,14 +60,62 @@ def db_create(config):
59
60
60
61
def db_nuke (config ):
61
62
"""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
+
63
65
logging .getLogger ('roundup.hyperdb' ).info (command )
64
66
db_command (config , command )
65
67
66
68
if os .path .exists (config .DATABASE ):
67
69
shutil .rmtree (config .DATABASE )
68
70
69
71
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
+
70
119
def db_command (config , command , database = 'postgres' ):
71
120
'''Perform some sort of database-level command. Retry 10 times if we
72
121
fail by conflicting with another user.
@@ -93,7 +142,7 @@ def db_command(config, command, database='postgres'):
93
142
return
94
143
finally :
95
144
conn .close ()
96
- raise RuntimeError ('10 attempts to create database failed' )
145
+ raise RuntimeError ('10 attempts to create database failed when running: %s' % commandb )
97
146
98
147
99
148
def pg_command (cursor , command ):
0 commit comments