comparison roundup/backends/back_postgresql.py @ 3918:c8899c4bf6ad

safer session cleanup on postgres backends The logic is all from Erik Forsberg, so thank him (or blame him ;) for it I just moved it into the postgres backend to make it a little cleaner and am committing it after making sure it seems to work. This fixes [SF#1703116]
author Justus Pendleton <jpend@users.sourceforge.net>
date Tue, 25 Sep 2007 02:44:50 +0000
parents 1d47f70a99bf
children 416606b09b27
comparison
equal deleted inserted replaced
3917:1f3310c0a100 3918:c8899c4bf6ad
1 #$Id: back_postgresql.py,v 1.39 2007-08-09 08:58:42 schlatterbeck Exp $ 1 #$Id: back_postgresql.py,v 1.40 2007-09-25 02:44:50 jpend Exp $
2 # 2 #
3 # Copyright (c) 2003 Martynas Sklyzmantas, Andrey Lebedev <andrey@micro.lt> 3 # Copyright (c) 2003 Martynas Sklyzmantas, Andrey Lebedev <andrey@micro.lt>
4 # 4 #
5 # This module is free software, and you may redistribute it and/or modify 5 # This module is free software, and you may redistribute it and/or modify
6 # under the same terms as Python, so long as this copyright message and 6 # under the same terms as Python, so long as this copyright message and
11 11
12 import os, shutil, popen2, time 12 import os, shutil, popen2, time
13 try: 13 try:
14 import psycopg 14 import psycopg
15 from psycopg import QuotedString 15 from psycopg import QuotedString
16 from psycopg import ProgrammingError
16 except: 17 except:
17 from psycopg2 import psycopg1 as psycopg 18 from psycopg2 import psycopg1 as psycopg
18 from psycopg2.extensions import QuotedString 19 from psycopg2.extensions import QuotedString
20 from psycopg2.psycopg1 import ProgrammingError
19 import logging 21 import logging
20 22
21 from roundup import hyperdb, date 23 from roundup import hyperdb, date
22 from roundup.backends import rdbms_common 24 from roundup.backends import rdbms_common
25 from roundup.backends import sessions_rdbms
23 26
24 def connection_dict(config, dbnamestr=None): 27 def connection_dict(config, dbnamestr=None):
25 ''' read_default_group is MySQL-specific, ignore it ''' 28 ''' read_default_group is MySQL-specific, ignore it '''
26 d = rdbms_common.connection_dict(config, dbnamestr) 29 d = rdbms_common.connection_dict(config, dbnamestr)
27 if d.has_key('read_default_group'): 30 if d.has_key('read_default_group'):
49 '''Perform some sort of database-level command. Retry 10 times if we 52 '''Perform some sort of database-level command. Retry 10 times if we
50 fail by conflicting with another user. 53 fail by conflicting with another user.
51 ''' 54 '''
52 template1 = connection_dict(config) 55 template1 = connection_dict(config)
53 template1['database'] = 'template1' 56 template1['database'] = 'template1'
54 57
55 try: 58 try:
56 conn = psycopg.connect(**template1) 59 conn = psycopg.connect(**template1)
57 except psycopg.OperationalError, message: 60 except psycopg.OperationalError, message:
58 raise hyperdb.DatabaseError, message 61 raise hyperdb.DatabaseError, message
59 62
60 conn.set_isolation_level(0) 63 conn.set_isolation_level(0)
61 cursor = conn.cursor() 64 cursor = conn.cursor()
62 try: 65 try:
63 for n in range(10): 66 for n in range(10):
64 if pg_command(cursor, command): 67 if pg_command(cursor, command):
68 raise RuntimeError, '10 attempts to create database failed' 71 raise RuntimeError, '10 attempts to create database failed'
69 72
70 def pg_command(cursor, command): 73 def pg_command(cursor, command):
71 '''Execute the postgresql command, which may be blocked by some other 74 '''Execute the postgresql command, which may be blocked by some other
72 user connecting to the database, and return a true value if it succeeds. 75 user connecting to the database, and return a true value if it succeeds.
73 76
74 If there is a concurrent update, retry the command. 77 If there is a concurrent update, retry the command.
75 ''' 78 '''
76 try: 79 try:
77 cursor.execute(command) 80 cursor.execute(command)
78 except psycopg.ProgrammingError, err: 81 except psycopg.ProgrammingError, err:
102 conn.close() 105 conn.close()
103 return 1 106 return 1
104 except: 107 except:
105 return 0 108 return 0
106 109
110 class Sessions(sessions_rdbms.Sessions):
111 def set(*args, **kwargs):
112 try:
113 sessions_rdbms.Sessions.set(*args, **kwargs)
114 except ProgrammingError, err:
115 response = str(err).split('\n')[0]
116 if -1 != response.find('ERROR') and \
117 -1 != response.find('could not serialize access due to concurrent update'):
118 # another client just updated, and we're running on
119 # serializable isolation.
120 # see http://www.postgresql.org/docs/7.4/interactive/transaction-iso.html
121 self.db.rollback()
122
107 class Database(rdbms_common.Database): 123 class Database(rdbms_common.Database):
108 arg = '%s' 124 arg = '%s'
109 125
110 # used by some code to switch styles of query 126 # used by some code to switch styles of query
111 implements_intersect = 1 127 implements_intersect = 1
128
129 def getSessionManager(self):
130 return Sessions(self)
112 131
113 def sql_open_connection(self): 132 def sql_open_connection(self):
114 db = connection_dict(self.config, 'database') 133 db = connection_dict(self.config, 'database')
115 logging.getLogger('hyperdb').info('open database %r'%db['database']) 134 logging.getLogger('hyperdb').info('open database %r'%db['database'])
116 try: 135 try:
156 # full-text indexing store 175 # full-text indexing store
157 self.sql('CREATE SEQUENCE ___textids_ids') 176 self.sql('CREATE SEQUENCE ___textids_ids')
158 self.sql('''CREATE TABLE __textids ( 177 self.sql('''CREATE TABLE __textids (
159 _textid integer primary key, _class VARCHAR(255), 178 _textid integer primary key, _class VARCHAR(255),
160 _itemid VARCHAR(255), _prop VARCHAR(255))''') 179 _itemid VARCHAR(255), _prop VARCHAR(255))''')
161 self.sql('''CREATE TABLE __words (_word VARCHAR(30), 180 self.sql('''CREATE TABLE __words (_word VARCHAR(30),
162 _textid integer)''') 181 _textid integer)''')
163 self.sql('CREATE INDEX words_word_idx ON __words(_word)') 182 self.sql('CREATE INDEX words_word_idx ON __words(_word)')
164 self.sql('CREATE INDEX words_by_id ON __words (_textid)') 183 self.sql('CREATE INDEX words_by_id ON __words (_textid)')
165 self.sql('CREATE UNIQUE INDEX __textids_by_props ON ' 184 self.sql('CREATE UNIQUE INDEX __textids_by_props ON '
166 '__textids (_class, _itemid, _prop)') 185 '__textids (_class, _itemid, _prop)')
264 class IssueClass(PostgresqlClass, rdbms_common.IssueClass): 283 class IssueClass(PostgresqlClass, rdbms_common.IssueClass):
265 pass 284 pass
266 class FileClass(PostgresqlClass, rdbms_common.FileClass): 285 class FileClass(PostgresqlClass, rdbms_common.FileClass):
267 pass 286 pass
268 287
288 # vim: set et sts=4 sw=4

Roundup Issue Tracker: http://roundup-tracker.org/