Mercurial > p > roundup > code
comparison roundup/backends/back_postgresql.py @ 7714:b41750bf9f03
fix: figure out dbname when using pg_service.
See: https://issues.roundup-tracker.org/msg7890 where drop database
is missing the dbname.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Mon, 18 Dec 2023 16:56:49 -0500 |
| parents | 4af0d235b570 |
| children | 95f0002e85c4 |
comparison
equal
deleted
inserted
replaced
| 7713:2c1d30467909 | 7714:b41750bf9f03 |
|---|---|
| 48 return d | 48 return d |
| 49 | 49 |
| 50 | 50 |
| 51 def db_create(config): | 51 def db_create(config): |
| 52 """Clear all database contents and drop database itself""" | 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 if config.RDBMS_TEMPLATE: | 55 if config.RDBMS_TEMPLATE: |
| 55 command = command + " TEMPLATE=%s" % config.RDBMS_TEMPLATE | 56 command = command + " TEMPLATE=%s" % config.RDBMS_TEMPLATE |
| 56 logging.getLogger('roundup.hyperdb').info(command) | 57 logging.getLogger('roundup.hyperdb').info(command) |
| 57 db_command(config, command) | 58 db_command(config, command) |
| 58 | 59 |
| 59 | 60 |
| 60 def db_nuke(config): | 61 def db_nuke(config): |
| 61 """Clear all database contents and drop database itself""" | 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 logging.getLogger('roundup.hyperdb').info(command) | 65 logging.getLogger('roundup.hyperdb').info(command) |
| 64 db_command(config, command) | 66 db_command(config, command) |
| 65 | 67 |
| 66 if os.path.exists(config.DATABASE): | 68 if os.path.exists(config.DATABASE): |
| 67 shutil.rmtree(config.DATABASE) | 69 shutil.rmtree(config.DATABASE) |
| 68 | 70 |
| 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 | |
| 69 | 118 |
| 70 def db_command(config, command, database='postgres'): | 119 def db_command(config, command, database='postgres'): |
| 71 '''Perform some sort of database-level command. Retry 10 times if we | 120 '''Perform some sort of database-level command. Retry 10 times if we |
| 72 fail by conflicting with another user. | 121 fail by conflicting with another user. |
| 73 | 122 |
| 91 for _n in range(10): | 140 for _n in range(10): |
| 92 if pg_command(cursor, command): | 141 if pg_command(cursor, command): |
| 93 return | 142 return |
| 94 finally: | 143 finally: |
| 95 conn.close() | 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 def pg_command(cursor, command): | 148 def pg_command(cursor, command): |
| 100 '''Execute the postgresql command, which may be blocked by some other | 149 '''Execute the postgresql command, which may be blocked by some other |
| 101 user connecting to the database, and return a true value if it succeeds. | 150 user connecting to the database, and return a true value if it succeeds. |
