From be47fc2da756e091f5da22b2dfd5ba1f2aa3d1d2 Mon Sep 17 00:00:00 2001
From: Axel Gembe <ago@bastart.eu.org>
Date: Thu, 26 Jun 2008 13:59:28 +0200
Subject: [PATCH 2/3] db: distinguish between read and write access
This makes the existing code use the .read and .write members of the database
pair to distinguish between read and write access. Note that if no write
database is specified, the same database connection as for reading will be used
for writing. While this already passes all the automated tests, this is still a
big patch and I'm not sure everything is 100% correct, so please help me test
it if you can.
Signed-off-by: Axel Gembe <ago@bastart.eu.org>
---
trac/admin/console.py | 25 +++---
trac/admin/tests/console.py | 4 +-
trac/attachment.py | 16 ++--
trac/db_default.py | 16 ++--
trac/env.py | 12 ++--
trac/perm.py | 14 ++--
trac/search/api.py | 4 +-
trac/test.py | 15 +++-
trac/tests/env.py | 2 +-
trac/tests/perm.py | 12 ++--
trac/ticket/admin.py | 10 +-
trac/ticket/api.py | 2 +-
trac/ticket/model.py | 133 ++++++++++++++++---------------
trac/ticket/notification.py | 2 +-
trac/ticket/query.py | 14 ++--
trac/ticket/report.py | 30 ++++----
trac/ticket/roadmap.py | 16 ++--
trac/ticket/tests/model.py | 20 +++---
trac/ticket/tests/report.py | 5 +-
trac/ticket/web_ui.py | 8 +-
trac/upgrades/db18.py | 2 +-
trac/versioncontrol/api.py | 2 +-
trac/versioncontrol/cache.py | 32 ++++---
trac/versioncontrol/svn_fs.py | 2 +-
trac/versioncontrol/tests/cache.py | 17 ++--
trac/versioncontrol/web_ui/changeset.py | 2 +-
trac/web/auth.py | 10 +-
trac/web/session.py | 18 +++--
trac/web/tests/auth.py | 16 ++--
trac/web/tests/session.py | 38 ++++++---
trac/wiki/api.py | 2 +-
trac/wiki/macros.py | 2 +-
trac/wiki/model.py | 14 ++--
trac/wiki/tests/model.py | 16 +++--
trac/wiki/web_ui.py | 6 +-
35 files changed, 288 insertions(+), 251 deletions(-)
diff --git a/trac/admin/console.py b/trac/admin/console.py
index 7bf11c6..dc00276 100755
|
a
|
b
|
|
| 167 | 167 | def db_query(self, sql, cursor=None, params=None): |
| 168 | 168 | if not cursor: |
| 169 | 169 | cnx = self.db_open() |
| 170 | | cursor = cnx.cursor() |
| | 170 | cursor = cnx.read.cursor() |
| 171 | 171 | if params: |
| 172 | 172 | cursor.execute(sql, params) |
| 173 | 173 | else: |
| … |
… |
|
| 178 | 178 | def db_update(self, sql, cursor=None, params=None): |
| 179 | 179 | if not cursor: |
| 180 | 180 | cnx = self.db_open() |
| 181 | | cursor = cnx.cursor() |
| | 181 | cursor = cnx.write.cursor() |
| 182 | 182 | else: |
| 183 | 183 | cnx = None |
| 184 | 184 | if params: |
| … |
… |
|
| 186 | 186 | else: |
| 187 | 187 | cursor.execute(sql) |
| 188 | 188 | if cnx: |
| 189 | | cnx.commit() |
| | 189 | cnx.write.commit() |
| 190 | 190 | |
| 191 | 191 | ## |
| 192 | 192 | ## Utility methods |
| … |
… |
|
| 563 | 563 | # Add a few default wiki pages |
| 564 | 564 | print ' Installing default wiki pages' |
| 565 | 565 | cnx = self.__env.get_db_cnx() |
| 566 | | cursor = cnx.cursor() |
| | 566 | cursor = cnx.write.cursor() |
| 567 | 567 | pages_dir = pkg_resources.resource_filename('trac.wiki', |
| 568 | 568 | 'default-pages') |
| 569 | 569 | self._do_wiki_load(pages_dir, cursor) |
| 570 | | cnx.commit() |
| | 570 | cnx.write.commit() |
| 571 | 571 | |
| 572 | 572 | if repository_dir: |
| 573 | 573 | try: |
| … |
… |
|
| 635 | 635 | from trac.versioncontrol.cache import CACHE_METADATA_KEYS |
| 636 | 636 | print 'Resyncing repository history... ' |
| 637 | 637 | cnx = self.db_open() |
| 638 | | cursor = cnx.cursor() |
| | 638 | cursor = cnx.write.cursor() |
| 639 | 639 | cursor.execute("DELETE FROM revision") |
| 640 | 640 | cursor.execute("DELETE FROM node_change") |
| 641 | 641 | cursor.executemany("DELETE FROM system WHERE name=%s", |
| 642 | 642 | [(k,) for k in CACHE_METADATA_KEYS]) |
| 643 | 643 | cursor.executemany("INSERT INTO system (name, value) VALUES (%s, %s)", |
| 644 | 644 | [(k, '') for k in CACHE_METADATA_KEYS]) |
| 645 | | cnx.commit() |
| | 645 | cnx.write.commit() |
| 646 | 646 | repos = env.get_repository().sync(self._resync_feedback) |
| | 647 | cursor = cnx.read.cursor() |
| 647 | 648 | cursor.execute("SELECT count(rev) FROM revision") |
| 648 | 649 | for cnt, in cursor: |
| 649 | 650 | print cnt, 'revisions cached.', |
| … |
… |
|
| 950 | 951 | sql = ("INSERT INTO enum(value,type,name) " |
| 951 | 952 | " SELECT 1+COALESCE(max(%(cast)s),0),'%(type)s','%(name)s'" |
| 952 | 953 | " FROM enum WHERE type='%(type)s'" |
| 953 | | % {'type':type, 'name':name, 'cast': cnx.cast('value', 'int')}) |
| 954 | | cursor = cnx.cursor() |
| | 954 | % {'type':type, 'name':name, 'cast': cnx.write.cast('value', 'int')}) |
| | 955 | cursor = cnx.write.cursor() |
| 955 | 956 | self.db_update(sql, cursor) |
| 956 | | cnx.commit() |
| | 957 | cnx.write.commit() |
| 957 | 958 | |
| 958 | 959 | def _do_enum_change(self, type, name, newname): |
| 959 | 960 | enum_cls = self._enum_map[type] |
| … |
… |
|
| 1151 | 1152 | |
| 1152 | 1153 | # Bogus statement to lock the database while copying files |
| 1153 | 1154 | cnx = self.db_open() |
| 1154 | | cursor = cnx.cursor() |
| | 1155 | cursor = cnx.write.cursor() |
| 1155 | 1156 | cursor.execute("UPDATE system SET name=NULL WHERE name IS NULL") |
| 1156 | 1157 | |
| 1157 | 1158 | try: |
| … |
… |
|
| 1167 | 1168 | copytree(self.__env.path, dest, symlinks=1, skip=skip) |
| 1168 | 1169 | finally: |
| 1169 | 1170 | # Unlock database |
| 1170 | | cnx.rollback() |
| | 1171 | cnx.write.rollback() |
| 1171 | 1172 | |
| 1172 | 1173 | print 'Hotcopy done.' |
| 1173 | 1174 | |
diff --git a/trac/admin/tests/console.py b/trac/admin/tests/console.py
index 66418c8..5746c71 100644
|
a
|
b
|
|
| 27 | 27 | from trac.config import Configuration |
| 28 | 28 | from trac.env import Environment |
| 29 | 29 | from trac.admin import console |
| 30 | | from trac.test import InMemoryDatabase |
| | 30 | from trac.test import InMemoryDatabase, DatabasePair |
| 31 | 31 | from trac.util.datefmt import get_date_format_hint |
| 32 | 32 | |
| 33 | 33 | STRIP_TRAILING_SPACE = re.compile(r'( +)$', re.MULTILINE) |
| … |
… |
|
| 62 | 62 | def get_db_cnx(self): |
| 63 | 63 | if not hasattr(self, '_db'): |
| 64 | 64 | self._db = InMemoryDatabase() |
| 65 | | return self._db |
| | 65 | return DatabasePair(self._db, self._db) |
| 66 | 66 | |
| 67 | 67 | def create(self, db_str=None): |
| 68 | 68 | pass |
diff --git a/trac/attachment.py b/trac/attachment.py
index 71723b8..976cb39 100644
|
a
|
b
|
|
| 132 | 132 | def _fetch(self, filename, db=None): |
| 133 | 133 | if not db: |
| 134 | 134 | db = self.env.get_db_cnx() |
| 135 | | cursor = db.cursor() |
| | 135 | cursor = db.read.cursor() |
| 136 | 136 | cursor.execute("SELECT filename,description,size,time,author,ipnr " |
| 137 | 137 | "FROM attachment WHERE type=%s AND id=%s " |
| 138 | 138 | "AND filename=%s ORDER BY time", |
| … |
… |
|
| 172 | 172 | else: |
| 173 | 173 | handle_ta = False |
| 174 | 174 | |
| 175 | | cursor = db.cursor() |
| | 175 | cursor = db.write.cursor() |
| 176 | 176 | cursor.execute("DELETE FROM attachment WHERE type=%s AND id=%s " |
| 177 | 177 | "AND filename=%s", (self.parent_realm, self.parent_id, |
| 178 | 178 | self.filename)) |
| … |
… |
|
| 183 | 183 | self.env.log.error('Failed to delete attachment file %s', |
| 184 | 184 | self.path, exc_info=True) |
| 185 | 185 | if handle_ta: |
| 186 | | db.rollback() |
| | 186 | db.write.rollback() |
| 187 | 187 | raise TracError(_('Could not delete attachment')) |
| 188 | 188 | |
| 189 | 189 | self.env.log.info('Attachment removed: %s' % self.title) |
| 190 | 190 | if handle_ta: |
| 191 | | db.commit() |
| | 191 | db.write.commit() |
| 192 | 192 | |
| 193 | 193 | for listener in AttachmentModule(self.env).change_listeners: |
| 194 | 194 | listener.attachment_deleted(self) |
| … |
… |
|
| 224 | 224 | basename = os.path.basename(path).encode('ascii') |
| 225 | 225 | filename = unicode_unquote(basename) |
| 226 | 226 | |
| 227 | | cursor = db.cursor() |
| | 227 | cursor = db.write.cursor() |
| 228 | 228 | cursor.execute("INSERT INTO attachment " |
| 229 | 229 | "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)", |
| 230 | 230 | (self.parent_realm, self.parent_id, filename, |
| … |
… |
|
| 237 | 237 | self.author) |
| 238 | 238 | |
| 239 | 239 | if handle_ta: |
| 240 | | db.commit() |
| | 240 | db.write.commit() |
| 241 | 241 | |
| 242 | 242 | for listener in AttachmentModule(self.env).change_listeners: |
| 243 | 243 | listener.attachment_added(self) |
| … |
… |
|
| 248 | 248 | def select(cls, env, parent_realm, parent_id, db=None): |
| 249 | 249 | if not db: |
| 250 | 250 | db = env.get_db_cnx() |
| 251 | | cursor = db.cursor() |
| | 251 | cursor = db.read.cursor() |
| 252 | 252 | cursor.execute("SELECT filename,description,size,time,author,ipnr " |
| 253 | 253 | "FROM attachment WHERE type=%s AND id=%s ORDER BY time", |
| 254 | 254 | (parent_realm, unicode(parent_id))) |
| … |
… |
|
| 441 | 441 | """ |
| 442 | 442 | # Traverse attachment directory |
| 443 | 443 | db = self.env.get_db_cnx() |
| 444 | | cursor = db.cursor() |
| | 444 | cursor = db.read.cursor() |
| 445 | 445 | cursor.execute("SELECT type, id, filename, time, description, author " |
| 446 | 446 | " FROM attachment " |
| 447 | 447 | " WHERE time > %s AND time < %s " |
diff --git a/trac/db_default.py b/trac/db_default.py
index 01354fc..cfb6966 100644
|
a
|
b
|
|
| 180 | 180 | FROM ticket t |
| 181 | 181 | LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' |
| 182 | 182 | WHERE status <> 'closed' |
| 183 | | ORDER BY """ + db.cast('p.value', 'int') + """, milestone, t.type, time |
| | 183 | ORDER BY """ + db.write.cast('p.value', 'int') + """, milestone, t.type, time |
| 184 | 184 | """), |
| 185 | 185 | #---------------------------------------------------------------------------- |
| 186 | 186 | ('Active Tickets by Version', |
| … |
… |
|
| 202 | 202 | FROM ticket t |
| 203 | 203 | LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' |
| 204 | 204 | WHERE status <> 'closed' |
| 205 | | ORDER BY (version IS NULL),version, """ + db.cast('p.value', 'int') + |
| | 205 | ORDER BY (version IS NULL),version, """ + db.write.cast('p.value', 'int') + |
| 206 | 206 | """, t.type, time |
| 207 | 207 | """), |
| 208 | 208 | #---------------------------------------------------------------------------- |
| … |
… |
|
| 226 | 226 | LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' |
| 227 | 227 | WHERE status <> 'closed' |
| 228 | 228 | ORDER BY (milestone IS NULL),milestone, %s, t.type, time |
| 229 | | """ % (db.concat("'Milestone '", 'milestone'), db.cast('p.value', 'int'))), |
| | 229 | """ % (db.write.concat("'Milestone '", 'milestone'), db.write.cast('p.value', 'int'))), |
| 230 | 230 | #---------------------------------------------------------------------------- |
| 231 | 231 | ('Accepted, Active Tickets by Owner', |
| 232 | 232 | """ |
| … |
… |
|
| 242 | 242 | FROM ticket t |
| 243 | 243 | LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' |
| 244 | 244 | WHERE status = 'accepted' |
| 245 | | ORDER BY owner, """ + db.cast('p.value', 'int') + """, t.type, time |
| | 245 | ORDER BY owner, """ + db.write.cast('p.value', 'int') + """, t.type, time |
| 246 | 246 | """), |
| 247 | 247 | #---------------------------------------------------------------------------- |
| 248 | 248 | ('Accepted, Active Tickets by Owner (Full Description)', |
| … |
… |
|
| 259 | 259 | FROM ticket t |
| 260 | 260 | LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' |
| 261 | 261 | WHERE status = 'accepted' |
| 262 | | ORDER BY owner, """ + db.cast('p.value', 'int') + """, t.type, time |
| | 262 | ORDER BY owner, """ + db.write.cast('p.value', 'int') + """, t.type, time |
| 263 | 263 | """), |
| 264 | 264 | #---------------------------------------------------------------------------- |
| 265 | 265 | ('All Tickets By Milestone (Including closed)', |
| … |
… |
|
| 282 | 282 | LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' |
| 283 | 283 | ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'), |
| 284 | 284 | (CASE status WHEN 'closed' THEN changetime ELSE (-1) * %s END) DESC |
| 285 | | """ % db.cast('p.value', 'int')), |
| | 285 | """ % db.write.cast('p.value', 'int')), |
| 286 | 286 | #---------------------------------------------------------------------------- |
| 287 | 287 | ('My Tickets', |
| 288 | 288 | """ |
| … |
… |
|
| 300 | 300 | FROM ticket t |
| 301 | 301 | LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' |
| 302 | 302 | WHERE t.status <> 'closed' AND owner = $USER |
| 303 | | ORDER BY (status = 'accepted') DESC, """ + db.cast('p.value', 'int') + |
| | 303 | ORDER BY (status = 'accepted') DESC, """ + db.write.cast('p.value', 'int') + |
| 304 | 304 | """, milestone, t.type, time |
| 305 | 305 | """), |
| 306 | 306 | #---------------------------------------------------------------------------- |
| … |
… |
|
| 323 | 323 | FROM ticket t |
| 324 | 324 | LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority' |
| 325 | 325 | WHERE status <> 'closed' |
| 326 | | ORDER BY (owner = $USER) DESC, """ + db.cast('p.value', 'int') + |
| | 326 | ORDER BY (owner = $USER) DESC, """ + db.write.cast('p.value', 'int') + |
| 327 | 327 | """, milestone, t.type, time |
| 328 | 328 | """)) |
| 329 | 329 | |
diff --git a/trac/env.py b/trac/env.py
index 11a4314..6810171 100644
|
a
|
b
|
|
| 322 | 322 | """ |
| 323 | 323 | if not db: |
| 324 | 324 | db = self.get_db_cnx() |
| 325 | | cursor = db.cursor() |
| | 325 | cursor = db.read.cursor() |
| 326 | 326 | cursor.execute("SELECT value FROM system " |
| 327 | 327 | "WHERE name='%sdatabase_version'" % |
| 328 | 328 | (initial and 'initial_' or '')) |
| … |
… |
|
| 380 | 380 | """ |
| 381 | 381 | if not cnx: |
| 382 | 382 | cnx = self.get_db_cnx() |
| 383 | | cursor = cnx.cursor() |
| | 383 | cursor = cnx.read.cursor() |
| 384 | 384 | cursor.execute("SELECT DISTINCT s.sid, n.value, e.value " |
| 385 | 385 | "FROM session AS s " |
| 386 | 386 | " LEFT JOIN session_attribute AS n ON (n.sid=s.sid " |
| … |
… |
|
| 440 | 440 | self.backup(backup_dest) |
| 441 | 441 | for participant in upgraders: |
| 442 | 442 | participant.upgrade_environment(db) |
| 443 | | db.commit() |
| | 443 | db.write.commit() |
| 444 | 444 | |
| 445 | 445 | # Database schema may have changed, so close all connections |
| 446 | 446 | self.shutdown() |
| … |
… |
|
| 473 | 473 | def environment_created(self): |
| 474 | 474 | """Insert default data into the database.""" |
| 475 | 475 | db = self.env.get_db_cnx() |
| 476 | | cursor = db.cursor() |
| | 476 | cursor = db.write.cursor() |
| 477 | 477 | for table, cols, vals in db_default.get_data(db): |
| 478 | 478 | cursor.executemany("INSERT INTO %s (%s) VALUES (%s)" % (table, |
| 479 | 479 | ','.join(cols), ','.join(['%s' for c in cols])), |
| 480 | 480 | vals) |
| 481 | | db.commit() |
| | 481 | db.write.commit() |
| 482 | 482 | self._update_sample_config() |
| 483 | 483 | |
| 484 | 484 | def environment_needs_upgrade(self, db): |
| … |
… |
|
| 490 | 490 | return True |
| 491 | 491 | |
| 492 | 492 | def upgrade_environment(self, db): |
| 493 | | cursor = db.cursor() |
| | 493 | cursor = db.write.cursor() |
| 494 | 494 | dbver = self.env.get_version() |
| 495 | 495 | for i in range(dbver + 1, db_default.db_version + 1): |
| 496 | 496 | name = 'db%i' % i |
diff --git a/trac/perm.py b/trac/perm.py
index 9b65827..0540b5a 100644
|
a
|
b
|
|
| 171 | 171 | |
| 172 | 172 | actions = set([]) |
| 173 | 173 | db = self.env.get_db_cnx() |
| 174 | | cursor = db.cursor() |
| | 174 | cursor = db.read.cursor() |
| 175 | 175 | cursor.execute("SELECT username,action FROM permission") |
| 176 | 176 | rows = cursor.fetchall() |
| 177 | 177 | while True: |
| … |
… |
|
| 198 | 198 | # The optimized loop we had before didn't. This is very inefficient, |
| 199 | 199 | # but it works. |
| 200 | 200 | db = self.env.get_db_cnx() |
| 201 | | cursor = db.cursor() |
| | 201 | cursor = db.read.cursor() |
| 202 | 202 | result = set() |
| 203 | 203 | users = set([u[0] for u in self.env.get_known_users()]) |
| 204 | 204 | for user in users: |
| … |
… |
|
| 214 | 214 | The permissions are returned as a list of (subject, action) |
| 215 | 215 | formatted tuples.""" |
| 216 | 216 | db = self.env.get_db_cnx() |
| 217 | | cursor = db.cursor() |
| | 217 | cursor = db.read.cursor() |
| 218 | 218 | cursor.execute("SELECT username,action FROM permission") |
| 219 | 219 | return [(row[0], row[1]) for row in cursor] |
| 220 | 220 | |
| 221 | 221 | def grant_permission(self, username, action): |
| 222 | 222 | """Grants a user the permission to perform the specified action.""" |
| 223 | 223 | db = self.env.get_db_cnx() |
| 224 | | cursor = db.cursor() |
| | 224 | cursor = db.write.cursor() |
| 225 | 225 | cursor.execute("INSERT INTO permission VALUES (%s, %s)", |
| 226 | 226 | (username, action)) |
| 227 | 227 | self.log.info('Granted permission for %s to %s' % (action, username)) |
| 228 | | db.commit() |
| | 228 | db.write.commit() |
| 229 | 229 | |
| 230 | 230 | def revoke_permission(self, username, action): |
| 231 | 231 | """Revokes a users' permission to perform the specified action.""" |
| 232 | 232 | db = self.env.get_db_cnx() |
| 233 | | cursor = db.cursor() |
| | 233 | cursor = db.write.cursor() |
| 234 | 234 | cursor.execute("DELETE FROM permission WHERE username=%s AND action=%s", |
| 235 | 235 | (username, action)) |
| 236 | 236 | self.log.info('Revoked permission for %s to %s' % (action, username)) |
| 237 | | db.commit() |
| | 237 | db.write.commit() |
| 238 | 238 | |
| 239 | 239 | |
| 240 | 240 | class DefaultPermissionGroupProvider(Component): |
diff --git a/trac/search/api.py b/trac/search/api.py
index cfd80f1..1402af9 100644
|
a
|
b
|
|
| 47 | 47 | """ |
| 48 | 48 | assert columns and terms |
| 49 | 49 | |
| 50 | | likes = ['%s %s' % (i, db.like()) for i in columns] |
| | 50 | likes = ['%s %s' % (i, db.read.like()) for i in columns] |
| 51 | 51 | c = ' OR '.join(likes) |
| 52 | 52 | sql = '(' + ') AND ('.join([c] * len(terms)) + ')' |
| 53 | 53 | args = [] |
| 54 | 54 | for t in terms: |
| 55 | | args.extend(['%' + db.like_escape(t) + '%'] * len(columns)) |
| | 55 | args.extend(['%' + db.read.like_escape(t) + '%'] * len(columns)) |
| 56 | 56 | return sql, tuple(args) |
| 57 | 57 | |
| 58 | 58 | def shorten_result(text='', keywords=[], maxlen=240, fuzz=60): |
diff --git a/trac/test.py b/trac/test.py
index f79d078..0a96371 100755
|
a
|
b
|
|
| 36 | 36 | from trac.util import translation |
| 37 | 37 | |
| 38 | 38 | |
| | 39 | class DatabasePair(object): |
| | 40 | """ |
| | 41 | Holds two database connections, one for reading and one for writing. |
| | 42 | """ |
| | 43 | def __init__(self, db, dbwr): |
| | 44 | self.read = db |
| | 45 | self.write = dbwr |
| | 46 | |
| 39 | 47 | def Mock(bases=(), *initargs, **kw): |
| 40 | 48 | """ |
| 41 | 49 | Simple factory for dummy classes that can be used as replacement for the |
| … |
… |
|
| 172 | 180 | ComponentManager.__init__(self) |
| 173 | 181 | Component.__init__(self) |
| 174 | 182 | self.enabled_components = enable or ['trac.*'] |
| 175 | | self.db = InMemoryDatabase() |
| | 183 | ndb = InMemoryDatabase() |
| | 184 | self.db = DatabasePair(ndb, ndb) |
| 176 | 185 | self.systeminfo = [('Python', sys.version)] |
| 177 | 186 | |
| 178 | 187 | import trac |
| … |
… |
|
| 195 | 204 | |
| 196 | 205 | from trac import db_default |
| 197 | 206 | if default_data: |
| 198 | | cursor = self.db.cursor() |
| | 207 | cursor = self.db.write.cursor() |
| 199 | 208 | for table, cols, vals in db_default.get_data(self.db): |
| 200 | 209 | cursor.executemany("INSERT INTO %s (%s) VALUES (%s)" |
| 201 | 210 | % (table, ','.join(cols), |
| 202 | 211 | ','.join(['%s' for c in cols])), |
| 203 | 212 | vals) |
| 204 | | self.db.commit() |
| | 213 | self.db.write.commit() |
| 205 | 214 | |
| 206 | 215 | self.known_users = [] |
| 207 | 216 | translation.activate(Locale and Locale('en', 'US')) |
diff --git a/trac/tests/env.py b/trac/tests/env.py
index 681fd52..98a40e0 100644
|
a
|
b
|
|
| 26 | 26 | |
| 27 | 27 | def test_get_known_users(self): |
| 28 | 28 | """Testing env.get_known_users""" |
| 29 | | cursor = self.db.cursor() |
| | 29 | cursor = self.db.write.cursor() |
| 30 | 30 | cursor.executemany("INSERT INTO session VALUES (%s,%s,0)", |
| 31 | 31 | [('123', 0),('tom', 1), ('joe', 1), ('jane', 1)]) |
| 32 | 32 | cursor.executemany("INSERT INTO session_attribute VALUES (%s,%s,%s,%s)", |
diff --git a/trac/tests/perm.py b/trac/tests/perm.py
index a5d1d92..d022e83 100644
|
a
|
b
|
|
| 15 | 15 | |
| 16 | 16 | def test_simple_actions(self): |
| 17 | 17 | db = self.env.get_db_cnx() |
| 18 | | cursor = db.cursor() |
| | 18 | cursor = db.write.cursor() |
| 19 | 19 | cursor.executemany("INSERT INTO permission VALUES (%s,%s)", [ |
| 20 | 20 | ('john', 'WIKI_MODIFY'), ('john', 'REPORT_ADMIN'), |
| 21 | 21 | ('kate', 'TICKET_CREATE')]) |
| … |
… |
|
| 25 | 25 | |
| 26 | 26 | def test_simple_group(self): |
| 27 | 27 | db = self.env.get_db_cnx() |
| 28 | | cursor = db.cursor() |
| | 28 | cursor = db.write.cursor() |
| 29 | 29 | cursor.executemany("INSERT INTO permission VALUES (%s,%s)", [ |
| 30 | 30 | ('dev', 'WIKI_MODIFY'), ('dev', 'REPORT_ADMIN'), |
| 31 | 31 | ('john', 'dev')]) |
| … |
… |
|
| 34 | 34 | |
| 35 | 35 | def test_nested_groups(self): |
| 36 | 36 | db = self.env.get_db_cnx() |
| 37 | | cursor = db.cursor() |
| | 37 | cursor = db.write.cursor() |
| 38 | 38 | cursor.executemany("INSERT INTO permission VALUES (%s,%s)", [ |
| 39 | 39 | ('dev', 'WIKI_MODIFY'), ('dev', 'REPORT_ADMIN'), |
| 40 | 40 | ('admin', 'dev'), ('john', 'admin')]) |
| … |
… |
|
| 43 | 43 | |
| 44 | 44 | def test_mixed_case_group(self): |
| 45 | 45 | db = self.env.get_db_cnx() |
| 46 | | cursor = db.cursor() |
| | 46 | cursor = db.write.cursor() |
| 47 | 47 | cursor.executemany("INSERT INTO permission VALUES (%s,%s)", [ |
| 48 | 48 | ('Dev', 'WIKI_MODIFY'), ('Dev', 'REPORT_ADMIN'), |
| 49 | 49 | ('Admin', 'Dev'), ('john', 'Admin')]) |
| … |
… |
|
| 52 | 52 | |
| 53 | 53 | def test_builtin_groups(self): |
| 54 | 54 | db = self.env.get_db_cnx() |
| 55 | | cursor = db.cursor() |
| | 55 | cursor = db.write.cursor() |
| 56 | 56 | cursor.executemany("INSERT INTO permission VALUES (%s,%s)", [ |
| 57 | 57 | ('authenticated', 'WIKI_MODIFY'), |
| 58 | 58 | ('authenticated', 'REPORT_ADMIN'), |
| … |
… |
|
| 64 | 64 | |
| 65 | 65 | def test_get_all_permissions(self): |
| 66 | 66 | db = self.env.get_db_cnx() |
| 67 | | cursor = db.cursor() |
| | 67 | cursor = db.write.cursor() |
| 68 | 68 | cursor.executemany("INSERT INTO permission VALUES (%s,%s)", [ |
| 69 | 69 | ('dev', 'WIKI_MODIFY'), ('dev', 'REPORT_ADMIN'), |
| 70 | 70 | ('john', 'dev')]) |
diff --git a/trac/ticket/admin.py b/trac/ticket/admin.py
index 5aef567..2b9ba87 100644
|
a
|
b
|
|
| 98 | 98 | for name in sel: |
| 99 | 99 | comp = model.Component(self.env, name, db=db) |
| 100 | 100 | comp.delete(db=db) |
| 101 | | db.commit() |
| | 101 | db.write.commit() |
| 102 | 102 | req.redirect(req.href.admin(cat, page)) |
| 103 | 103 | |
| 104 | 104 | # Set default component |
| … |
… |
|
| 196 | 196 | for name in sel: |
| 197 | 197 | mil = model.Milestone(self.env, name, db=db) |
| 198 | 198 | mil.delete(db=db) |
| 199 | | db.commit() |
| | 199 | db.write.commit() |
| 200 | 200 | req.redirect(req.href.admin(cat, page)) |
| 201 | 201 | |
| 202 | 202 | # Set default milestone |
| … |
… |
|
| 278 | 278 | for name in sel: |
| 279 | 279 | ver = model.Version(self.env, name, db=db) |
| 280 | 280 | ver.delete(db=db) |
| 281 | | db.commit() |
| | 281 | db.write.commit() |
| 282 | 282 | req.redirect(req.href.admin(cat, page)) |
| 283 | 283 | |
| 284 | 284 | # Set default version |
| … |
… |
|
| 358 | 358 | for name in sel: |
| 359 | 359 | enum = self._enum_cls(self.env, name, db=db) |
| 360 | 360 | enum.delete(db=db) |
| 361 | | db.commit() |
| | 361 | db.write.commit() |
| 362 | 362 | req.redirect(req.href.admin(cat, page)) |
| 363 | 363 | |
| 364 | 364 | # Appy changes |
| … |
… |
|
| 387 | 387 | if new_value != enum.value: |
| 388 | 388 | enum.value = new_value |
| 389 | 389 | enum.update(db=db) |
| 390 | | db.commit() |
| | 390 | db.write.commit() |
| 391 | 391 | |
| 392 | 392 | req.redirect(req.href.admin(cat, page)) |
| 393 | 393 | |
diff --git a/trac/ticket/api.py b/trac/ticket/api.py
index 42e98a0..184afaf 100644
|
a
|
b
|
|
| 321 | 321 | if Ticket.id_is_valid(num): |
| 322 | 322 | # TODO: watch #6436 and when done, attempt to retrieve |
| 323 | 323 | # ticket directly (try: Ticket(self.env, num) ...) |
| 324 | | cursor = formatter.db.cursor() |
| | 324 | cursor = formatter.db.read.cursor() |
| 325 | 325 | cursor.execute("SELECT type,summary,status,resolution " |
| 326 | 326 | "FROM ticket WHERE id=%s", (str(num),)) |
| 327 | 327 | for type, summary, status, resolution in cursor: |
diff --git a/trac/ticket/model.py b/trac/ticket/model.py
index a380d0b..8d60560 100644
|
a
|
b
|
|
| 50 | 50 | self.id = self.time_created = self.time_changed = None |
| 51 | 51 | self._old = {} |
| 52 | 52 | |
| 53 | | def _get_db(self, db): |
| 54 | | return db or self.env.get_db_cnx() |
| 55 | | |
| 56 | | def _get_db_for_write(self, db): |
| 57 | | if db: |
| 58 | | return (db, False) |
| 59 | | else: |
| 60 | | return (self.env.get_db_cnx(), True) |
| 61 | | |
| 62 | 53 | exists = property(fget=lambda self: self.id is not None) |
| 63 | 54 | |
| 64 | 55 | def _init_defaults(self, db=None): |
| … |
… |
|
| 86 | 77 | def _fetch_ticket(self, tkt_id, db=None): |
| 87 | 78 | row = None |
| 88 | 79 | if self.id_is_valid(tkt_id): |
| 89 | | db = self._get_db(db) |
| | 80 | if db is None: |
| | 81 | db = self.env.get_db_cnx() |
| 90 | 82 | |
| 91 | 83 | # Fetch the standard ticket fields |
| 92 | 84 | std_fields = [f['name'] for f in self.fields if not f.get('custom')] |
| 93 | | cursor = db.cursor() |
| | 85 | |