@@ -50,7 +50,8 @@ def connection_dict(config, dbnamestr=None):
5050
5151def 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
6061def 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+
70119def 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
99148def pg_command (cursor , command ):
0 commit comments