77'''Postgresql backend via psycopg2 for Roundup.'''
88__docformat__ = 'restructuredtext'
99
10- import os , shutil , time
10+ import logging
11+ import os
12+ import shutil
13+ import time
14+
1115ISOLATION_LEVEL_READ_UNCOMMITTED = None
1216ISOLATION_LEVEL_READ_COMMITTED = None
1317ISOLATION_LEVEL_REPEATABLE_READ = None
1418ISOLATION_LEVEL_SERIALIZABLE = None
1519
16- import psycopg2
17- from psycopg2 . extensions import QuotedString
18- from psycopg2 .extensions import ISOLATION_LEVEL_READ_UNCOMMITTED
19- from psycopg2 .extensions import ISOLATION_LEVEL_READ_COMMITTED
20- from psycopg2 .extensions import ISOLATION_LEVEL_REPEATABLE_READ
21- from psycopg2 .extensions import ISOLATION_LEVEL_SERIALIZABLE
22- from psycopg2 import ProgrammingError
23- from psycopg2 .extensions import TransactionRollbackError
20+ import psycopg2 # noqa: E402
21+ from psycopg2 import ProgrammingError # noqa: E402
22+ from psycopg2 .extensions import QuotedString # noqa: E402
23+ from psycopg2 .extensions import ISOLATION_LEVEL_READ_UNCOMMITTED # noqa: F401 E402
24+ from psycopg2 .extensions import ISOLATION_LEVEL_READ_COMMITTED # noqa: E402
25+ from psycopg2 .extensions import ISOLATION_LEVEL_REPEATABLE_READ # noqa: E402
26+ from psycopg2 . extensions import ISOLATION_LEVEL_SERIALIZABLE # noqa: E402
27+ from psycopg2 .extensions import TransactionRollbackError # noqa: F401 E402
2428
25- import logging
29+ from roundup import hyperdb # noqa: E402
30+ from roundup .backends import rdbms_common # noqa: E402
31+ from roundup .backends import sessions_rdbms # noqa: E402
2632
27- from roundup import hyperdb , date
28- from roundup .backends import rdbms_common
29- from roundup .backends import sessions_rdbms
33+ isolation_levels = {
34+ 'read uncommitted' : ISOLATION_LEVEL_READ_COMMITTED ,
35+ 'read committed' : ISOLATION_LEVEL_READ_COMMITTED ,
36+ 'repeatable read' : ISOLATION_LEVEL_REPEATABLE_READ ,
37+ 'serializable' : ISOLATION_LEVEL_SERIALIZABLE
38+ }
3039
31- isolation_levels = \
32- { 'read uncommitted' : ISOLATION_LEVEL_READ_COMMITTED
33- , 'read committed' : ISOLATION_LEVEL_READ_COMMITTED
34- , 'repeatable read' : ISOLATION_LEVEL_REPEATABLE_READ
35- , 'serializable' : ISOLATION_LEVEL_SERIALIZABLE
36- }
3740
3841def connection_dict (config , dbnamestr = None ):
3942 ''' read_default_group is MySQL-specific, ignore it '''
@@ -44,29 +47,32 @@ def connection_dict(config, dbnamestr=None):
4447 del d ['read_default_file' ]
4548 return d
4649
50+
4751def db_create (config ):
4852 """Clear all database contents and drop database itself"""
49- command = "CREATE DATABASE \" %s\" WITH ENCODING='UNICODE'" % config .RDBMS_NAME
53+ command = "CREATE DATABASE \" %s\" WITH ENCODING='UNICODE'" % config .RDBMS_NAME
5054 if config .RDBMS_TEMPLATE :
5155 command = command + " TEMPLATE=%s" % config .RDBMS_TEMPLATE
5256 logging .getLogger ('roundup.hyperdb' ).info (command )
5357 db_command (config , command )
5458
59+
5560def db_nuke (config ):
5661 """Clear all database contents and drop database itself"""
57- command = 'DROP DATABASE "%s"' % config .RDBMS_NAME
62+ command = 'DROP DATABASE "%s"' % config .RDBMS_NAME
5863 logging .getLogger ('roundup.hyperdb' ).info (command )
5964 db_command (config , command )
6065
6166 if os .path .exists (config .DATABASE ):
6267 shutil .rmtree (config .DATABASE )
6368
69+
6470def db_command (config , command , database = 'postgres' ):
6571 '''Perform some sort of database-level command. Retry 10 times if we
6672 fail by conflicting with another user.
6773
6874 Since PostgreSQL version 8.1 there is a database "postgres",
69- before "template1" seems to have been used, so we fall back to it.
75+ before "template1" seems to have been used, so we fall back to it.
7076 Compare to issue2550543.
7177 '''
7278 template1 = connection_dict (config )
@@ -82,13 +88,14 @@ def db_command(config, command, database='postgres'):
8288 conn .set_isolation_level (0 )
8389 cursor = conn .cursor ()
8490 try :
85- for n in range (10 ):
91+ for _n in range (10 ):
8692 if pg_command (cursor , command ):
8793 return
8894 finally :
8995 conn .close ()
9096 raise RuntimeError ('10 attempts to create database failed' )
9197
98+
9299def pg_command (cursor , command ):
93100 '''Execute the postgresql command, which may be blocked by some other
94101 user connecting to the database, and return a true value if it succeeds.
@@ -108,19 +115,21 @@ def pg_command(cursor, command):
108115 if msg in response :
109116 time .sleep (0.1 )
110117 return 0
111- raise RuntimeError (response )
118+ raise RuntimeError (response )
112119 return 1
113120
121+
114122def db_exists (config ):
115123 """Check if database already exists"""
116124 db = connection_dict (config , 'database' )
117125 try :
118126 conn = psycopg2 .connect (** db )
119127 conn .close ()
120128 return 1
121- except :
129+ except Exception :
122130 return 0
123131
132+
124133class Sessions (sessions_rdbms .Sessions ):
125134 def set (self , * args , ** kwargs ):
126135 try :
@@ -134,6 +143,7 @@ def set(self, *args, **kwargs):
134143 # see http://www.postgresql.org/docs/7.4/interactive/transaction-iso.html
135144 self .db .rollback ()
136145
146+
137147class Database (rdbms_common .Database ):
138148 """Postgres DB backend implementation
139149
@@ -154,15 +164,15 @@ class Database(rdbms_common.Database):
154164 def sql_open_connection (self ):
155165 db = connection_dict (self .config , 'database' )
156166 logging .getLogger ('roundup.hyperdb' ).info (
157- 'open database %r' % db ['database' ])
167+ 'open database %r' % db ['database' ])
158168 try :
159169 conn = psycopg2 .connect (** db )
160170 except psycopg2 .OperationalError as message :
161171 raise hyperdb .DatabaseError (message )
162172
163173 cursor = conn .cursor ()
164174 if ISOLATION_LEVEL_REPEATABLE_READ is not None :
165- lvl = isolation_levels [self .config .RDBMS_ISOLATION_LEVEL ]
175+ lvl = isolation_levels [self .config .RDBMS_ISOLATION_LEVEL ]
166176 conn .set_isolation_level (lvl )
167177
168178 return (conn , cursor )
@@ -265,22 +275,23 @@ def fix_version_2_tables(self):
265275
266276 # convert session / OTK *_time columns to REAL
267277 for name in ('otk' , 'session' ):
268- self .sql ('drop index %ss_key_idx' % name )
269- self .sql ('drop table %ss' % name )
278+ self .sql ('drop index %ss_key_idx' % name )
279+ self .sql ('drop table %ss' % name )
270280 self .sql ('''CREATE TABLE %ss (%s_key VARCHAR(255),
271- %s_value VARCHAR(255), %s_time REAL)''' % ( name , name , name ,
272- name ))
273- self .sql ('CREATE INDEX %ss_key_idx ON %ss(%s_key)' % (name , name ,
274- name ))
281+ %s_value VARCHAR(255), %s_time REAL)''' % ( name , name ,
282+ name , name ))
283+ self .sql ('CREATE INDEX %ss_key_idx ON %ss(%s_key)' % (name , name ,
284+ name ))
275285
276286 def fix_version_3_tables (self ):
277287 rdbms_common .Database .fix_version_3_tables (self )
278288 self .sql ('''CREATE INDEX words_both_idx ON public.__words
279289 USING btree (_word, _textid)''' )
280290
281291 def _add_fts_table (self ):
282- self .sql ('CREATE TABLE __fts (_class VARCHAR(255), '
283- '_itemid VARCHAR(255), _prop VARCHAR(255), _tsv tsvector)'
292+ self .sql (
293+ 'CREATE TABLE __fts (_class VARCHAR(255), '
294+ '_itemid VARCHAR(255), _prop VARCHAR(255), _tsv tsvector)'
284295 )
285296
286297 self .sql ('CREATE INDEX __fts_idx ON __fts USING GIN (_tsv)' )
@@ -308,7 +319,7 @@ def add_new_columns_v2(self):
308319 # update existing tables to have the new actor column
309320 tables = self .database_schema ['tables' ]
310321 for name in tables :
311- self .sql ('ALTER TABLE _%s add __actor VARCHAR(255)' % name )
322+ self .sql ('ALTER TABLE _%s add __actor VARCHAR(255)' % name )
312323
313324 def __repr__ (self ):
314325 return '<roundpsycopgsql 0x%x>' % id (self )
@@ -320,49 +331,56 @@ def sql_stringquote(self, value):
320331
321332 def sql_index_exists (self , table_name , index_name ):
322333 sql = 'select count(*) from pg_indexes where ' \
323- 'tablename=%s and indexname=%s' % (self .arg , self .arg )
334+ 'tablename=%s and indexname=%s' % (self .arg , self .arg )
324335 self .sql (sql , (table_name , index_name ))
325336 return self .cursor .fetchone ()[0 ]
326337
327338 def create_class_table (self , spec , create_sequence = 1 ):
328339 if create_sequence :
329- sql = 'CREATE SEQUENCE _%s_ids' % spec .classname
340+ sql = 'CREATE SEQUENCE _%s_ids' % spec .classname
330341 self .sql (sql )
331342
332343 return rdbms_common .Database .create_class_table (self , spec )
333344
334345 def drop_class_table (self , cn ):
335- sql = 'drop table _%s' % cn
346+ sql = 'drop table _%s' % cn
336347 self .sql (sql )
337348
338- sql = 'drop sequence _%s_ids' % cn
349+ sql = 'drop sequence _%s_ids' % cn
339350 self .sql (sql )
340351
341352 def newid (self , classname ):
342- sql = "select nextval('_%s_ids') from dual" % classname
353+ sql = "select nextval('_%s_ids') from dual" % classname
343354 self .sql (sql )
344355 return str (self .cursor .fetchone ()[0 ])
345356
346357 def setid (self , classname , setid ):
347- sql = "select setval('_%s_ids', %s) from dual" % (classname , int (setid ))
358+ sql = "select setval('_%s_ids', %s) from dual" % (classname ,
359+ int (setid ))
348360 self .sql (sql )
349361
350362 def clear (self ):
351363 rdbms_common .Database .clear (self )
352364
353365 # reset the sequences
354366 for cn in self .classes :
355- self .cursor .execute ('DROP SEQUENCE _%s_ids' % cn )
356- self .cursor .execute ('CREATE SEQUENCE _%s_ids' % cn )
367+ self .cursor .execute ('DROP SEQUENCE _%s_ids' % cn )
368+ self .cursor .execute ('CREATE SEQUENCE _%s_ids' % cn )
369+
357370
358371class PostgresqlClass :
359372 order_by_null_values = '(%s is not NULL)'
360373 case_insensitive_like = 'ILIKE'
361374
375+
362376class Class (PostgresqlClass , rdbms_common .Class ):
363377 pass
378+
379+
364380class IssueClass (PostgresqlClass , rdbms_common .IssueClass ):
365381 pass
382+
383+
366384class FileClass (PostgresqlClass , rdbms_common .FileClass ):
367385 pass
368386
0 commit comments