Edgewall Software

Ticket #8751: transaction_rename_base.diff

File transaction_rename_base.diff, 102.5 KB (added by shookie@…, 2 years ago)

the consolidated/combined transaction patch, I use as base for #1106, just for convinience

  • trac/attachment.py

    diff --git a/trac/attachment.py b/trac/attachment.py
    index d084802..a3ed370 100644
    a b from trac.admin import AdminCommandError, IAdminCommandProvider, PrefixList, \ 
    3030                       console_datetime_format, get_dir_list 
    3131from trac.config import BoolOption, IntOption 
    3232from trac.core import * 
     33from trac.db.util import with_transaction 
    3334from trac.env import IEnvironmentSetupParticipant 
    3435from trac.mimeview import * 
    3536from trac.perm import PermissionError, IPermissionPolicy 
    class Attachment(object): 
    170171 
    171172    def delete(self, db=None): 
    172173        assert self.filename, 'Cannot delete non-existent attachment' 
    173         if not db: 
    174             db = self.env.get_db_cnx() 
    175             handle_ta = True 
    176         else: 
    177             handle_ta = False 
    178174 
    179         cursor = db.cursor() 
    180         cursor.execute("DELETE FROM attachment WHERE type=%s AND id=%s " 
    181                        "AND filename=%s", (self.parent_realm, self.parent_id, 
    182                        self.filename)) 
    183         if os.path.isfile(self.path): 
    184             try: 
    185                 os.unlink(self.path) 
    186             except OSError, e: 
    187                 self.env.log.error('Failed to delete attachment file %s: %s', 
    188                            self.path, exception_to_unicode(e, traceback=True)) 
    189                 if handle_ta: 
    190                     db.rollback() 
    191                 raise TracError(_('Could not delete attachment')) 
     175        @with_transaction(self.env, db) 
     176        def do_delete(db): 
     177            cursor = db.cursor() 
     178            cursor.execute("DELETE FROM attachment WHERE type=%s AND id=%s " 
     179                           "AND filename=%s", 
     180                           (self.parent_realm, self.parent_id, self.filename)) 
     181            if os.path.isfile(self.path): 
     182                try: 
     183                    os.unlink(self.path) 
     184                except OSError, e: 
     185                    self.env.log.error('Failed to delete attachment ' 
     186                                       'file %s: %s', 
     187                                       self.path, 
     188                                       exception_to_unicode(e, traceback=True)) 
     189                    raise TracError(_('Could not delete attachment')) 
    192190 
    193191        self.env.log.info('Attachment removed: %s' % self.title) 
    194         if handle_ta: 
    195             db.commit() 
    196192 
    197193        for listener in AttachmentModule(self.env).change_listeners: 
    198194            listener.attachment_deleted(self) 
    class Attachment(object): 
    200196 
    201197    def insert(self, filename, fileobj, size, t=None, db=None): 
    202198        # FIXME: `t` should probably be switched to `datetime` too 
    203         if not db: 
    204             db = self.env.get_db_cnx() 
    205             handle_ta = True 
    206         else: 
    207             handle_ta = False 
    208199 
    209200        self.size = size and int(size) or 0 
    210201        timestamp = int(t or time.time()) 
    class Attachment(object): 
    228219            basename = os.path.basename(path).encode('ascii') 
    229220            filename = unicode_unquote(basename) 
    230221 
    231             cursor = db.cursor() 
    232             cursor.execute("INSERT INTO attachment " 
    233                            "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)", 
    234                            (self.parent_realm, self.parent_id, filename, 
    235                             self.size, timestamp, self.description, 
    236                             self.author, self.ipnr)) 
    237             shutil.copyfileobj(fileobj, targetfile) 
    238             self.resource.id = self.filename = filename 
    239  
    240             self.env.log.info('New attachment: %s by %s', self.title, 
    241                               self.author) 
    242  
    243             if handle_ta: 
    244                 db.commit() 
    245  
     222            @with_transaction(self.env, db) 
     223            def do_insert(db): 
     224                cursor = db.cursor() 
     225                cursor.execute("INSERT INTO attachment " 
     226                               "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)", 
     227                               (self.parent_realm, self.parent_id, filename, 
     228                                self.size, timestamp, self.description, 
     229                                self.author, self.ipnr)) 
     230                shutil.copyfileobj(fileobj, targetfile) 
     231                self.resource.id = self.filename = filename 
     232 
     233                self.env.log.info('New attachment: %s by %s', self.title, 
     234                                  self.author) 
     235        finally: 
    246236            targetfile.close() 
    247237 
    248             for listener in AttachmentModule(self.env).change_listeners: 
    249                 listener.attachment_added(self) 
     238        for listener in AttachmentModule(self.env).change_listeners: 
     239            listener.attachment_added(self) 
    250240 
    251         finally: 
    252             if not targetfile.closed: 
    253                 targetfile.close() 
    254241 
    255242    @classmethod 
    256243    def select(cls, env, parent_realm, parent_id, db=None): 
  • trac/cache.py

    diff --git a/trac/cache.py b/trac/cache.py
    index 23b4245..d50974c 100644
    a b except ImportError: 
    1717    import dummy_threading as threading 
    1818 
    1919from trac.core import Component 
     20from trac.db.util import with_transaction 
    2021from trac.util.compat import partial 
    2122 
    2223__all__ = ["CacheManager", "cached", "cached_value"] 
    class CacheManager(Component): 
    189190        self._lock.acquire() 
    190191        try: 
    191192            # Invalidate in other processes 
    192             handle_ta = db is None 
    193             if handle_ta: 
    194                 db = self.env.get_db_cnx() 
    195             cursor = db.cursor() 
    196              
     193 
    197194            # The row corresponding to the cache may not exist in the table 
    198195            # yet. 
    199196            #  - If the row exists, the UPDATE increments the generation, the 
    class CacheManager(Component): 
    201198            #  - If the row doesn't exist, the UPDATE does nothing, but starts 
    202199            #    a transaction. The SELECT then returns nothing, and we can 
    203200            #    safely INSERT a new row. 
    204             cursor.execute("UPDATE cache SET generation=generation+1 " 
    205                            "WHERE id=%s", (id,)) 
    206             cursor.execute("SELECT generation FROM cache WHERE id=%s", (id,)) 
    207             if not cursor.fetchone(): 
    208                 cursor.execute("INSERT INTO cache VALUES (%s, %s)", (id, 0)) 
    209             if handle_ta: 
    210                 db.commit() 
     201            @with_transaction(self.env, db) 
     202            def do_invalidate(db): 
     203                cursor = db.cursor() 
     204                cursor.execute("UPDATE cache SET generation=generation+1 " 
     205                               "WHERE id=%s", (id,)) 
     206                cursor.execute("SELECT generation FROM cache WHERE id=%s", 
     207                               (id,)) 
     208                if not cursor.fetchone(): 
     209                    cursor.execute("INSERT INTO cache VALUES (%s, %s)", (id, 0)) 
    211210             
    212211            # Invalidate in this process 
    213212            self._cache.pop(id, None) 
  • trac/db/util.py

    diff --git a/trac/db/util.py b/trac/db/util.py
    index 511e940..8bb9a5b 100644
    a b  
    1515# 
    1616# Author: Christopher Lenz <cmlenz@gmx.de> 
    1717 
     18def with_transaction(env, db=None): 
     19    """Transaction decorator for simple use-once transactions. 
     20    Will be replaced by a context manager once python 2.4 support is dropped. 
     21 
     22    >>> def api_method(p1, p2): 
     23    >>>     result[0] = value1 
     24    >>>     @with_transaction(env, db) 
     25    >>>     def implementation_method(db): 
     26    >>>         # implementation 
     27    >>>         result[0] = value2 
     28    """ 
     29    def transaction_wrapper(fn): 
     30        if db: 
     31            fn(db) 
     32        else: 
     33            dbtmp = env.get_db_cnx() 
     34            try: 
     35                fn(dbtmp) 
     36                dbtmp.commit() 
     37            except: 
     38                dbtmp.rollback() 
     39                raise 
     40    return transaction_wrapper 
     41 
     42 
    1843def sql_escape_percent(sql): 
    1944    import re 
    2045    return re.sub("'((?:[^']|(?:''))*)'", 
  • trac/env.py

    diff --git a/trac/env.py b/trac/env.py
    index df08010..08646d2 100644
    a b from trac.config import * 
    3030from trac.core import Component, ComponentManager, implements, Interface, \ 
    3131                      ExtensionPoint, TracError 
    3232from trac.db import DatabaseManager 
     33from trac.db.util import with_transaction 
    3334from trac.util import copytree, create_file, get_pkginfo, makedirs 
    3435from trac.util.compat import any 
    3536from trac.util.text import exception_to_unicode, printerr, printout 
    class Environment(Component, ComponentManager): 
    486487 
    487488        if backup: 
    488489            self.backup(backup_dest) 
    489         for participant in upgraders: 
    490             participant.upgrade_environment(db) 
    491         db.commit() 
     490 
     491        @with_transaction(self.env) 
     492        def do_upgrade(db): 
     493            for participant in upgraders: 
     494                participant.upgrade_environment(db) 
    492495 
    493496        # Database schema may have changed, so close all connections 
    494497        self.shutdown(except_logging=True) 
    class EnvironmentSetup(Component): 
    520523 
    521524    def environment_created(self): 
    522525        """Insert default data into the database.""" 
    523         db = self.env.get_db_cnx() 
    524         cursor = db.cursor() 
    525         for table, cols, vals in db_default.get_data(db): 
    526             cursor.executemany("INSERT INTO %s (%s) VALUES (%s)" % (table, 
    527                                ','.join(cols), ','.join(['%s' for c in cols])), 
    528                                vals) 
    529         db.commit() 
     526        @with_transaction(self.env) 
     527        def do_db_populate(db): 
     528            cursor = db.cursor() 
     529            for table, cols, vals in db_default.get_data(db): 
     530                cursor.executemany("INSERT INTO %s (%s) VALUES (%s)" 
     531                                   % (table, ','.join(cols), 
     532                                      ','.join(['%s' for c in cols])), 
     533                                   vals) 
    530534        self._update_sample_config() 
    531535 
    532536    def environment_needs_upgrade(self, db): 
  • trac/perm.py

    diff --git a/trac/perm.py b/trac/perm.py
    index 860d462..d43b591 100644
    a b from time import time 
    2121from trac.admin import AdminCommandError, IAdminCommandProvider 
    2222from trac.config import ExtensionOption, OrderedExtensionsOption 
    2323from trac.core import * 
     24from trac.db.util import with_transaction 
    2425from trac.resource import Resource, get_resource_name 
    2526from trac.util.text import print_table, printout, wrap 
    2627from trac.util.translation import _ 
    class DefaultPermissionStore(Component): 
    221222 
    222223    def grant_permission(self, username, action): 
    223224        """Grants a user the permission to perform the specified action.""" 
    224         db = self.env.get_db_cnx() 
    225         cursor = db.cursor() 
    226         cursor.execute("INSERT INTO permission VALUES (%s, %s)", 
    227                        (username, action)) 
    228         self.log.info('Granted permission for %s to %s' % (action, username)) 
    229         db.commit() 
     225        @with_transaction(self.env) 
     226        def do_grant(db): 
     227            cursor = db.cursor() 
     228            cursor.execute("INSERT INTO permission VALUES (%s, %s)", 
     229                           (username, action)) 
     230            self.log.info('Granted permission for %s to %s' 
     231                          % (action, username)) 
    230232 
    231233    def revoke_permission(self, username, action): 
    232234        """Revokes a users' permission to perform the specified action.""" 
    233235        db = self.env.get_db_cnx() 
    234         cursor = db.cursor() 
    235         cursor.execute("DELETE FROM permission WHERE username=%s AND action=%s", 
    236                        (username, action)) 
    237         self.log.info('Revoked permission for %s to %s' % (action, username)) 
    238         db.commit() 
     236        @with_transaction(self.env) 
     237        def do_revoke(db): 
     238            cursor = db.cursor() 
     239            cursor.execute("DELETE FROM permission WHERE username=%s " 
     240                           "AND action=%s", 
     241                           (username, action)) 
     242            self.log.info('Revoked permission for %s to %s' 
     243                          % (action, username)) 
    239244 
    240245 
    241246class DefaultPermissionGroupProvider(Component): 
  • trac/ticket/admin.py

    diff --git a/trac/ticket/admin.py b/trac/ticket/admin.py
    index ad0c9c3..4076fe6 100644
    a b from datetime import datetime 
    1515 
    1616from trac.admin import * 
    1717from trac.core import * 
     18from trac.db.util import with_transaction 
    1819from trac.perm import PermissionSystem 
    1920from trac.resource import ResourceNotFound 
    2021from trac.ticket import model 
    class ComponentAdminPanel(TicketAdminPanel): 
    125126                        raise TracError(_('No component selected')) 
    126127                    if not isinstance(sel, list): 
    127128                        sel = [sel] 
    128                     db = self.env.get_db_cnx() 
    129                     for name in sel: 
    130                         comp = model.Component(self.env, name, db=db) 
    131                         comp.delete(db=db) 
    132                     db.commit() 
     129                    @with_transaction(self.env) 
     130                    def do_remove(db): 
     131                        for name in sel: 
     132                            comp = model.Component(self.env, name, db=db) 
     133                            comp.delete(db=db) 
    133134                    add_notice(req, _('The selected components have been ' 
    134135                                      'removed.')) 
    135136                    req.redirect(req.href.admin(cat, page)) 
    class ComponentAdminPanel(TicketAdminPanel): 
    215216        component.insert() 
    216217     
    217218    def _do_rename(self, name, newname): 
    218         db = self.env.get_db_cnx() 
    219         component = model.Component(self.env, name, db=db) 
    220         component.name = newname 
    221         component.update(db=db) 
    222         db.commit() 
     219        @with_transaction(self.env) 
     220        def do_rename(db): 
     221            component = model.Component(self.env, name, db=db) 
     222            component.name = newname 
     223            component.update(db=db) 
    223224     
    224225    def _do_remove(self, name): 
    225         db = self.env.get_db_cnx() 
    226         component = model.Component(self.env, name, db=db) 
    227         component.delete(db=db) 
    228         db.commit() 
     226        @with_transaction(self.env) 
     227        def do_remove(db): 
     228            component = model.Component(self.env, name, db=db) 
     229            component.delete(db=db) 
    229230     
    230231    def _do_chown(self, name, owner): 
    231         db = self.env.get_db_cnx() 
    232         component = model.Component(self.env, name, db=db) 
    233         component.owner = owner 
    234         component.update(db=db) 
    235         db.commit() 
     232        @with_transaction(self.env) 
     233        def do_chown(db): 
     234            component = model.Component(self.env, name, db=db) 
     235            component.owner = owner 
     236            component.update(db=db) 
    236237 
    237238 
    238239class MilestoneAdminPanel(TicketAdminPanel): 
    class MilestoneAdminPanel(TicketAdminPanel): 
    313314                        raise TracError(_('No milestone selected')) 
    314315                    if not isinstance(sel, list): 
    315316                        sel = [sel] 
    316                     db = self.env.get_db_cnx() 
    317                     for name in sel: 
    318                         mil = model.Milestone(self.env, name, db=db) 
    319                         mil.delete(db=db, author=req.authname) 
    320                     db.commit() 
     317                    @with_transaction(self.env) 
     318                    def do_remove(db): 
     319                        for name in sel: 
     320                            mil = model.Milestone(self.env, name, db=db) 
     321                            mil.delete(db=db, author=req.authname) 
    321322                    add_notice(req, _('The selected milestones have been ' 
    322323                                      'removed.')) 
    323324                    req.redirect(req.href.admin(cat, page)) 
    class MilestoneAdminPanel(TicketAdminPanel): 
    407408        milestone.insert() 
    408409     
    409410    def _do_rename(self, name, newname): 
    410         db = self.env.get_db_cnx() 
    411         milestone = model.Milestone(self.env, name, db=db) 
    412         milestone.name = newname 
    413         milestone.update(db=db) 
    414         db.commit() 
     411        @with_transaction(self.env) 
     412        def do_rename(db): 
     413            milestone = model.Milestone(self.env, name, db=db) 
     414            milestone.name = newname 
     415            milestone.update(db=db) 
    415416     
    416417    def _do_due(self, name, due): 
    417         db = self.env.get_db_cnx() 
    418         milestone = model.Milestone(self.env, name, db=db) 
    419         milestone.due = due and parse_date(due) 
    420         milestone.update(db=db) 
    421         db.commit() 
     418        @with_transaction(self.env) 
     419        def do_due(db): 
     420            milestone = model.Milestone(self.env, name, db=db) 
     421            milestone.due = due and parse_date(due) 
     422            milestone.update(db=db) 
    422423     
    423424    def _do_completed(self, name, completed): 
    424         db = self.env.get_db_cnx() 
    425         milestone = model.Milestone(self.env, name, db=db) 
    426         milestone.completed = completed and parse_date(completed) 
    427         milestone.update(db=db) 
    428         db.commit() 
     425        @with_transaction(self.env) 
     426        def do_completed(db): 
     427            milestone = model.Milestone(self.env, name, db=db) 
     428            milestone.completed = completed and parse_date(completed) 
     429            milestone.update(db=db) 
    429430     
    430431    def _do_remove(self, name): 
    431         db = self.env.get_db_cnx() 
    432         milestone = model.Milestone(self.env, name, db=db) 
    433         milestone.delete(author=getuser(), db=db) 
    434         db.commit() 
     432        @with_transaction(self.env) 
     433        def do_remove(db): 
     434            milestone = model.Milestone(self.env, name, db=db) 
     435            milestone.delete(author=getuser(), db=db) 
    435436 
    436437 
    437438class VersionAdminPanel(TicketAdminPanel): 
    class VersionAdminPanel(TicketAdminPanel): 
    493494                        raise TracError(_('No version selected')) 
    494495                    if not isinstance(sel, list): 
    495496                        sel = [sel] 
    496                     db = self.env.get_db_cnx() 
    497                     for name in sel: 
    498                         ver = model.Version(self.env, name, db=db) 
    499                         ver.delete(db=db) 
    500                     db.commit() 
     497                    @with_transaction(self.env) 
     498                    def do_remove(db): 
     499                        for name in sel: 
     500                            ver = model.Version(self.env, name, db=db) 
     501                            ver.delete(db=db) 
    501502                    add_notice(req, _('The selected versions have been ' 
    502503                                      'removed.')) 
    503504                    req.redirect(req.href.admin(cat, page)) 
    class VersionAdminPanel(TicketAdminPanel): 
    566567        version.insert() 
    567568     
    568569    def _do_rename(self, name, newname): 
    569         db = self.env.get_db_cnx() 
    570         version = model.Version(self.env, name, db=db) 
    571         version.name = newname 
    572         version.update(db=db) 
    573         db.commit() 
     570        @with_transaction(self.env) 
     571        def do_rename(db): 
     572            version = model.Version(self.env, name, db=db) 
     573            version.name = newname 
     574            version.update(db=db) 
    574575     
    575576    def _do_time(self, name, time): 
    576         db = self.env.get_db_cnx() 
    577         version = model.Version(self.env, name, db=db) 
    578         version.time = time and parse_date(time) 
    579         version.update(db=db) 
    580         db.commit() 
     577        @with_transaction(self.env) 
     578        def do_time(db): 
     579            version = model.Version(self.env, name, db=db) 
     580            version.time = time and parse_date(time) 
     581            version.update(db=db) 
    581582     
    582583    def _do_remove(self, name): 
    583         db = self.env.get_db_cnx() 
    584         version = model.Version(self.env, name, db=db) 
    585         version.delete(db=db) 
    586         db.commit() 
     584        @with_transaction(self.env) 
     585        def do_remove(db): 
     586            version = model.Version(self.env, name, db=db) 
     587            version.delete(db=db) 
    587588 
    588589 
    589590class AbstractEnumAdminPanel(TicketAdminPanel): 
    class AbstractEnumAdminPanel(TicketAdminPanel): 
    643644                        raise TracError(_('No %s selected') % self._type) 
    644645                    if not isinstance(sel, list): 
    645646                        sel = [sel] 
    646                     db = self.env.get_db_cnx() 
    647                     for name in sel: 
    648                         enum = self._enum_cls(self.env, name, db=db) 
    649                         enum.delete(db=db) 
    650                     db.commit() 
     647                    @with_transaction(self.env) 
     648                    def do_remove(db): 
     649                        for name in sel: 
     650                            enum = self._enum_cls(self.env, name, db=db) 
     651                            enum.delete(db=db) 
    651652                    add_notice(req, _('The selected %(fields)s have been ' 
    652653                                      'removed.', 
    653654                                      fields=self._label[1].lower())) 
    654655                    req.redirect(req.href.admin(cat, page)) 
    655656 
    656                 # Appy changes 
     657                # Apply changes 
    657658                elif req.args.get('apply'): 
    658                     changed = False 
     659                    changed = [False] 
    659660                     
    660661                    # Set default value 
    661662                    name = req.args.get('default') 
    class AbstractEnumAdminPanel(TicketAdminPanel): 
    666667                                        name) 
    667668                        try: 
    668669                            self.config.save() 
    669                             changed = True 
     670                            changed[0] = True 
    670671                        except Exception, e: 
    671672                            self.log.error('Error writing to trac.ini: %s', 
    672673                                           exception_to_unicode(e)) 
    class AbstractEnumAdminPanel(TicketAdminPanel): 
    684685                    values = dict([(val, True) for val in order.values()]) 
    685686                    if len(order) != len(values): 
    686687                        raise TracError(_('Order numbers must be unique')) 
    687                     db = self.env.get_db_cnx() 
    688                     for enum in self._enum_cls.select(self.env, db=db): 
    689                         new_value = order[enum.value] 
    690                         if new_value != enum.value: 
    691                             enum.value = new_value 
    692                             enum.update(db=db) 
    693                             changed = True 
    694                     db.commit() 
    695  
    696                     if changed: 
     688                    @with_transaction(self.env) 
     689                    def do_change(db): 
     690                        for enum in self._enum_cls.select(self.env, db=db): 
     691                            new_value = order[enum.value] 
     692                            if new_value != enum.value: 
     693                                enum.value = new_value 
     694                                enum.update(db=db) 
     695                                changed[0] = True 
     696 
     697                    if changed[0]: 
    697698                        add_notice(req, _('Your changes have been saved.')) 
    698699                    req.redirect(req.href.admin(cat, page)) 
    699700 
    class AbstractEnumAdminPanel(TicketAdminPanel): 
    753754        enum.insert() 
    754755     
    755756    def _do_change(self, name, newname): 
    756         db = self.env.get_db_cnx() 
    757         enum = self._enum_cls(self.env, name, db=db) 
    758         enum.name = newname 
    759         enum.update(db=db) 
    760         db.commit() 
     757        @with_transaction(self.env) 
     758        def do_change(db): 
     759            enum = self._enum_cls(self.env, name, db=db) 
     760            enum.name = newname 
     761            enum.update(db=db) 
    761762     
    762763    def _do_remove(self, value): 
    763         db = self.env.get_db_cnx() 
    764         enum = self._enum_cls(self.env, value, db=db) 
    765         enum.delete(db=db) 
    766         db.commit() 
     764        @with_transaction(self.env) 
     765        def do_remove(db): 
     766            enum = self._enum_cls(self.env, value, db=db) 
     767            enum.delete(db=db) 
    767768     
    768769    def _do_order(self, name, up_down): 
    769770        if up_down not in ('up', 'down'): 
    class AbstractEnumAdminPanel(TicketAdminPanel): 
    779780                break 
    780781        else: 
    781782            return 
    782         enum1.update(db=db) 
    783         enum2.update(db=db) 
    784         db.commit() 
     783        @with_transaction(self.env) 
     784        def do_order(db): 
     785            enum1.update(db=db) 
     786            enum2.update(db=db) 
     787 
    785788 
    786789 
    787790class PriorityAdminPanel(AbstractEnumAdminPanel): 
    class TicketAdmin(Component): 
    834837            number = int(number) 
    835838        except ValueError: 
    836839            raise AdminCommandError(_('<number> must be a number')) 
    837         db = self.env.get_db_cnx() 
    838         ticket = model.Ticket(self.env, number, db=db) 
    839         ticket.delete(db=db) 
    840         db.commit() 
     840        @with_transaction(self.env) 
     841        def do_remove(db): 
     842            ticket = model.Ticket(self.env, number, db=db) 
     843            ticket.delete(db=db) 
    841844        printout(_('Ticket %(num)s and all associated data removed.', 
    842845                   num=number)) 
  • trac/ticket/model.py

    diff --git a/trac/ticket/model.py b/trac/ticket/model.py
    index 5b91fd6..7ec802e 100644
    a b from datetime import date, datetime 
    2222 
    2323from trac.attachment import Attachment 
    2424from trac.core import TracError 
     25from trac.db.util import with_transaction 
    2526from trac.resource import Resource, ResourceNotFound 
    2627from trac.ticket.api import TicketSystem 
    2728from trac.util import embedded_numbers, partition 
    class Ticket(object): 
    6566    def _get_db(self, db): 
    6667        return db or self.env.get_db_cnx() 
    6768 
    68     def _get_db_for_write(self, db): 
    69         if db: 
    70             return (db, False) 
    71         else: 
    72             return (self.env.get_db_cnx(), True) 
    73  
    7469    exists = property(fget=lambda self: self.id is not None) 
    7570 
    7671    def _init_defaults(self, db=None): 
    class Ticket(object): 
    178173    def insert(self, when=None, db=None): 
    179174        """Add ticket to database""" 
    180175        assert not self.exists, 'Cannot insert an existing ticket' 
    181         db, handle_ta = self._get_db_for_write(db) 
    182176 
    183177        # Add a timestamp 
    184178        if when is None: 
    185179            when = datetime.now(utc) 
    186180        self.values['time'] = self.values['changetime'] = when 
    187181 
    188         cursor = db.cursor() 
    189  
    190182        # The owner field defaults to the component owner 
    191183        if self.values.get('component') and not self.values.get('owner'): 
    192184            try: 
    class Ticket(object): 
    213205                    custom_fields.append(fname) 
    214206                else: 
    215207                    std_fields.append(fname) 
    216         cursor.execute("INSERT INTO ticket (%s) VALUES (%s)" 
    217                        % (','.join(std_fields), 
    218                           ','.join(['%s'] * len(std_fields))), 
    219                        [values[name] for name in std_fields]) 
    220         tkt_id = db.get_last_id(cursor, 'ticket') 
    221  
    222         # Insert custom fields 
    223         if custom_fields: 
    224             cursor.executemany("INSERT INTO ticket_custom (ticket,name,value) " 
    225                                "VALUES (%s,%s,%s)", [(tkt_id, name, self[name]) 
    226                                                      for name in custom_fields]) 
    227         if handle_ta: 
    228             db.commit() 
    229208 
    230         self.id = tkt_id 
    231         self.resource = self.resource(id=tkt_id) 
     209        @with_transaction(self.env, db) 
     210        def do_insert(db): 
     211            cursor = db.cursor() 
     212            cursor.execute("INSERT INTO ticket (%s) VALUES (%s)" 
     213                           % (','.join(std_fields), 
     214                              ','.join(['%s'] * len(std_fields))), 
     215                           [values[name] for name in std_fields]) 
     216            self.id = db.get_last_id(cursor, 'ticket') 
     217 
     218            # Insert custom fields 
     219            if custom_fields: 
     220                cursor.executemany("INSERT INTO ticket_custom " 
     221                                   "(ticket,name,value) VALUES (%s,%s,%s)", 
     222                                   [(self.id, name, self[name]) 
     223                                    for name in custom_fields]) 
     224 
     225        self.resource = self.resource(id=self.id) 
    232226        self._old = {} 
    233227 
    234228        for listener in TicketSystem(self.env).change_listeners: 
    class Ticket(object): 
    247241        if not self._old and not comment: 
    248242            return False # Not modified 
    249243 
    250         db, handle_ta = self._get_db_for_write(db) 
    251         cursor = db.cursor() 
    252244        if when is None: 
    253245            when = datetime.now(utc) 
    254246        when_ts = to_timestamp(when) 
    255247 
    256248        if 'component' in self.values: 
    257             # If the component is changed on a 'new' ticket then owner field 
    258             # is updated accordingly. (#623). 
     249            # If the component is changed on a 'new' ticket 
     250            # then owner field is updated accordingly. (#623). 
    259251            if self.values.get('status') == 'new' \ 
    260252                    and 'component' in self._old \ 
    261253                    and 'owner' not in self._old: 
    262254                try: 
    263                     old_comp = Component(self.env, self._old['component'], db) 
     255                    old_comp = Component(self.env, self._old['component']) 
    264256                    old_owner = old_comp.owner or '' 
    265257                    current_owner = self.values.get('owner') or '' 
    266258                    if old_owner == current_owner: 
    267                         new_comp = Component(self.env, self['component'], db) 
     259                        new_comp = Component(self.env, self['component']) 
    268260                        if new_comp.owner: 
    269261                            self['owner'] = new_comp.owner 
    270262                except TracError: 
    class Ticket(object): 
    279271                if cc not in cclist: 
    280272                    cclist.append(cc) 
    281273            self.values['cc'] = ', '.join(cclist) 
    282  
    283         # find cnum if it isn't provided 
    284         if not cnum: 
    285             num = 0 
    286             cursor.execute(""" 
    287                 SELECT DISTINCT tc1.time,COALESCE(tc2.oldvalue,'') 
    288                 FROM ticket_change AS tc1 
    289                   LEFT OUTER JOIN 
    290                     (SELECT time,oldvalue FROM ticket_change 
    291                      WHERE field='comment') AS tc2 
    292                   ON (tc1.time = tc2.time) 
    293                 WHERE ticket=%s ORDER BY tc1.time DESC 
    294                 """, (self.id,)) 
    295             for ts, old in cursor: 
    296                 # Use oldvalue if available, else count edits 
    297                 try: 
    298                     num += int(old.rsplit('.', 1)[-1]) 
    299                     break 
    300                 except ValueError: 
    301                     num += 1 
    302             cnum = str(num + 1) 
    303  
    304         # store fields 
    305         custom_fields = [f['name'] for f in self.fields if f.get('custom')] 
    306         for name in self._old.keys(): 
    307             if name in custom_fields: 
    308                 cursor.execute("SELECT * FROM ticket_custom "  
    309                                "WHERE ticket=%s and name=%s", (self.id, name)) 
    310                 if cursor.fetchone(): 
    311                     cursor.execute("UPDATE ticket_custom SET value=%s " 
    312                                    "WHERE ticket=%s AND name=%s", 
    313                                    (self[name], self.id, name)) 
     274             
     275        @with_transaction(self.env, db) 
     276        def do_save(db): 
     277            cursor = db.cursor() 
     278             
     279            # find cnum if it isn't provided 
     280            comment_num = cnum 
     281            if not comment_num: 
     282                num = 0 
     283                cursor.execute(""" 
     284                    SELECT DISTINCT tc1.time,COALESCE(tc2.oldvalue,'') 
     285                    FROM ticket_change AS tc1 
     286                      LEFT OUTER JOIN 
     287                        (SELECT time,oldvalue FROM ticket_change 
     288                        WHERE field='comment') AS tc2 
     289                      ON (tc1.time = tc2.time) 
     290                    WHERE ticket=%s ORDER BY tc1.time DESC 
     291                    """, (self.id,)) 
     292                for ts, old in cursor: 
     293                    # Use oldvalue if available, else count edits 
     294                    try: 
     295                        num += int(old.rsplit('.', 1)[-1]) 
     296                        break 
     297                    except ValueError: 
     298                        num += 1 
     299                comment_num = str(num + 1) 
     300 
     301            # store fields 
     302            custom_fields = [f['name'] for f in self.fields if f.get('custom')] 
     303             
     304            for name in self._old.keys(): 
     305                if name in custom_fields: 
     306                    cursor.execute(""" 
     307                        SELECT * FROM ticket_custom 
     308                        WHERE ticket=%s and name=%s 
     309                        """, (self.id, name)) 
     310                    if cursor.fetchone(): 
     311                        cursor.execute(""" 
     312                            UPDATE ticket_custom SET value=%s 
     313                            WHERE ticket=%s AND name=%s 
     314                            """, (self[name], self.id, name)) 
     315                    else: 
     316                        cursor.execute(""" 
     317                            INSERT INTO ticket_custom (ticket,name,value) 
     318                            VALUES(%s,%s,%s) 
     319                            """, (self.id, name, self[name])) 
    314320                else: 
    315                     cursor.execute("INSERT INTO ticket_custom (ticket,name," 
    316                                    "value) VALUES(%s,%s,%s)", 
    317                                    (self.id, name, self[name])) 
    318             else: 
    319                 cursor.execute("UPDATE ticket SET %s=%%s WHERE id=%%s" % name, 
    320                                (self[name], self.id)) 
    321             cursor.execute("INSERT INTO ticket_change " 
    322                            "(ticket,time,author,field,oldvalue,newvalue) " 
    323                            "VALUES (%s, %s, %s, %s, %s, %s)", 
    324                            (self.id, when_ts, author, name, self._old[name], 
    325                             self[name])) 
    326          
    327         # always save comment, even if empty (numbering support for timeline) 
    328         cursor.execute("INSERT INTO ticket_change " 
    329                        "(ticket,time,author,field,oldvalue,newvalue) " 
    330                        "VALUES (%s,%s,%s,'comment',%s,%s)", 
    331                        (self.id, when_ts, author, cnum, comment)) 
    332  
    333         cursor.execute("UPDATE ticket SET changetime=%s WHERE id=%s", 
    334                        (when_ts, self.id)) 
     321                    cursor.execute("UPDATE ticket SET %s=%%s WHERE id=%%s" 
     322                                   % name, (self[name], self.id)) 
     323                cursor.execute(""" 
     324                    INSERT INTO ticket_change 
     325                        (ticket,time,author,field,oldvalue,newvalue) 
     326                    VALUES (%s, %s, %s, %s, %s, %s) 
     327                    """, (self.id, when_ts, author, name, self._old[name], 
     328                          self[name])) 
     329             
     330            # always save comment, even if empty (numbering support for timeline) 
     331            cursor.execute(""" 
     332                INSERT INTO ticket_change 
     333                    (ticket,time,author,field,oldvalue,newvalue) 
     334                VALUES (%s,%s,%s,'comment',%s,%s) 
     335                """, (self.id, when_ts, author, comment_num, comment)) 
     336     
     337            cursor.execute("UPDATE ticket SET changetime=%s WHERE id=%s", 
     338                           (when_ts, self.id)) 
    335339 
    336         if handle_ta: 
    337             db.commit() 
    338340        old_values = self._old 
    339341        self._old = {} 
    340342        self.values['changetime'] = when 
    class Ticket(object): 
    387389        return log 
    388390 
    389391    def delete(self, db=None): 
    390         db, handle_ta = self._get_db_for_write(db) 
    391         Attachment.delete_all(self.env, 'ticket', self.id, db) 
    392         cursor = db.cursor() 
    393         cursor.execute("DELETE FROM ticket WHERE id=%s", (self.id,)) 
    394         cursor.execute("DELETE FROM ticket_change WHERE ticket=%s", (self.id,)) 
    395         cursor.execute("DELETE FROM ticket_custom WHERE ticket=%s", (self.id,)) 
    396  
    397         if handle_ta: 
    398             db.commit() 
     392        @with_transaction(self.env, db) 
     393        def do_delete(db): 
     394            Attachment.delete_all(self.env, 'ticket', self.id, db) 
     395            cursor = db.cursor() 
     396            cursor.execute("DELETE FROM ticket WHERE id=%s", (self.id,)) 
     397            cursor.execute("DELETE FROM ticket_change WHERE ticket=%s", 
     398                           (self.id,)) 
     399            cursor.execute("DELETE FROM ticket_custom WHERE ticket=%s", 
     400                           (self.id,)) 
    399401 
    400402        for listener in TicketSystem(self.env).change_listeners: 
    401403            listener.ticket_deleted(self) 
    class Ticket(object): 
    426428        if when is None: 
    427429            when = datetime.now(utc) 
    428430        when_ts = to_timestamp(when) 
    429          
    430         db, handle_ta = self._get_db_for_write(db) 
    431         cursor = db.cursor() 
    432          
    433         # Find the current value of the comment 
    434         cursor.execute("SELECT newvalue FROM ticket_change " 
    435                        "WHERE ticket=%s AND time=%s AND field='comment'", 
    436                        (self.id, ts)) 
    437         old_comment = False 
    438         for old_comment, in cursor: 
    439             break 
    440         if comment == (old_comment or ''): 
    441             return 
    442          
    443         # Comment history is stored in fields named "_comment%d" 
    444         # Find the next edit number 
    445         cursor.execute("SELECT field FROM ticket_change " 
    446                        "WHERE ticket=%%s AND time=%%s AND field %s" 
    447                        % db.like(), 
    448                        (self.id, ts, db.like_escape('_comment') + '%')) 
    449         fields = list(cursor) 
    450         rev = fields and max(int(field[8:]) for field, in fields) + 1 or 0 
    451         cursor.execute("INSERT INTO ticket_change " 
    452                        "(ticket,time,author,field,oldvalue,newvalue) " 
    453                        "VALUES (%s,%s,%s,%s,%s,%s)", 
    454                        (self.id, ts, author, '_comment%d' % rev, 
    455                         old_comment or '', str(when_ts))) 
    456         if old_comment is False: 
    457             # There was no comment field, add one and find the original author 
    458             # in one of the other changed fields 
    459             cursor.execute("SELECT author FROM ticket_change " 
    460                            "WHERE ticket=%%s AND time=%%s AND NOT field %s " 
    461                            "LIMIT 1" % db.like(), 
    462                            (self.id, ts, db.like_escape('_') + '%')) 
    463             old_author = None 
    464             for old_author, in cursor: 
     431 
     432        @with_transaction(self.env, db) 
     433        def do_save(db): 
     434            cursor = db.cursor() 
     435            # Find the current value of the comment 
     436            cursor.execute("SELECT newvalue FROM ticket_change " 
     437                           "WHERE ticket=%s AND time=%s AND field='comment'", 
     438                           (self.id, ts)) 
     439            old_comment = False 
     440            for old_comment, in cursor: 
    465441                break 
     442            if comment == (old_comment or ''): 
     443                return 
     444         
     445            # Comment history is stored in fields named "_comment%d" 
     446            # Find the next edit number 
     447            cursor.execute("SELECT field FROM ticket_change " 
     448                           "WHERE ticket=%%s AND time=%%s AND field %s" 
     449                           % db.like(), 
     450                           (self.id, ts, db.like_escape('_comment') + '%')) 
     451            fields = list(cursor) 
     452            rev = fields and max(int(field[8:]) for field, in fields) + 1 or 0 
    466453            cursor.execute("INSERT INTO ticket_change " 
    467                            "  (ticket,time,author,field,oldvalue,newvalue) " 
    468                            "VALUES (%s,%s,%s,'comment','',%s)", 
    469                            (self.id, ts, old_author, comment)) 
    470         else: 
    471             cursor.execute("UPDATE ticket_change SET newvalue=%s " 
    472                            "WHERE ticket=%s AND time=%s AND field='comment'", 
    473                            (comment, self.id, ts)) 
    474         if handle_ta: 
    475             db.commit() 
     454                           "(ticket,time,author,field,oldvalue,newvalue) " 
     455                           "VALUES (%s,%s,%s,%s,%s,%s)", 
     456                           (self.id, ts, author, '_comment%d' % rev, 
     457                            old_comment or '', str(when_ts))) 
     458            if old_comment is False: 
     459                # There was no comment field, add one, find the original author 
     460                # in one of the other changed fields 
     461                cursor.execute("SELECT author FROM ticket_change " 
     462                               "WHERE ticket=%%s AND time=%%s AND NOT field %s" 
     463                               " LIMIT 1" % db.like(), 
     464                               (self.id, ts, db.like_escape('_') + '%')) 
     465                old_author = None 
     466                for old_author, in cursor: 
     467                    break 
     468                cursor.execute("INSERT INTO ticket_change " 
     469                               " (ticket,time,author,field,oldvalue,newvalue) " 
     470                               "VALUES (%s,%s,%s,'comment','',%s)", 
     471                               (self.id, ts, old_author, comment)) 
     472            else: 
     473                cursor.execute("UPDATE ticket_change SET newvalue=%s " 
     474                               "WHERE ticket=%s AND time=%s AND " 
     475                               "field='comment'", 
     476                               (comment, self.id, ts)) 
    476477 
    477478    def get_comment_history(self, cnum, db=None): 
    478479        db = self._get_db(db) 
    class AbstractEnum(object): 
    583584 
    584585    def delete(self, db=None): 
    585586        assert self.exists, 'Cannot delete non-existent %s' % self.type 
    586         if not db: 
    587             db = self.env.get_db_cnx() 
    588             handle_ta = True 
    589         else: 
    590             handle_ta = False 
    591587 
    592         cursor = db.cursor() 
    593         self.env.log.info('Deleting %s %s' % (self.type, self.name)) 
    594         cursor.execute("DELETE FROM enum WHERE type=%s AND value=%s", 
    595                        (self.type, self._old_value)) 
    596         # Re-order any enums that have higher value than deleted (close gap) 
    597         for enum in list(self.select(self.env)): 
    598             try: 
    599                 if int(enum.value) > int(self._old_value): 
    600                     enum.value = unicode(int(enum.value) - 1) 
    601                     enum.update(db=db) 
    602             except ValueError: 
    603                 pass # Ignore cast error for this non-essential operation 
    604         TicketSystem(self.env).reset_ticket_fields(db) 
    605  
    606         if handle_ta: 
    607             db.commit() 
     588        @with_transaction(self.env, db) 
     589        def do_delete(db): 
     590            cursor = db.cursor() 
     591            self.env.log.info('Deleting %s %s' % (self.type, self.name)) 
     592            cursor.execute("DELETE FROM enum WHERE type=%s AND value=%s", 
     593                           (self.type, self._old_value)) 
     594            # Re-order any enums that have higher value than deleted (close gap) 
     595            for enum in list(self.select(self.env, db)): 
     596                try: 
     597                    if int(enum.value) > int(self._old_value): 
     598                        enum.value = unicode(int(enum.value) - 1) 
     599                        enum.update(db=db) 
     600                except ValueError: 
     601                    pass # Ignore cast error for this non-essential operation 
     602            TicketSystem(self.env).reset_ticket_fields(db) 
    608603        self.value = self._old_value = None 
    609604        self.name = self._old_name = None 
    610605 
    class AbstractEnum(object): 
    613608        self.name = simplify_whitespace(self.name) 
    614609        if not self.name: 
    615610            raise TracError(_('Invalid %(type)s name.', type=self.type)) 
    616         if not db: 
    617             db = self.env.get_db_cnx() 
    618             handle_ta = True 
    619         else: 
    620             handle_ta = False 
    621611 
    622         cursor = db.cursor() 
    623         self.env.log.debug("Creating new %s '%s'" % (self.type, self.name)) 
    624         if not self.value: 
    625             cursor.execute(("SELECT COALESCE(MAX(%s),0) FROM enum " 
    626                             "WHERE type=%%s") % db.cast('value', 'int'), 
    627                            (self.type,)) 
    628             self.value = int(float(cursor.fetchone()[0])) + 1 
    629         cursor.execute("INSERT INTO enum (type,name,value) VALUES (%s,%s,%s)", 
    630                        (self.type, self.name, self.value)) 
    631         TicketSystem(self.env).reset_ticket_fields(db) 
    632  
    633         if handle_ta: 
    634             db.commit() 
     612        @with_transaction(self.env, db) 
     613        def do_insert(db): 
     614            cursor = db.cursor() 
     615            self.env.log.debug("Creating new %s '%s'" % (self.type, self.name)) 
     616            if not self.value: 
     617                cursor.execute(("SELECT COALESCE(MAX(%s),0) FROM enum " 
     618                                "WHERE type=%%s") % db.cast('value', 'int'), 
     619                               (self.type,)) 
     620                self.value = int(float(cursor.fetchone()[0])) + 1 
     621            cursor.execute("INSERT INTO enum (type,name,value) " 
     622                           "VALUES (%s,%s,%s)", 
     623                           (self.type, self.name, self.value)) 
     624            TicketSystem(self.env).reset_ticket_fields(db) 
     625 
    635626        self._old_name = self.name 
    636627        self._old_value = self.value 
    637628 
    class AbstractEnum(object): 
    640631        self.name = simplify_whitespace(self.name) 
    641632        if not self.name: 
    642633            raise TracError(_('Invalid %(type)s name.', type=self.type)) 
    643         if not db: 
    644             db = self.env.get_db_cnx() 
    645             handle_ta = True 
    646         else: 
    647             handle_ta = False 
    648634 
    649         cursor = db.cursor() 
    650         self.env.log.info('Updating %s "%s"' % (self.type, self.name)) 
    651         cursor.execute("UPDATE enum SET name=%s,value=%s " 
    652                        "WHERE type=%s AND name=%s", 
    653                        (self.name, self.value, self.type, self._old_name)) 
    654         if self.name != self._old_name: 
    655             # Update tickets 
    656             cursor.execute("UPDATE ticket SET %s=%%s WHERE %s=%%s" % 
    657                            (self.ticket_col, self.ticket_col), 
    658                            (self.name, self._old_name)) 
    659         TicketSystem(self.env).reset_ticket_fields(db) 
    660  
    661         if handle_ta: 
    662             db.commit() 
     635        @with_transaction(self.env, db) 
     636        def do_update(db): 
     637            cursor = db.cursor() 
     638            self.env.log.info('Updating %s "%s"' % (self.type, self.name)) 
     639            cursor.execute("UPDATE enum SET name=%s,value=%s " 
     640                           "WHERE type=%s AND name=%s", 
     641                           (self.name, self.value, self.type, self._old_name)) 
     642            if self.name != self._old_name: 
     643                # Update tickets 
     644                cursor.execute("UPDATE ticket SET %s=%%s WHERE %s=%%s" % 
     645                               (self.ticket_col, self.ticket_col), 
     646                               (self.name, self._old_name)) 
     647            TicketSystem(self.env).reset_ticket_fields(db) 
     648 
    663649        self._old_name = self.name 
    664650        self._old_value = self.value 
    665651 
    class Component(object): 
    734720 
    735721    def delete(self, db=None): 
    736722        assert self.exists, 'Cannot delete non-existent component' 
    737         if not db: 
    738             db = self.env.get_db_cnx() 
    739             handle_ta = True 
    740         else: 
    741             handle_ta = False 
    742723 
    743         cursor = db.cursor() 
    744         self.env.log.info('Deleting component %s' % self.name) 
    745         cursor.execute("DELETE FROM component WHERE name=%s", (self.name,)) 
    746         self.name = self._old_name = None 
    747         TicketSystem(self.env).reset_ticket_fields(db) 
    748  
    749         if handle_ta: 
    750             db.commit() 
     724        @with_transaction(self.env, db) 
     725        def do_delete(db): 
     726            cursor = db.cursor() 
     727            self.env.log.info('Deleting component %s' % self.name) 
     728            cursor.execute("DELETE FROM component WHERE name=%s", (self.name,)) 
     729            self.name = self._old_name = None 
     730            TicketSystem(self.env).reset_ticket_fields(db) 
    751731 
    752732    def insert(self, db=None): 
    753733        assert not self.exists, 'Cannot insert existing component' 
    754734        self.name = simplify_whitespace(self.name) 
    755735        if not self.name: 
    756736            raise TracError(_('Invalid component name.')) 
    757         if not db: 
    758             db = self.env.get_db_cnx() 
    759             handle_ta = True 
    760         else: 
    761             handle_ta = False 
    762737 
    763         cursor = db.cursor() 
    764         self.env.log.debug("Creating new component '%s'" % self.name) 
    765         cursor.execute("INSERT INTO component (name,owner,description) " 
    766                        "VALUES (%s,%s,%s)", 
    767                        (self.name, self.owner, self.description)) 
    768         self._old_name = self.name 
    769         TicketSystem(self.env).reset_ticket_fields(db) 
    770  
    771         if handle_ta: 
    772             db.commit() 
     738        @with_transaction(self.env, db) 
     739        def do_insert(db): 
     740            cursor = db.cursor() 
     741            self.env.log.debug("Creating new component '%s'" % self.name) 
     742            cursor.execute("INSERT INTO component (name,owner,description) " 
     743                           "VALUES (%s,%s,%s)", 
     744                           (self.name, self.owner, self.description)) 
     745            self._old_name = self.name 
     746            TicketSystem(self.env).reset_ticket_fields(db) 
    773747 
    774748    def update(self, db=None): 
    775749        assert self.exists, 'Cannot update non-existent component' 
    776750        self.name = simplify_whitespace(self.name) 
    777751        if not self.name: 
    778752            raise TracError(_('Invalid component name.')) 
    779         if not db: 
    780             db = self.env.get_db_cnx() 
    781             handle_ta = True 
    782         else: 
    783             handle_ta = False 
    784753 
    785         cursor = db.cursor() 
    786         self.env.log.info('Updating component "%s"' % self.name) 
    787         cursor.execute("UPDATE component SET name=%s,owner=%s,description=%s " 
    788                        "WHERE name=%s", 
    789                        (self.name, self.owner, self.description, 
    790                         self._old_name)) 
    791         if self.name != self._old_name: 
    792             # Update tickets 
    793             cursor.execute("UPDATE ticket SET component=%s WHERE component=%s", 
    794                            (self.name, self._old_name)) 
    795             self._old_name = self.name 
    796         TicketSystem(self.env).reset_ticket_fields(db) 
    797  
    798         if handle_ta: 
    799             db.commit() 
     754        @with_transaction(self.env, db) 
     755        def do_update(db): 
     756            cursor = db.cursor() 
     757            self.env.log.info('Updating component "%s"' % self.name) 
     758            cursor.execute("UPDATE component SET name=%s,owner=%s," 
     759                           "description=%s WHERE name=%s", 
     760                           (self.name, self.owner, self.description, 
     761                            self._old_name)) 
     762            if self.name != self._old_name: 
     763                # Update tickets 
     764                cursor.execute("UPDATE ticket SET component=%s " 
     765                               "WHERE component=%s", 
     766                               (self.name, self._old_name)) 
     767                self._old_name = self.name 
     768            TicketSystem(self.env).reset_ticket_fields(db) 
    800769 
    801770    @classmethod 
    802771    def select(cls, env, db=None): 
    class Milestone(object): 
    862831                     'description': self.description} 
    863832 
    864833    def delete(self, retarget_to=None, author=None, db=None): 
    865         if not db: 
    866             db = self.env.get_db_cnx() 
    867             handle_ta = True 
    868         else: 
    869             handle_ta = False 
    870  
    871         cursor = db.cursor() 
    872         self.env.log.info('Deleting milestone %s' % self.name) 
    873         cursor.execute("DELETE FROM milestone WHERE name=%s", (self.name,)) 
    874  
    875         # Retarget/reset tickets associated with this milestone 
    876         now = datetime.now(utc) 
    877         cursor.execute("SELECT id FROM ticket WHERE milestone=%s", (self.name,)) 
    878         tkt_ids = [int(row[0]) for row in cursor] 
    879         for tkt_id in tkt_ids: 
    880             ticket = Ticket(self.env, tkt_id, db) 
    881             ticket['milestone'] = retarget_to 
    882             ticket.save_changes(author, 'Milestone %s deleted' % self.name, 
    883                                 now, db=db) 
    884         self._old['name'] = None 
    885         TicketSystem(self.env).reset_ticket_fields(db) 
    886  
    887         if handle_ta: 
    888             db.commit() 
     834        @with_transaction(self.env, db) 
     835        def do_delete(db): 
     836            cursor = db.cursor() 
     837            self.env.log.info('Deleting milestone %s' % self.name) 
     838            cursor.execute("DELETE FROM milestone WHERE name=%s", (self.name,)) 
     839 
     840            # Retarget/reset tickets associated with this milestone 
     841            now = datetime.now(utc) 
     842            cursor.execute("SELECT id FROM ticket WHERE milestone=%s", 
     843                           (self.name,)) 
     844            tkt_ids = [int(row[0]) for row in cursor] 
     845            for tkt_id in tkt_ids: 
     846                ticket = Ticket(self.env, tkt_id, db) 
     847                ticket['milestone'] = retarget_to 
     848                ticket.save_changes(author, 'Milestone %s deleted' % self.name, 
     849                                    now, db=db) 
     850            self._old['name'] = None 
     851            TicketSystem(self.env).reset_ticket_fields(db) 
    889852 
    890853        for listener in TicketSystem(self.env).milestone_change_listeners: 
    891854            listener.milestone_deleted(self) 
    class Milestone(object): 
    894857        self.name = simplify_whitespace(self.name) 
    895858        if not self.name: 
    896859            raise TracError(_('Invalid milestone name.')) 
    897         if not db: 
    898             db = self.env.get_db_cnx() 
    899             handle_ta = True 
    900         else: 
    901             handle_ta = False 
    902860 
    903         cursor = db.cursor() 
    904         self.env.log.debug("Creating new milestone '%s'" % self.name) 
    905         cursor.execute("INSERT INTO milestone (name,due,completed,description) " 
    906                        "VALUES (%s,%s,%s,%s)", 
    907                        (self.name, to_timestamp(self.due), to_timestamp(self.completed), 
    908                         self.description)) 
    909         self._to_old() 
    910         TicketSystem(self.env).reset_ticket_fields(db) 
    911  
    912         if handle_ta: 
    913             db.commit() 
     861        @with_transaction(self.env, db) 
     862        def do_insert(db): 
     863            cursor = db.cursor() 
     864            self.env.log.debug("Creating new milestone '%s'" % self.name) 
     865            cursor.execute("INSERT INTO milestone " 
     866                           "(name,due,completed,description) " 
     867                           "VALUES (%s,%s,%s,%s)", 
     868                           (self.name, to_timestamp(self.due), 
     869                            to_timestamp(self.completed), self.description)) 
     870            self._to_old() 
     871            TicketSystem(self.env).reset_ticket_fields(db) 
    914872 
    915873        for listener in TicketSystem(self.env).milestone_change_listeners: 
    916874            listener.milestone_created(self) 
    class Milestone(object): 
    919877        self.name = simplify_whitespace(self.name) 
    920878        if not self.name: 
    921879            raise TracError(_('Invalid milestone name.')) 
    922         if not db: 
    923             db = self.env.get_db_cnx() 
    924             handle_ta = True 
    925         else: 
    926             handle_ta = False 
    927880 
    928         cursor = db.cursor() 
    929         self.env.log.info('Updating milestone "%s"' % self.name) 
    930         cursor.execute("UPDATE milestone SET name=%s,due=%s," 
    931                        "completed=%s,description=%s WHERE name=%s", 
    932                        (self.name, to_timestamp(self.due), to_timestamp(self.completed), 
    933                         self.description, self._old['name'])) 
    934         self.env.log.info('Updating milestone field of all tickets ' 
    935                           'associated with milestone "%s"' % self.name) 
    936         cursor.execute("UPDATE ticket SET milestone=%s WHERE milestone=%s", 
    937                        (self.name, self._old['name'])) 
    938         TicketSystem(self.env).reset_ticket_fields(db) 
    939  
    940         if handle_ta: 
    941             db.commit() 
     881        @with_transaction(self.env, db) 
     882        def do_insert(db): 
     883            cursor = db.cursor() 
     884            self.env.log.info('Updating milestone "%s"' % self.name) 
     885            cursor.execute("UPDATE milestone SET name=%s,due=%s," 
     886                           "completed=%s,description=%s WHERE name=%s", 
     887                           (self.name, to_timestamp(self.due), 
     888                            to_timestamp(self.completed), 
     889                            self.description, self._old['name'])) 
     890            self.env.log.info('Updating milestone field of all tickets ' 
     891                              'associated with milestone "%s"' % self.name) 
     892            cursor.execute("UPDATE ticket SET milestone=%s WHERE milestone=%s", 
     893                           (self.name, self._old['name'])) 
     894            TicketSystem(self.env).reset_ticket_fields(db) 
    942895 
    943896        old_values = dict((k, v) for k, v in self._old.iteritems() 
    944897                          if getattr(self, k) != v) 
    class Version(object): 
    1000953                raise ResourceNotFound(_('Version %(name)s does not exist.', 
    1001954                                         name=name)) 
    1002955            self.name = self._old_name = name 
    1003             self.time = row[0] and datetime.fromtimestamp(int(row[0]), utc) or None 
     956            self.time = row[0] and datetime.fromtimestamp(int(row[0]), utc) \ 
     957                            or None 
    1004958            self.description = row[1] or '' 
    1005959        else: 
    1006960            self.name = self._old_name = None 
    class Version(object): 
    1011965 
    1012966    def delete(self, db=None): 
    1013967        assert self.exists, 'Cannot delete non-existent version' 
    1014         if not db: 
    1015             db = self.env.get_db_cnx() 
    1016             handle_ta = True 
    1017         else: 
    1018             handle_ta = False 
    1019968 
    1020         cursor = db.cursor() 
    1021         self.env.log.info('Deleting version %s' % self.name) 
    1022         cursor.execute("DELETE FROM version WHERE name=%s", (self.name,)) 
    1023         self.name = self._old_name = None 
    1024         TicketSystem(self.env).reset_ticket_fields(db) 
    1025  
    1026         if handle_ta: 
    1027             db.commit() 
     969        @with_transaction(self.env, db) 
     970        def do_delete(db): 
     971            cursor = db.cursor() 
     972            self.env.log.info('Deleting version %s' % self.name) 
     973            cursor.execute("DELETE FROM version WHERE name=%s", (self.name,)) 
     974            self.name = self._old_name = None 
     975            TicketSystem(self.env).reset_ticket_fields(db) 
    1028976 
    1029977    def insert(self, db=None): 
    1030978        assert not self.exists, 'Cannot insert existing version' 
    1031979        self.name = simplify_whitespace(self.name) 
    1032980        if not self.name: 
    1033981            raise TracError(_('Invalid version name.')) 
    1034         if not db: 
    1035             db = self.env.get_db_cnx() 
    1036             handle_ta = True 
    1037         else: 
    1038             handle_ta = False 
    1039982 
    1040         cursor = db.cursor() 
    1041         self.env.log.debug("Creating new version '%s'" % self.name) 
    1042         cursor.execute("INSERT INTO version (name,time,description) " 
    1043                        "VALUES (%s,%s,%s)", 
    1044                        (self.name, to_timestamp(self.time), self.description)) 
    1045         self._old_name = self.name 
    1046         TicketSystem(self.env).reset_ticket_fields(db) 
    1047  
    1048         if handle_ta: 
    1049             db.commit() 
     983        @with_transaction(self.env, db) 
     984        def do_insert(db): 
     985            cursor = db.cursor() 
     986            self.env.log.debug("Creating new version '%s'" % self.name) 
     987            cursor.execute("INSERT INTO version (name,time,description) " 
     988                           "VALUES (%s,%s,%s)", 
     989                           (self.name, to_timestamp(self.time), 
     990                            self.description)) 
     991            self._old_name = self.name 
     992            TicketSystem(self.env).reset_ticket_fields(db) 
    1050993 
    1051994    def update(self, db=None): 
    1052995        assert self.exists, 'Cannot update non-existent version' 
    1053996        self.name = simplify_whitespace(self.name) 
    1054997        if not self.name: 
    1055998            raise TracError(_('Invalid version name.')) 
    1056         if not db: 
    1057             db = self.env.get_db_cnx() 
    1058             handle_ta = True 
    1059         else: 
    1060             handle_ta = False 
    1061999 
    1062         cursor = db.cursor() 
    1063         self.env.log.info('Updating version "%s"' % self.name) 
    1064         cursor.execute("UPDATE version SET name=%s,time=%s,description=%s " 
    1065                        "WHERE name=%s", 
    1066                        (self.name, to_timestamp(self.time), self.description, 
    1067                         self._old_name)) 
    1068         if self.name != self._old_name: 
    1069             # Update tickets 
    1070             cursor.execute("UPDATE ticket SET version=%s WHERE version=%s", 
    1071                            (self.name, self._old_name)) 
    1072             self._old_name = self.name 
    1073         TicketSystem(self.env).reset_ticket_fields(db) 
    1074  
    1075         if handle_ta: 
    1076             db.commit() 
     1000        @with_transaction(self.env, db) 
     1001        def do_update(db): 
     1002            cursor = db.cursor() 
     1003            self.env.log.info('Updating version "%s"' % self.name) 
     1004            cursor.execute("UPDATE version SET name=%s,time=%s,description=%s " 
     1005                           "WHERE name=%s", 
     1006                           (self.name, to_timestamp(self.time), 
     1007                            self.description, self._old_name)) 
     1008            if self.name != self._old_name: 
     1009                # Update tickets 
     1010                cursor.execute("UPDATE ticket SET version=%s WHERE version=%s", 
     1011                               (self.name, self._old_name)) 
     1012                self._old_name = self.name 
     1013            TicketSystem(self.env).reset_ticket_fields(db) 
    10771014 
    10781015    @classmethod 
    10791016    def select(cls, env, db=None): 
    class Version(object): 
    10851022        for name, time, description in cursor: 
    10861023            version = cls(env) 
    10871024            version.name = version._old_name = name 
    1088             version.time = time and datetime.fromtimestamp(int(time), utc) or None 
     1025            version.time = time and datetime.fromtimestamp(int(time), utc) \ 
     1026                                or None 
    10891027            version.description = description or '' 
    10901028            versions.append(version) 
    10911029        def version_order(v): 
  • trac/ticket/report.py

    diff --git a/trac/ticket/report.py b/trac/ticket/report.py
    index 817692a..828452a 100644
    a b from genshi.builder import tag 
    2525from trac.config import IntOption 
    2626from trac.core import * 
    2727from trac.db import get_column_names 
     28from trac.db.util import with_transaction 
    2829from trac.mimeview import Context 
    2930from trac.perm import IPermissionRequestor 
    3031from trac.resource import Resource, ResourceNotFound 
    class ReportModule(Component): 
    9293        data = {} 
    9394        if req.method == 'POST': 
    9495            if action == 'new': 
    95                 self._do_create(req, db) 
     96                self._do_create(req) 
    9697            elif action == 'delete': 
    97                 self._do_delete(req, db, id) 
     98                self._do_delete(req, id) 
    9899            elif action == 'edit': 
    99                 self._do_save(req, db, id) 
     100                self._do_save(req, id) 
    100101        elif action in ('copy', 'edit', 'new'): 
    101102            template = 'report_edit.html' 
    102103            data = self._render_editor(req, db, id, action=='copy') 
    class ReportModule(Component): 
    133134 
    134135    # Internal methods 
    135136 
    136     def _do_create(self, req, db): 
     137    def _do_create(self, req): 
    137138        req.perm.require('REPORT_CREATE') 
    138139 
    139140        if 'cancel' in req.args: 
    class ReportModule(Component): 
    142143        title = req.args.get('title', '') 
    143144        query = req.args.get('query', '') 
    144145        description = req.args.get('description', '') 
    145         cursor = db.cursor() 
    146         cursor.execute("INSERT INTO report (title,query,description) " 
    147                        "VALUES (%s,%s,%s)", (title, query, description)) 
    148         id = db.get_last_id(cursor, 'report') 
    149         db.commit() 
     146        report_id = [ None ] 
     147        @with_transaction(self.env) 
     148        def do_create(db): 
     149            cursor = db.cursor() 
     150            cursor.execute("INSERT INTO report (title,query,description) " 
     151                           "VALUES (%s,%s,%s)", (title, query, description)) 
     152            report_id[0] = db.get_last_id(cursor, 'report') 
    150153        add_notice(req, _('The report has been created.')) 
    151         req.redirect(req.href.report(id)) 
     154        req.redirect(req.href.report(report_id[0])) 
    152155 
    153     def _do_delete(self, req, db, id): 
     156    def _do_delete(self, req, id): 
    154157        req.perm.require('REPORT_DELETE') 
    155158 
    156159        if 'cancel' in req.args: 
    157160            req.redirect(req.href.report(id)) 
    158161 
    159         cursor = db.cursor() 
    160         cursor.execute("DELETE FROM report WHERE id=%s", (id,)) 
    161         db.commit() 
     162        @with_transaction(self.env) 
     163        def do_delete(db): 
     164            cursor = db.cursor() 
     165            cursor.execute("DELETE FROM report WHERE id=%s", (id,)) 
    162166        add_notice(req, _('The report {%(id)d} has been deleted.', id=id)) 
    163167        req.redirect(req.href.report()) 
    164168 
    165     def _do_save(self, req, db, id): 
     169    def _do_save(self, req, id): 
    166170        """Save report changes to the database""" 
    167171        req.perm.require('REPORT_MODIFY') 
    168172 
    class ReportModule(Component): 
    170174            title = req.args.get('title', '') 
    171175            query = req.args.get('query', '') 
    172176            description = req.args.get('description', '') 
    173             cursor = db.cursor() 
    174             cursor.execute("UPDATE report SET title=%s,query=%s,description=%s " 
    175                            "WHERE id=%s", (title, query, description, id)) 
    176             db.commit() 
     177            @with_transaction(self.env) 
     178            def do_save(db): 
     179                cursor = db.cursor() 
     180                cursor.execute("UPDATE report " 
     181                               "SET title=%s,query=%s,description=%s " 
     182                               "WHERE id=%s", (title, query, description, id)) 
    177183            add_notice(req, _('Your changes have been saved.')) 
    178184        req.redirect(req.href.report(id)) 
    179185 
  • trac/ticket/roadmap.py

    diff --git a/trac/ticket/roadmap.py b/trac/ticket/roadmap.py
    index e8f896e..9bc1431 100644
    a b from trac import __version__ 
    2626from trac.attachment import AttachmentModule 
    2727from trac.config import ExtensionOption 
    2828from trac.core import * 
     29from trac.db.util import with_transaction 
    2930from trac.mimeview import Context 
    3031from trac.perm import IPermissionRequestor 
    3132from trac.resource import * 
    class MilestoneModule(Component): 
    587588            elif action == 'edit': 
    588589                return self._do_save(req, db, milestone) 
    589590            elif action == 'delete': 
    590                 self._do_delete(req, db, milestone) 
     591                self._do_delete(req, milestone) 
    591592        elif action in ('new', 'edit'): 
    592593            return self._render_editor(req, db, milestone) 
    593594        elif action == 'delete': 
    class MilestoneModule(Component): 
    600601 
    601602    # Internal methods 
    602603 
    603     def _do_delete(self, req, db, milestone): 
     604    def _do_delete(self, req, milestone): 
    604605        req.perm(milestone.resource).require('MILESTONE_DELETE') 
    605606 
    606607        retarget_to = None 
    607608        if req.args.has_key('retarget'): 
    608609            retarget_to = req.args.get('target') or None 
    609610        milestone.delete(retarget_to, req.authname) 
    610         db.commit() 
    611611        add_notice(req, _('The milestone "%(name)s" has been deleted.', 
    612612                          name=milestone.name)) 
    613613        req.redirect(req.href.roadmap()) 
    class MilestoneModule(Component): 
    671671            milestone.update() 
    672672            # eventually retarget opened tickets associated with the milestone 
    673673            if 'retarget' in req.args and completed: 
    674                 cursor = db.cursor() 
    675                 cursor.execute("UPDATE ticket SET milestone=%s WHERE " 
    676                                "milestone=%s and status != 'closed'", 
    677                                 (retarget_to, old_name)) 
    678                 self.env.log.info('Tickets associated with milestone %s ' 
    679                                   'retargeted to %s' % (old_name, retarget_to)) 
     674                @with_transaction(self.env) 
     675                def retarget(db): 
     676                    cursor = db.cursor() 
     677                    cursor.execute("UPDATE ticket SET milestone=%s WHERE " 
     678                                   "milestone=%s and status != 'closed'", 
     679                                   (retarget_to, old_name)) 
     680                    self.env.log.info('Tickets associated with milestone %s ' 
     681                                      'retargeted to %s' 
     682                                      % (old_name, retarget_to)) 
    680683        else: 
    681684            milestone.insert() 
    682         db.commit() 
    683685 
    684686        add_notice(req, _('Your changes have been saved.')) 
    685687        req.redirect(req.href.milestone(milestone.name)) 
  • trac/versioncontrol/api.py

    diff --git a/trac/versioncontrol/api.py b/trac/versioncontrol/api.py
    index 4d2f06e..1727603 100644
    a b except ImportError: 
    2626from trac.admin import AdminCommandError, IAdminCommandProvider 
    2727from trac.config import ListOption, Option 
    2828from trac.core import * 
     29from trac.db.util import with_transaction 
    2930from trac.resource import IResourceManager, Resource, ResourceNotFound 
    3031from trac.util.text import printout, to_unicode 
    3132from trac.util.translation import _ 
    class DbRepositoryProvider(Component): 
    206207        if type_ and type_ not in rm.get_supported_types(): 
    207208            raise TracError(_("The repository type '%(type)s' is not " 
    208209                              "supported", type=type_)) 
    209         db = self.env.get_db_cnx() 
    210         id = rm.get_repository_id(reponame, db) 
    211         cursor = db.cursor() 
    212         cursor.executemany("INSERT INTO repository (id, name, value) " 
    213                            "VALUES (%s, %s, %s)", 
    214                            [(id, 'dir', dir), 
    215                             (id, 'type', type_ or '')]) 
    216         db.commit() 
     210        @with_transaction(self.env) 
     211        def do_add(db): 
     212            id = rm.get_repository_id(reponame, db) 
     213            cursor = db.cursor() 
     214            cursor.executemany("INSERT INTO repository (id, name, value) " 
     215                               "VALUES (%s, %s, %s)", 
     216                               [(id, 'dir', dir), 
     217                                (id, 'type', type_ or '')]) 
    217218        rm.reload_repositories() 
    218219     
    219220    def add_alias(self, reponame, target): 
    class DbRepositoryProvider(Component): 
    223224        if is_default(target): 
    224225            target = '' 
    225226        rm = RepositoryManager(self.env) 
    226         db = self.env.get_db_cnx() 
    227         id = rm.get_repository_id(reponame, db) 
    228         cursor = db.cursor() 
    229         cursor.executemany("INSERT INTO repository (id, name, value) " 
    230                            "VALUES (%s, %s, %s)", 
    231                            [(id, 'dir', None), 
    232                             (id, 'alias', target)]) 
    233         db.commit() 
     227        @with_transaction(self.env) 
     228        def do_add(db): 
     229            id = rm.get_repository_id(reponame, db) 
     230            cursor = db.cursor() 
     231            cursor.executemany("INSERT INTO repository (id, name, value) " 
     232                               "VALUES (%s, %s, %s)", 
     233                               [(id, 'dir', None), 
     234                                (id, 'alias', target)]) 
    234235        rm.reload_repositories() 
    235236     
    236237    def remove_repository(self, reponame): 
    class DbRepositoryProvider(Component): 
    238239        if is_default(reponame): 
    239240            reponame = '' 
    240241        rm = RepositoryManager(self.env) 
    241         db = self.env.get_db_cnx() 
    242         id = rm.get_repository_id(reponame, db) 
    243         cursor = db.cursor() 
    244         cursor.execute("DELETE FROM repository WHERE id=%s", (id,)) 
    245         cursor.execute("DELETE FROM revision WHERE repos=%s", (id,)) 
    246         cursor.execute("DELETE FROM node_change WHERE repos=%s", (id,)) 
    247         db.commit() 
     242        @with_transaction(self.env) 
     243        def do_remove(db): 
     244            id = rm.get_repository_id(reponame, db) 
     245            cursor = db.cursor() 
     246            cursor.execute("DELETE FROM repository WHERE id=%s", (id,)) 
     247            cursor.execute("DELETE FROM revision WHERE repos=%s", (id,)) 
     248            cursor.execute("DELETE FROM node_change WHERE repos=%s", (id,)) 
    248249        rm.reload_repositories() 
    249250     
    250251    def modify_repository(self, reponame, changes): 
    class DbRepositoryProvider(Component): 
    252253        if is_default(reponame): 
    253254            reponame = '' 
    254255        rm = RepositoryManager(self.env) 
    255         db = self.env.get_db_cnx() 
    256         id = rm.get_repository_id(reponame, db) 
    257         cursor = db.cursor() 
    258         for (k, v) in changes.iteritems(): 
    259             if k not in self.repository_attrs: 
    260                 continue 
    261             if k in('alias', 'name') and is_default(v): 
    262                 v = '' 
    263             cursor.execute("UPDATE repository SET value=%s " 
    264                            "WHERE id=%s AND name=%s", (v, id, k)) 
    265             cursor.execute("SELECT value FROM repository " 
    266                            "WHERE id=%s AND name=%s", (id, k)) 
    267             if not cursor.fetchone(): 
    268                 cursor.execute("INSERT INTO repository (id, name, value) " 
    269                                "VALUES (%s, %s, %s)", (id, k, v)) 
    270         db.commit() 
     256        @with_transaction(self.env) 
     257        def do_modify(db): 
     258            cursor = db.cursor() 
     259            id = rm.get_repository_id(reponame, db) 
     260            for (k, v) in changes.iteritems(): 
     261                if k not in self.repository_attrs: 
     262                    continue 
     263                if k in('alias', 'name') and is_default(v): 
     264                    v = '' 
     265                cursor.execute("UPDATE repository SET value=%s " 
     266                               "WHERE id=%s AND name=%s", (v, id, k)) 
     267                cursor.execute("SELECT value FROM repository " 
     268                               "WHERE id=%s AND name=%s", (id, k)) 
     269                if not cursor.fetchone(): 
     270                    cursor.execute("INSERT INTO repository (id, name, value) " 
     271                                   "VALUES (%s, %s, %s)", (id, k, v)) 
    271272        rm.reload_repositories() 
    272273 
    273274 
    class RepositoryManager(Component): 
    460461        id = cursor.fetchone()[0] + 1 
    461462        cursor.execute("INSERT INTO repository (id, name, value) " 
    462463                       "VALUES (%s,%s,%s)", (id, 'name', reponame)) 
    463         if handle_ta: 
    464             db.commit() 
    465464        return id 
    466465     
    467466    def get_repository(self, reponame): 
  • trac/versioncontrol/cache.py

    diff --git a/trac/versioncontrol/cache.py b/trac/versioncontrol/cache.py
    index 0618f4e..6028feb 100644
    a b import os 
    1919 
    2020from trac.cache import CacheProxy 
    2121from trac.core import TracError 
     22from trac.db.util import with_transaction 
    2223from trac.util.datefmt import utc, to_timestamp 
    2324from trac.util.translation import _ 
    2425from trac.versioncontrol import Changeset, Node, Repository, NoSuchChangeset 
    class CachedRepository(Repository): 
    8485 
    8586    def sync_changeset(self, rev): 
    8687        cset = self.repos.get_changeset(rev) 
    87         db = self.env.get_db_cnx() 
    88         cursor = db.cursor() 
    89         cursor.execute("SELECT time,author,message FROM revision " 
    90                        "WHERE repos=%s AND rev=%s", 
    91                        (self.id, str(cset.rev))) 
    92         old_cset = None 
    93         for time, author, message in cursor: 
    94             date = datetime.fromtimestamp(time, utc) 
    95             old_cset = Changeset(self.repos, cset.rev, message, author, date) 
    96          
    97         cursor.execute("UPDATE revision SET time=%s, author=%s, message=%s " 
    98                        "WHERE repos=%s AND rev=%s", 
    99                        (to_timestamp(cset.date), cset.author, cset.message, 
    100                         self.id, str(cset.rev))) 
    101         db.commit() 
    102         return old_cset 
     88        old_cset = [None] 
     89 
     90        @with_transaction(self.env) 
     91        def do_sync(db): 
     92            cursor = db.cursor() 
     93            cursor.execute(""" 
     94                SELECT time,author,message FROM revision 
     95                WHERE repos=%s AND rev=%s 
     96                """, (self.id, str(cset.rev))) 
     97            for time, author, message in cursor: 
     98                date = datetime.fromtimestamp(time, utc) 
     99                old_cset[0] = Changeset(self.repos, cset.rev, message, author, 
     100                                        date) 
     101                cursor.execute(""" 
     102                    UPDATE revision SET time=%s, author=%s, message=%s 
     103                    WHERE repos=%s AND rev=%s 
     104                    """, (to_timestamp(cset.date), cset.author, 
     105                          cset.message, self.id, str(cset.rev))) 
     106        return old_cset[0] 
    103107         
    104108    def _metadata(self, db): 
    105109        """Retrieve data for the cached `metadata` attribute.""" 
  • trac/web/auth.py

    diff --git a/trac/web/auth.py b/trac/web/auth.py
    index 803a674..66dc092 100644
    a b from genshi.builder import tag 
    3232 
    3333from trac.config import BoolOption, IntOption 
    3434from trac.core import * 
     35from trac.db.util import with_transaction 
    3536from trac.web.api import IAuthenticator, IRequestHandler 
    3637from trac.web.chrome import INavigationContributor 
    3738from trac.util import hex_entropy, md5, md5crypt 
    class LoginModule(Component): 
    146147               _('Already logged in as %(user)s.', user=req.authname) 
    147148 
    148149        cookie = hex_entropy() 
    149         db = self.env.get_db_cnx() 
    150         cursor = db.cursor() 
    151         cursor.execute("INSERT INTO auth_cookie (cookie,name,ipnr,time) " 
    152                        "VALUES (%s, %s, %s, %s)", (cookie, remote_user, 
    153                        req.remote_addr, int(time.time()))) 
    154         db.commit() 
    155  
     150        @with_transaction(self.env) 
     151        def store_session_cookie(db): 
     152            cursor = db.cursor() 
     153            cursor.execute("INSERT INTO auth_cookie (cookie,name,ipnr,time) " 
     154                           "VALUES (%s, %s, %s, %s)", 
     155                           (cookie, remote_user, 
     156                            req.remote_addr, int(time.time()))) 
    156157        req.authname = remote_user 
    157158        req.outcookie['trac_auth'] = cookie 
    158159        req.outcookie['trac_auth']['path'] = req.base_path or '/' 
    class LoginModule(Component): 
    172173 
    173174        # While deleting this cookie we also take the opportunity to delete 
    174175        # cookies older than 10 days 
    175         db = self.env.get_db_cnx() 
    176         cursor = db.cursor() 
    177         cursor.execute("DELETE FROM auth_cookie WHERE name=%s OR time < %s", 
    178                        (req.authname, int(time.time()) - 86400 * 10)) 
    179         db.commit() 
     176        @with_transaction(self.env) 
     177        def delete_session_cookie(db): 
     178            cursor = db.cursor() 
     179            cursor.execute("DELETE FROM auth_cookie " 
     180                           "WHERE name=%s OR time < %s", 
     181                           (req.authname, int(time.time()) - 86400 * 10)) 
    180182        self._expire_cookie(req) 
    181183        custom_redirect = self.config['metanav'].get('logout.redirect') 
    182184        if custom_redirect: 
  • trac/web/session.py

    diff --git a/trac/web/session.py b/trac/web/session.py
    index 07e47e1..cf93752 100644
    a b  
    2121import time 
    2222 
    2323from trac.core import TracError 
     24from trac.db.util import with_transaction 
    2425from trac.util import hex_entropy 
    2526from trac.util.html import Markup 
    2627 
    class DetachedSession(dict): 
    7879 
    7980        authenticated = int(self.authenticated) 
    8081        now = int(time.time()) 
    81         db = self.env.get_db_cnx() 
    82         cursor = db.cursor() 
    8382 
    84         if self._new: 
    85             self.last_visit = now 
    86             self._new = False 
    87             # The session might already exist even if _new is True since 
    88             # it could have been created by a concurrent request (#3563). 
    89             try: 
    90                 cursor.execute("INSERT INTO session (sid,last_visit,authenticated)" 
    91                                " VALUES(%s,%s,%s)", 
    92                                (self.sid, self.last_visit, authenticated)) 
    93             except Exception, e: 
    94                 db.rollback() 
    95                 self.env.log.warning('Session %s already exists: %s' %  
    96                                      (self.sid, e)) 
    97         if self._old != self: 
    98             attrs = [(self.sid, authenticated, k, v) for k, v in self.items()] 
    99             cursor.execute("DELETE FROM session_attribute WHERE sid=%s", 
    100                            (self.sid,)) 
    101             self._old = dict(self.items()) 
    102             if attrs: 
    103                 # The session variables might already have been updated by a  
    104                 # concurrent request. 
     83        @with_transaction(self.env) 
     84        def delete_session_cookie(db): 
     85            cursor = db.cursor() 
     86            if self._new: 
     87                self.last_visit = now 
     88                self._new = False 
     89                # The session might already exist even if _new is True since 
     90                # it could have been created by a concurrent request (#3563). 
    10591                try: 
    106                     cursor.executemany("INSERT INTO session_attribute " 
    107                                        "(sid,authenticated,name,value) " 
    108                                        "VALUES(%s,%s,%s,%s)", attrs) 
    109                 except Exception, e: 
     92                    cursor.execute("INSERT INTO session " 
     93                                   " (sid,last_visit,authenticated)" 
     94                                   " VALUES (%s,%s,%s)", 
     95                                   (self.sid, self.last_visit, authenticated)) 
     96                except Exception: 
    11097                    db.rollback() 
    111                     self.env.log.warning('Attributes for session %s already ' 
    112                                          'updated: %s' % (self.sid, e)) 
    113             elif not authenticated: 
    114                 # No need to keep around empty unauthenticated sessions 
    115                 cursor.execute("DELETE FROM session " 
    116                                "WHERE sid=%s AND authenticated=0", (self.sid,)) 
    117                 db.commit() 
    118                 return 
    119  
    120         # Update the session last visit time if it is over an hour old, 
    121         # so that session doesn't get purged 
    122         if now - self.last_visit > UPDATE_INTERVAL: 
    123             self.last_visit = now 
    124             self.env.log.info("Refreshing session %s" % self.sid) 
    125             cursor.execute('UPDATE session SET last_visit=%s ' 
    126                            'WHERE sid=%s AND authenticated=%s', 
    127                            (self.last_visit, self.sid, authenticated)) 
    128             # Purge expired sessions. We do this only when the session was 
    129             # changed as to minimize the purging. 
    130             mintime = now - PURGE_AGE 
    131             self.env.log.debug('Purging old, expired, sessions.') 
    132             cursor.execute("DELETE FROM session_attribute " 
    133                            "WHERE authenticated=0 AND sid " 
    134                            "IN (SELECT sid FROM session WHERE " 
    135                            "authenticated=0 AND last_visit < %s)", 
    136                            (mintime,)) 
    137             cursor.execute("DELETE FROM session WHERE " 
    138                            "authenticated=0 AND last_visit < %s", 
    139                            (mintime,)) 
    140         db.commit() 
     98                    self.env.log.warning('Session %s already exists', self.sid) 
     99            if self._old != self: 
     100                attrs = [(self.sid, authenticated, k, v)  
     101                         for k, v in self.items()] 
     102                cursor.execute("DELETE FROM session_attribute WHERE sid=%s", 
     103                               (self.sid,)) 
     104                self._old = dict(self.items()) 
     105                if attrs: 
     106                    # The session variables might already have been updated 
     107                    # by a concurrent request. 
     108                    try: 
     109                        cursor.executemany("INSERT INTO session_attribute " 
     110                                           " (sid,authenticated,name,value) " 
     111                                           " VALUES (%s,%s,%s,%s)", attrs) 
     112                    except Exception: 
     113                        db.rollback() 
     114                        self.env.log.warning('Attributes for session %s ' 
     115                                             'already updated', self.sid) 
     116                elif not authenticated: 
     117                    # No need to keep around empty unauthenticated sessions 
     118                    cursor.execute("DELETE FROM session " 
     119                                   "WHERE sid=%s AND authenticated=0", 
     120                                   (self.sid,)) 
     121                    return 
     122            # Update the session last visit time if it is over an hour old, 
     123            # so that session doesn't get purged 
     124            if now - self.last_visit > UPDATE_INTERVAL: 
     125                self.last_visit = now 
     126                self.env.log.info("Refreshing session %s", self.sid) 
     127                cursor.execute('UPDATE session SET last_visit=%s ' 
     128                               'WHERE sid=%s AND authenticated=%s', 
     129                               (self.last_visit, self.sid, authenticated)) 
     130                # Purge expired sessions. We do this only when the session was 
     131                # changed as to minimize the purging. 
     132                mintime = now - PURGE_AGE 
     133                self.env.log.debug('Purging old, expired, sessions.') 
     134                cursor.execute("DELETE FROM session_attribute " 
     135                               "WHERE authenticated=0 AND sid " 
     136                               "IN (SELECT sid FROM session WHERE " 
     137                               "authenticated=0 AND last_visit < %s)", 
     138                               (mintime,)) 
     139                cursor.execute("DELETE FROM session WHERE " 
     140                               "authenticated=0 AND last_visit < %s", 
     141                               (mintime,)) 
    141142 
    142143 
    143144class Session(DetachedSession): 
    class Session(DetachedSession): 
    187188        assert new_sid, 'Session ID cannot be empty' 
    188189        if new_sid == self.sid: 
    189190            return 
    190         db = self.env.get_db_cnx() 
    191         cursor = db.cursor() 
     191        cursor = self.env.get_db_cnx().cursor() 
    192192        cursor.execute("SELECT sid FROM session WHERE sid=%s", (new_sid,)) 
    193193        if cursor.fetchone(): 
    194194            raise TracError(Markup('Session "%s" already exists.<br />' 
    195195                                   'Please choose a different session ID.') 
    196196                            % new_sid, 'Error renaming session') 
    197         self.env.log.debug('Changing session ID %s to %s' % (self.sid, new_sid)) 
    198         cursor.execute("UPDATE session SET sid=%s WHERE sid=%s " 
    199                        "AND authenticated=0", (new_sid, self.sid)) 
    200         cursor.execute("UPDATE session_attribute SET sid=%s " 
    201                        "WHERE sid=%s and authenticated=0", 
    202                        (new_sid, self.sid)) 
    203         db.commit() 
     197        self.env.log.debug('Changing session ID %s to %s', self.sid, new_sid) 
     198 
     199        @with_transaction(self.env) 
     200        def update_session_id(db): 
     201            cursor = db.cursor() 
     202            cursor.execute("UPDATE session SET sid=%s WHERE sid=%s " 
     203                           "AND authenticated=0", (new_sid, self.sid)) 
     204            cursor.execute("UPDATE session_attribute SET sid=%s " 
     205                           "WHERE sid=%s and authenticated=0", 
     206                           (new_sid, self.sid)) 
    204207        self.sid = new_sid 
    205208        self.bake_cookie() 
    206209 
    class Session(DetachedSession): 
    211214        assert self.req.authname != 'anonymous', \ 
    212215               'Cannot promote session of anonymous user' 
    213216 
    214         db = self.env.get_db_cnx() 
    215         cursor = db.cursor() 
    216         cursor.execute("SELECT authenticated FROM session " 
    217                        "WHERE sid=%s OR sid=%s ", (sid, self.req.authname)) 
    218         authenticated_flags = [row[0] for row in cursor.fetchall()] 
    219          
    220         if len(authenticated_flags) == 2: 
    221             # There's already an authenticated session for the user, we 
    222             # simply delete the anonymous session 
    223             cursor.execute("DELETE FROM session WHERE sid=%s " 
    224                            "AND authenticated=0", (sid,)) 
    225             cursor.execute("DELETE FROM session_attribute WHERE sid=%s " 
    226                            "AND authenticated=0", (sid,)) 
    227         elif len(authenticated_flags) == 1: 
    228             if not authenticated_flags[0]: 
    229                 # Update the anomymous session records so that the session ID 
    230                 # becomes the user name, and set the authenticated flag. 
    231                 self.env.log.debug('Promoting anonymous session %s to ' 
    232                                    'authenticated session for user %s', 
    233                                    sid, self.req.authname) 
    234                 cursor.execute("UPDATE session SET sid=%s,authenticated=1 " 
    235                                "WHERE sid=%s AND authenticated=0", 
    236                                (self.req.authname, sid)) 
    237                 cursor.execute("UPDATE session_attribute " 
    238                                "SET sid=%s,authenticated=1 WHERE sid=%s", 
    239                                (self.req.authname, sid)) 
    240         else: 
    241             # we didn't have an anonymous session for this sid 
    242             cursor.execute("INSERT INTO session (sid,last_visit,authenticated)" 
    243                            " VALUES(%s,%s,1)", 
    244                            (self.req.authname, int(time.time()))) 
     217        @with_transaction(self.env) 
     218        def update_session_id(db): 
     219            cursor = db.cursor() 
     220            cursor.execute("SELECT authenticated FROM session " 
     221                           "WHERE sid=%s OR sid=%s ", (sid, self.req.authname)) 
     222            authenticated_flags = [row[0] for row in cursor.fetchall()] 
     223             
     224            if len(authenticated_flags) == 2: 
     225                # There's already an authenticated session for the user, 
     226                # we simply delete the anonymous session 
     227                cursor.execute("DELETE FROM session WHERE sid=%s " 
     228                               "AND authenticated=0", (sid,)) 
     229                cursor.execute("DELETE FROM session_attribute WHERE sid=%s " 
     230                               "AND authenticated=0", (sid,)) 
     231            elif len(authenticated_flags) == 1: 
     232                if not authenticated_flags[0]: 
     233                    # Update the anomymous session records so the session ID 
     234                    # becomes the user name, and set the authenticated flag. 
     235                    self.env.log.debug('Promoting anonymous session %s to ' 
     236                                       'authenticated session for user %s', 
     237                                       sid, self.req.authname) 
     238                    cursor.execute("UPDATE session SET sid=%s,authenticated=1 " 
     239                                   "WHERE sid=%s AND authenticated=0", 
     240                                   (self.req.authname, sid)) 
     241                    cursor.execute("UPDATE session_attribute " 
     242                                   "SET sid=%s,authenticated=1 WHERE sid=%s", 
     243                                   (self.req.authname, sid)) 
     244            else: 
     245                # we didn't have an anonymous session for this sid 
     246                cursor.execute("INSERT INTO session " 
     247                               "(sid,last_visit,authenticated)" 
     248                               " VALUES(%s,%s,1)", 
     249                               (self.req.authname, int(time.time()))) 
    245250        self._new = False 
    246         db.commit() 
    247251 
    248252        self.sid = sid 
    249253        self.bake_cookie(0) # expire the cookie 
  • trac/wiki/admin.py

    diff --git a/trac/wiki/admin.py b/trac/wiki/admin.py
    index f8d0448..5e7ef75 100644
    a b import time 
    1919 
    2020from trac.admin import * 
    2121from trac.core import * 
     22from trac.db.util import with_transaction 
    2223from trac.wiki import model 
    2324from trac.wiki.api import WikiSystem 
    2425from trac.util import read_file 
    class WikiAdmin(Component): 
    118119        data = to_unicode(data, 'utf-8') 
    119120         
    120121        # Make sure we don't insert the exact same page twice 
    121         handle_ta = not db 
    122         if handle_ta: 
    123             db = self.env.get_db_cnx() 
    124         cursor = db.cursor() 
     122        cursor = self.env.get_db_cnx().cursor() 
     123 
    125124        cursor.execute("SELECT text FROM wiki WHERE name=%s " 
    126125                       "ORDER BY version DESC LIMIT 1", 
    127126                       (title,)) 
    class WikiAdmin(Component): 
    133132            printout('  %s already up to date.' % title) 
    134133            return False 
    135134         
    136         if replace and old: 
    137             cursor.execute("UPDATE wiki SET text=%s WHERE name=%s " 
    138                            "  AND version=(SELECT max(version) FROM wiki " 
    139                            "               WHERE name=%s)", 
    140                            (data, title, title)) 
    141         else: 
    142             cursor.execute("INSERT INTO wiki(version,name,time,author,ipnr," 
    143                            "                 text) " 
    144                            "SELECT 1+COALESCE(max(version),0),%s,%s," 
    145                            "       'trac','127.0.0.1',%s FROM wiki " 
    146                            "WHERE name=%s", 
    147                            (title, int(time.time()), data, title)) 
    148         if not old: 
    149             WikiSystem(self.env).pages.invalidate(db) 
    150         if handle_ta: 
    151             db.commit() 
     135        @with_transaction(self.env, db) 
     136        def do_import(db): 
     137            cursor = db.cursor() 
     138            if replace and old: 
     139                cursor.execute("UPDATE wiki SET text=%s WHERE name=%s " 
     140                               "  AND version=(SELECT max(version) FROM wiki " 
     141                               "               WHERE name=%s)", 
     142                               (data, title, title)) 
     143            else: 
     144                cursor.execute("INSERT INTO wiki(version,name,time,author," 
     145                               "                 ipnr,text) " 
     146                               "SELECT 1+COALESCE(max(version),0),%s,%s," 
     147                               "       'trac','127.0.0.1',%s FROM wiki " 
     148                               "WHERE name=%s", 
     149                               (title, int(time.time()), data, title)) 
     150            if not old: 
     151                WikiSystem(self.env).pages.invalidate(db) 
    152152        return True 
    153153 
    154154    def load_pages(self, dir, db=None, ignore=[], create_only=[], 
    class WikiAdmin(Component): 
    196196                    [_('Title'), _('Edits'), _('Modified')]) 
    197197     
    198198    def _do_remove(self, name): 
    199         db = self.env.get_db_cnx() 
    200         if name.endswith('*'): 
    201             pages = list(WikiSystem(self.env).get_pages(name.rstrip('*') 
    202                                                         or None)) 
    203             for p in pages: 
    204                 page = model.WikiPage(self.env, p, db=db) 
     199        @with_transaction(self.env)         
     200        def do_transaction(db): 
     201            if name.endswith('*'): 
     202                pages = list(WikiSystem(self.env).get_pages(name.rstrip('*') 
     203                                                            or None)) 
     204                for p in pages: 
     205                    page = model.WikiPage(self.env, p, db=db) 
     206                    page.delete(db=db) 
     207                print_table(((p,) for p in pages), [_('Deleted pages')]) 
     208            else: 
     209                page = model.WikiPage(self.env, name, db=db) 
    205210                page.delete(db=db) 
    206             print_table(((p,) for p in pages), [_('Deleted pages')]) 
    207         else: 
    208             page = model.WikiPage(self.env, name, db=db) 
    209             page.delete(db=db) 
    210         db.commit() 
    211211     
    212212    def _do_export(self, page, filename=None): 
    213213        self.export_page(page, filename) 
    class WikiAdmin(Component): 
    236236                self.export_page(p, dst, cursor) 
    237237     
    238238    def _load_or_replace(self, paths, replace): 
    239         db = self.env.get_db_cnx() 
    240         for path in paths: 
    241             if os.path.isdir(path): 
    242                 self.load_pages(path, db, replace=replace) 
    243             else: 
    244                 page = os.path.basename(path) 
    245                 page = unicode_unquote(page.encode('utf-8')) 
    246                 if self.import_page(path, page, db, replace=replace): 
    247                     printout(_("  %(page)s imported from %(filename)s", 
    248                                filename=path, page=page)) 
    249         db.commit() 
     239        @with_transaction(self.env) 
     240        def do_transaction(db): 
     241            for path in paths: 
     242                if os.path.isdir(path): 
     243                    self.load_pages(path, db, replace=replace) 
     244                else: 
     245                    page = os.path.basename(path) 
     246                    page = unicode_unquote(page.encode('utf-8')) 
     247                    if self.import_page(path, page, db, replace=replace): 
     248                        printout(_("  %(page)s imported from %(filename)s", 
     249                                   filename=path, page=page)) 
    250250     
    251251    def _do_load(self, *paths): 
    252252        self._load_or_replace(paths, replace=False) 
    class WikiAdmin(Component): 
    255255        self._load_or_replace(paths, replace=True) 
    256256     
    257257    def _do_upgrade(self): 
    258         db = self.env.get_db_cnx() 
    259         self.load_pages(pkg_resources.resource_filename('trac.wiki',  
    260                                                         'default-pages'), 
    261                         db, ignore=['WikiStart', 'checkwiki.py'], 
    262                         create_only=['InterMapTxt']) 
    263         db.commit() 
     258        @with_transaction(self.env) 
     259        def do_transaction(db): 
     260            self.load_pages(pkg_resources.resource_filename('trac.wiki', 
     261                                                            'default-pages'), 
     262                            db, ignore=['WikiStart', 'checkwiki.py'], 
     263                            create_only=['InterMapTxt']) 
  • trac/wiki/model.py

    diff --git a/trac/wiki/model.py b/trac/wiki/model.py
    index cb29395..d8183df 100644
    a b  
    1919from datetime import datetime 
    2020 
    2121from trac.core import * 
     22from trac.db.util import with_transaction 
    2223from trac.resource import Resource 
    2324from trac.util.datefmt import utc, to_timestamp 
    2425from trac.util.translation import _ 
    class WikiPage(object): 
    7879            self.text = self.comment = self.author = '' 
    7980            self.time = None 
    8081            self.readonly = 0 
    81  
     82             
    8283    exists = property(fget=lambda self: self.version > 0) 
    8384 
    8485    def delete(self, version=None, db=None): 
    8586        assert self.exists, 'Cannot delete non-existent page' 
    86         if not db: 
    87             db = self.env.get_db_cnx() 
    88             handle_ta = True 
    89         else: 
    90             handle_ta = False 
    91  
    92         cursor = db.cursor() 
    93         if version is None: 
    94             # Delete a wiki page completely 
    95             cursor.execute("DELETE FROM wiki WHERE name=%s", (self.name,)) 
    96             self.env.log.info('Deleted page %s' % self.name) 
    97         else: 
    98             # Delete only a specific page version 
    99             cursor.execute("DELETE FROM wiki WHERE name=%s and version=%s", 
    100                            (self.name, version)) 
    101             self.env.log.info('Deleted version %d of page %s' 
    102                               % (version, self.name)) 
    103  
    104         if version is None or version == self.version: 
    105             self._fetch(self.name, None, db) 
    106  
    107         if not self.exists: 
    108             # Invalidate page name cache 
    109             WikiSystem(self.env).pages.invalidate(db) 
    110             # Delete orphaned attachments 
    111             from trac.attachment import Attachment 
    112             Attachment.delete_all(self.env, 'wiki', self.name, db) 
    113  
    114         if handle_ta: 
    115             db.commit() 
     87         
     88        @with_transaction(self.env, db) 
     89        def do_delete(db): 
     90            cursor = db.cursor() 
     91            if version is None: 
     92                # Delete a wiki page completely 
     93                cursor.execute("DELETE FROM wiki WHERE name=%s", (self.name,)) 
     94                self.env.log.info('Deleted page %s' % self.name) 
     95            else: 
     96                # Delete only a specific page version 
     97                cursor.execute("DELETE FROM wiki WHERE name=%s and version=%s", 
     98                               (self.name, version)) 
     99                self.env.log.info('Deleted version %d of page %s' 
     100                                  % (version, self.name)) 
     101 
     102            if version is None or version == self.version: 
     103                self._fetch(self.name, None, db) 
     104 
     105            if not self.exists: 
     106                # Invalidate page name cache 
     107                WikiSystem(self.env).pages.invalidate(db) 
     108                # Delete orphaned attachments 
     109                from trac.attachment import Attachment 
     110                Attachment.delete_all(self.env, 'wiki', self.name, db) 
    116111 
    117112        # Let change listeners know about the deletion 
    118113        if not self.exists: 
    class WikiPage(object): 
    124119                    listener.wiki_page_version_deleted(self) 
    125120 
    126121    def save(self, author, comment, remote_addr, t=None, db=None): 
    127         if not db: 
    128             db = self.env.get_db_cnx() 
    129             handle_ta = True 
    130         else: 
    131             handle_ta = False 
    132  
    133         if t is None: 
    134             t = datetime.now(utc) 
    135  
    136         if self.text != self.old_text: 
    137             cursor = db.cursor() 
    138             cursor.execute("INSERT INTO wiki (name,version,time,author,ipnr," 
    139                            "text,comment,readonly) VALUES (%s,%s,%s,%s,%s,%s," 
    140                            "%s,%s)", (self.name, self.version + 1, 
    141                                       to_timestamp(t), author, remote_addr, 
    142                                       self.text, comment, self.readonly)) 
    143             self.version += 1 
    144             self.resource = self.resource(version=self.version) 
    145         elif self.readonly != self.old_readonly: 
    146             cursor = db.cursor() 
    147             cursor.execute("UPDATE wiki SET readonly=%s WHERE name=%s", 
    148                            (self.readonly, self.name)) 
    149         else: 
     122        new_text = self.text != self.old_text 
     123        if not new_text and self.readonly == self.old_readonly: 
    150124            raise TracError(_('Page not modified')) 
     125        t = t or datetime.now(utc) 
    151126 
    152         if self.version == 1: 
    153             # Invalidate page name cache 
    154             WikiSystem(self.env).pages.invalidate(db) 
    155          
    156         if handle_ta: 
    157             db.commit() 
     127        @with_transaction(self.env, db) 
     128        def do_save(db): 
     129            cursor = db.cursor() 
     130            if new_text: 
     131                cursor.execute("INSERT INTO wiki (name,version,time," 
     132                               "author,ipnr,text,comment,readonly)" 
     133                               "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)", 
     134                               (self.name, self.version + 1, to_timestamp(t), 
     135                                author, remote_addr, self.text, comment, 
     136                                self.readonly)) 
     137                self.version += 1 
     138                self.resource = self.resource(version=self.version) 
     139            else: 
     140                cursor.execute("UPDATE wiki SET readonly=%s WHERE name=%s", 
     141                               (self.readonly, self.name)) 
     142            if self.version == 1: 
     143                # Invalidate page name cache 
     144                WikiSystem(self.env).pages.invalidate(db) 
    158145         
    159146        self.author = author 
    160147        self.comment = comment 
  • trac/wiki/web_ui.py

    diff --git a/trac/wiki/web_ui.py b/trac/wiki/web_ui.py
    index 282dbeb..d8a69fe 100644
    a b from genshi.builder import tag 
    2525from trac.attachment import AttachmentModule 
    2626from trac.config import IntOption 
    2727from trac.core import * 
     28from trac.db.util import with_transaction 
    2829from trac.mimeview.api import Mimeview, IContentConverter, Context 
    2930from trac.perm import IPermissionRequestor 
    3031from trac.resource import * 
    class WikiModule(Component): 
    244245        version = int(req.args.get('version', 0)) or None 
    245246        old_version = int(req.args.get('old_version', 0)) or version 
    246247 
    247         db = self.env.get_db_cnx() 
    248         if version and old_version and version > old_version: 
    249             # delete from `old_version` exclusive to `version` inclusive: 
    250             for v in range(old_version, version): 
    251                 page.delete(v + 1, db) 
    252         else: 
    253             # only delete that `version`, or the whole page if `None` 
    254             page.delete(version, db) 
    255         db.commit() 
     248        @with_transaction(self.env) 
     249        def do_transaction(db): 
     250            if version and old_version and version > old_version: 
     251                # delete from `old_version` exclusive to `version` inclusive: 
     252                for v in range(old_version, version): 
     253                    page.delete(v + 1, db) 
     254            else: 
     255                # only delete that `version`, or the whole page if `None` 
     256                page.delete(version, db) 
    256257 
    257258        if not page.exists: 
    258259            add_notice(req, _('The page %(name)s has been deleted.',