Mercurial > p > roundup > code
comparison roundup/backends/back_postgresql.py @ 5751:5cb6e6b594b0
issue2551040: New release of psycopg2 drops support for psycopg1
First try at suport for psycopg2. Change .travis.yml to install newest
psycopg2 without psycopg1 support.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Sat, 01 Jun 2019 15:47:34 -0400 |
| parents | 62de601bdf6f |
| children | 6a6b4651be1f |
comparison
equal
deleted
inserted
replaced
| 5750:2c0f89edabe1 | 5751:5cb6e6b594b0 |
|---|---|
| 2 # | 2 # |
| 3 # This module is free software, and you may redistribute it and/or modify | 3 # This module is free software, and you may redistribute it and/or modify |
| 4 # under the same terms as Python, so long as this copyright message and | 4 # under the same terms as Python, so long as this copyright message and |
| 5 # disclaimer are retained in their original form. | 5 # disclaimer are retained in their original form. |
| 6 # | 6 # |
| 7 '''Postgresql backend via psycopg for Roundup.''' | 7 '''Postgresql backend via psycopg2 for Roundup.''' |
| 8 __docformat__ = 'restructuredtext' | 8 __docformat__ = 'restructuredtext' |
| 9 | 9 |
| 10 import os, shutil, time | 10 import os, shutil, time |
| 11 ISOLATION_LEVEL_READ_UNCOMMITTED = None | 11 ISOLATION_LEVEL_READ_UNCOMMITTED = None |
| 12 ISOLATION_LEVEL_READ_COMMITTED = None | 12 ISOLATION_LEVEL_READ_COMMITTED = None |
| 13 ISOLATION_LEVEL_REPEATABLE_READ = None | 13 ISOLATION_LEVEL_REPEATABLE_READ = None |
| 14 ISOLATION_LEVEL_SERIALIZABLE = None | 14 ISOLATION_LEVEL_SERIALIZABLE = None |
| 15 | 15 |
| 16 from psycopg2 import psycopg1 as psycopg | 16 import psycopg2 |
| 17 from psycopg2.extensions import QuotedString | 17 from psycopg2.extensions import QuotedString |
| 18 from psycopg2.extensions import ISOLATION_LEVEL_READ_UNCOMMITTED | 18 from psycopg2.extensions import ISOLATION_LEVEL_READ_UNCOMMITTED |
| 19 from psycopg2.extensions import ISOLATION_LEVEL_READ_COMMITTED | 19 from psycopg2.extensions import ISOLATION_LEVEL_READ_COMMITTED |
| 20 from psycopg2.extensions import ISOLATION_LEVEL_REPEATABLE_READ | 20 from psycopg2.extensions import ISOLATION_LEVEL_REPEATABLE_READ |
| 21 from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE | 21 from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE |
| 22 from psycopg2.psycopg1 import ProgrammingError | 22 from psycopg2 import ProgrammingError |
| 23 from psycopg2.extensions import TransactionRollbackError | 23 from psycopg2.extensions import TransactionRollbackError |
| 24 | 24 |
| 25 import logging | 25 import logging |
| 26 | 26 |
| 27 from roundup import hyperdb, date | 27 from roundup import hyperdb, date |
| 64 def db_command(config, command, database='postgres'): | 64 def db_command(config, command, database='postgres'): |
| 65 '''Perform some sort of database-level command. Retry 10 times if we | 65 '''Perform some sort of database-level command. Retry 10 times if we |
| 66 fail by conflicting with another user. | 66 fail by conflicting with another user. |
| 67 | 67 |
| 68 Since PostgreSQL version 8.1 there is a database "postgres", | 68 Since PostgreSQL version 8.1 there is a database "postgres", |
| 69 before "template1" seems to habe been used, so we fall back to it. | 69 before "template1" seems to have been used, so we fall back to it. |
| 70 Compare to issue2550543. | 70 Compare to issue2550543. |
| 71 ''' | 71 ''' |
| 72 template1 = connection_dict(config) | 72 template1 = connection_dict(config) |
| 73 template1['database'] = database | 73 template1['database'] = database |
| 74 | 74 |
| 75 try: | 75 try: |
| 76 conn = psycopg.connect(**template1) | 76 conn = psycopg2.connect(**template1) |
| 77 except psycopg.OperationalError as message: | 77 except psycopg2.OperationalError as message: |
| 78 if str(message).find('database "postgres" does not exist') >= 0: | 78 if str(message).find('database "postgres" does not exist') >= 0: |
| 79 return db_command(config, command, database='template1') | 79 return db_command(config, command, database='template1') |
| 80 raise hyperdb.DatabaseError(message) | 80 raise hyperdb.DatabaseError(message) |
| 81 | 81 |
| 82 conn.set_isolation_level(0) | 82 conn.set_isolation_level(0) |
| 95 | 95 |
| 96 If there is a concurrent update, retry the command. | 96 If there is a concurrent update, retry the command. |
| 97 ''' | 97 ''' |
| 98 try: | 98 try: |
| 99 cursor.execute(command) | 99 cursor.execute(command) |
| 100 except psycopg.DatabaseError as err: | 100 except psycopg2.DatabaseError as err: |
| 101 response = str(err).split('\n')[0] | 101 response = str(err).split('\n')[0] |
| 102 if "FATAL" not in response : | 102 if "FATAL" not in response : |
| 103 msgs = ( | 103 msgs = ( |
| 104 'is being accessed by other users', | 104 'is being accessed by other users', |
| 105 'could not serialize access due to concurrent update', | 105 'could not serialize access due to concurrent update', |
| 113 | 113 |
| 114 def db_exists(config): | 114 def db_exists(config): |
| 115 """Check if database already exists""" | 115 """Check if database already exists""" |
| 116 db = connection_dict(config, 'database') | 116 db = connection_dict(config, 'database') |
| 117 try: | 117 try: |
| 118 conn = psycopg.connect(**db) | 118 conn = psycopg2.connect(**db) |
| 119 conn.close() | 119 conn.close() |
| 120 return 1 | 120 return 1 |
| 121 except: | 121 except: |
| 122 return 0 | 122 return 0 |
| 123 | 123 |
| 154 def sql_open_connection(self): | 154 def sql_open_connection(self): |
| 155 db = connection_dict(self.config, 'database') | 155 db = connection_dict(self.config, 'database') |
| 156 logging.getLogger('roundup.hyperdb').info( | 156 logging.getLogger('roundup.hyperdb').info( |
| 157 'open database %r'%db['database']) | 157 'open database %r'%db['database']) |
| 158 try: | 158 try: |
| 159 conn = psycopg.connect(**db) | 159 conn = psycopg2.connect(**db) |
| 160 except psycopg.OperationalError as message: | 160 except psycopg2.OperationalError as message: |
| 161 raise hyperdb.DatabaseError(message) | 161 raise hyperdb.DatabaseError(message) |
| 162 | 162 |
| 163 cursor = conn.cursor() | 163 cursor = conn.cursor() |
| 164 if ISOLATION_LEVEL_REPEATABLE_READ is not None: | 164 if ISOLATION_LEVEL_REPEATABLE_READ is not None: |
| 165 lvl = isolation_levels [self.config.RDBMS_ISOLATION_LEVEL] | 165 lvl = isolation_levels [self.config.RDBMS_ISOLATION_LEVEL] |
| 243 | 243 |
| 244 def __repr__(self): | 244 def __repr__(self): |
| 245 return '<roundpsycopgsql 0x%x>' % id(self) | 245 return '<roundpsycopgsql 0x%x>' % id(self) |
| 246 | 246 |
| 247 def sql_stringquote(self, value): | 247 def sql_stringquote(self, value): |
| 248 ''' psycopg.QuotedString returns a "buffer" object with the | 248 ''' psycopg2.QuotedString returns a "buffer" object with the |
| 249 single-quotes around it... ''' | 249 single-quotes around it... ''' |
| 250 return str(QuotedString(str(value)))[1:-1] | 250 return str(QuotedString(str(value)))[1:-1] |
| 251 | 251 |
| 252 def sql_index_exists(self, table_name, index_name): | 252 def sql_index_exists(self, table_name, index_name): |
| 253 sql = 'select count(*) from pg_indexes where ' \ | 253 sql = 'select count(*) from pg_indexes where ' \ |
