Index: trac/db_default.py
===================================================================
--- trac/db_default.py	(revision 2659)
+++ trac/db_default.py	(working copy)
@@ -18,7 +18,7 @@
 from trac.db import Table, Column, Index
 
 # Database version identifier. Used for automatic upgrades.
-db_version = 16
+db_version = 17
 
 def __mkreports(reports):
     """Utility function used to create report data in same syntax as the
@@ -83,11 +83,11 @@
         Column('author'),
         Column('message'),
         Index(['time'])],
-    Table('node_change', key=('rev', 'path', 'change'))[
+    Table('node_change', key=('rev', 'path', 'change_type'))[
         Column('rev'),
         Column('path'),
-        Column('kind', size=1),
-        Column('change', size=1),
+        Column('node_type', size=1),
+        Column('change_type', size=1),
         Column('base_path'),
         Column('base_rev'),
         Index(['rev'])],
@@ -437,6 +437,7 @@
 )
 
 default_components = ('trac.About', 'trac.attachment',
+                      'trac.db.mysql_backend',
                       'trac.db.postgres_backend', 'trac.db.sqlite_backend',
                       'trac.mimeview.enscript', 'trac.mimeview.patch',
                       'trac.mimeview.php', 'trac.mimeview.rst',
Index: trac/ticket/api.py
===================================================================
--- trac/ticket/api.py	(revision 2659)
+++ trac/ticket/api.py	(working copy)
@@ -167,13 +167,19 @@
             return
         db = self.env.get_db_cnx()
         sql, args = query_to_sql(db, query, 'b.newvalue')
-        sql2, args2 = query_to_sql(db, query, 'summary||keywords||description||reporter||cc')
+        search_fields = ['summary', 'keywords', 'description', 'reporter', 'cc']
+        sql_seq = []
+        for f in search_fields:
+            subsql, subarg = query_to_sql(db, query, f)
+            sql_seq.append(subsql)
+            args += subarg
+        sql2 = ' OR '.join(sql_seq)
         cursor = db.cursor()
-        cursor.execute("SELECT DISTINCT a.summary,a.description,a.reporter, "
-                       "a.keywords,a.id,a.time FROM ticket a "
-                       "LEFT JOIN ticket_change b ON a.id = b.ticket "
-                       "WHERE (b.field='comment' AND %s ) OR %s" % (sql, sql2),
-                       args + args2)
+        sql3 = "SELECT DISTINCT a.summary,a.description,a.reporter, " + \
+                       "a.keywords,a.id,a.time FROM ticket a " +  \
+                       "LEFT JOIN ticket_change b ON a.id = b.ticket " + \
+                       "WHERE (b.field='comment' AND %s ) OR %s" % (sql, sql2)
+        cursor.execute(sql3, args)
         for summary,desc,author,keywords,tid,date in cursor:
             yield (self.env.href.ticket(tid),
                    '#%d: %s' % (tid, util.escape(util.shorten_line(summary))),
Index: trac/db/tests/api.py
===================================================================
--- trac/db/tests/api.py	(revision 2659)
+++ trac/db/tests/api.py	(working copy)
@@ -39,8 +39,15 @@
                                        'host': 'localhost', 'port': 9431,
                                        'path': '/trac'}),
                          _parse_db_str('postgres://john:letmein@localhost:9431/trac'))
+    def test_mysql_simple(self):
+        self.assertEqual(('mysql', {'host': 'localhost', 'path': '/trac'}),
+                         _parse_db_str('mysql://localhost/trac'))
 
-
+    def test_mysql_with_creds(self):
+        self.assertEqual(('mysql', {'user': 'john', 'password': 'letmein',
+                                       'host': 'localhost', 'port': 3306,
+                                       'path': '/trac'}),
+                         _parse_db_str('mysql://john:letmein@localhost:3306/trac'))
 def suite():
     return unittest.makeSuite(ParseConnectionStringTestCase,'test')
 
Index: trac/db/mysql_backend.py
===================================================================
--- trac/db/mysql_backend.py	(revision 0)
+++ trac/db/mysql_backend.py	(revision 0)
@@ -0,0 +1,138 @@
+# -*- coding: iso8859-1 -*-
+#
+# Copyright (C) 2005 Edgewall Software
+# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
+# Copyright (C) 2005 Jeff Weiss <trac@jeffweiss.org>
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.com/license.html.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://projects.edgewall.com/trac/.
+#
+# Derived from postgres_backend.py
+# Author: Christopher Lenz <cmlenz@gmx.de> 
+# Author: Jeff Weiss <trac@jeffweiss.org>
+
+from trac.core import *
+from trac.db.api import IDatabaseConnector
+from trac.db.util import ConnectionWrapper
+
+import MySQLdb
+
+class MySQLConnector(Component):
+    """MySQL database support.  Still extremely experimental!
+Currently only supports database urls of the following formats:
+mysql://user:password@host/database
+mysql://user:password@host:port/database
+"""
+
+    implements(IDatabaseConnector)
+
+    def get_supported_schemes(self):
+        return [('mysql', 1)]
+
+    def get_connection(self, path, user=None, password=None, host=None,
+                       port=None, params={}):
+        return MySQLConnection(path, user, password, host, port, params)
+
+    def init_db(self, path, user=None, password=None, host=None, port=None,
+                params={}):
+        cnx = self.get_connection(path, user, password, host, port, params)
+        cursor = cnx.cursor()
+        from trac.db_default import schema
+        for table in schema:
+            for stmt in self.to_sql(table):
+                self.env.log.debug(stmt)
+                cursor.execute(stmt)
+        cnx.commit()
+
+    def to_sql(self, table):
+        sql = ["CREATE TABLE %s (" % table.name]
+        coldefs = []
+        textkeys = []
+        textcols = []
+        mytablekey = []
+        num_key_text = 0
+        for column in table.columns:
+            ctype = column.type
+            if column.auto_increment:
+                ctype = "INT AUTO_INCREMENT"
+                # Overide the column type because otherwise it will screw
+                # up the rest of the code for indexes and constraints
+                column.type = 'int'
+            if column.name in table.key:
+                mytablekey.append(column.name)
+                if column.type == 'text':
+                    num_key_text += 1
+                    textkeys.append(column.name)
+            if column.type == 'text':
+                textcols.append(column.name)
+            coldefs.append("    `%s` %s" % (column.name, ctype))
+        if len(table.key) >= 1:
+            if num_key_text == 0:
+                num_key_text = 1
+            size = 767 / num_key_text
+            keysize = "(%s)" % size
+            for key in textkeys:
+                if key in mytablekey:
+                    mytablekey.remove(key)
+                sizedkey =  '`'+key+'`' + keysize
+                mytablekey.append(sizedkey)
+            coldefs.append("    CONSTRAINT %s_pk PRIMARY KEY (%s)"
+                           % (table.name, ','.join(mytablekey)))
+        sql.append(',\n'.join(coldefs) + '\n)')
+        yield '\n'.join(sql)
+# This does not work because what I need to do is not add something about
+# the index size for the text fields because MySQL apparrently can't index
+# on the entire length of the text field
+        for index in table.indices:
+            myidxcols = set([])
+            processedidx = set([])
+            for col in index.columns:
+                if col in textcols:
+                    myidxcols.add(col)
+            myidx = set(index.columns) - myidxcols
+            for col in myidx:
+               escaped_col = '`'+col+'`'
+               processedidx.add(escaped_col) 
+            if len(myidxcols) > 0:
+                size = 767 / len(myidxcols)
+                idxsize  = "(%s)" % size
+                for col in myidxcols:
+                   sizedidx = '`'+col+'`'+idxsize
+                   processedidx.add(sizedidx) 
+            yield "CREATE INDEX %s_%s_idx ON %s (%s)" % (table.name, 
+                   '_'.join(index.columns), table.name, ','.join(processedidx))
+
+
+class MySQLConnection(ConnectionWrapper):
+    """Connection wrapper for MySQL."""
+
+    poolable = True
+
+    def __init__(self, path, usr=None, password=None, hst=None, prt=None,
+                 params={}):
+        if prt:
+            cnx = MySQLdb.connect(db=path[1:], user=usr, passwd=password, host=hst, port=prt)
+        else:
+            cnx = MySQLdb.connect(db=path[1:], user=usr, passwd=password, host=hst)
+
+        ConnectionWrapper.__init__(self, cnx)
+
+    def cast(self, column, type):
+        # Temporary hack needed for the union of selects in the search module
+        return 'CAST(%s AS %s)' % (column, type)
+
+    def like(self):
+        # Temporary hack needed for the case-insensitive string matching in the
+        # search module
+        return 'LIKE'
+
+    def get_last_id(self, cursor, table, column='id'):
+        sql = "SELECT MAX(%s) FROM %s" % (column, table)
+        cursor.execute(sql)
+        return cursor.fetchone()[0]
Index: trac/versioncontrol/api.py
===================================================================
--- trac/versioncontrol/api.py	(revision 2659)
+++ trac/versioncontrol/api.py	(working copy)
@@ -132,11 +132,11 @@
     DIRECTORY = "dir"
     FILE = "file"
 
-    def __init__(self, path, rev, kind):
-        assert kind in (Node.DIRECTORY, Node.FILE), "Unknown node kind %s" % kind
+    def __init__(self, path, rev, node_type):
+        assert node_type in (Node.DIRECTORY, Node.FILE), "Unknown node node_type %s" % node_type
         self.path = str(path)
         self.rev = rev
-        self.kind = kind
+        self.node_type = node_type
 
     def get_content(self):
         """
@@ -187,8 +187,8 @@
         raise NotImplementedError
     last_modified = property(lambda x: x.get_last_modified())
 
-    isdir = property(lambda x: x.kind == Node.DIRECTORY)
-    isfile = property(lambda x: x.kind == Node.FILE)
+    isdir = property(lambda x: x.node_type == Node.DIRECTORY)
+    isfile = property(lambda x: x.node_type == Node.FILE)
 
 
 class Changeset(object):
@@ -210,10 +210,10 @@
 
     def get_changes(self):
         """
-        Generator that produces a (path, kind, change, base_rev, base_path)
+        Generator that produces a (path, node_type, change_type, base_rev, base_path)
         tuple for every change in the changeset, where change can be one of
         Changeset.ADD, Changeset.COPY, Changeset.DELETE, Changeset.EDIT or
-        Changeset.MOVE, and kind is one of Node.FILE or Node.DIRECTORY.
+        Changeset.MOVE, and node_type is one of Node.FILE or Node.DIRECTORY.
         """
         raise NotImplementedError
 
Index: trac/versioncontrol/tests/svn_fs.py
===================================================================
--- trac/versioncontrol/tests/svn_fs.py	(revision 2659)
+++ trac/versioncontrol/tests/svn_fs.py	(working copy)
@@ -117,13 +117,13 @@
         node = self.repos.get_node('/trunk')
         self.assertEqual('trunk', node.name)
         self.assertEqual('/trunk', node.path)
-        self.assertEqual(Node.DIRECTORY, node.kind)
+        self.assertEqual(Node.DIRECTORY, node.node_type)
         self.assertEqual(14, node.rev)
         self.assertEqual(1133340423L, node.last_modified)
         node = self.repos.get_node('/trunk/README.txt')
         self.assertEqual('README.txt', node.name)
         self.assertEqual('/trunk/README.txt', node.path)
-        self.assertEqual(Node.FILE, node.kind)
+        self.assertEqual(Node.FILE, node.node_type)
         self.assertEqual(3, node.rev)
         self.assertEqual(1112361898, node.last_modified)
 
@@ -131,13 +131,13 @@
         node = self.repos.get_node('/trunk', 1)
         self.assertEqual('trunk', node.name)
         self.assertEqual('/trunk', node.path)
-        self.assertEqual(Node.DIRECTORY, node.kind)
+        self.assertEqual(Node.DIRECTORY, node.node_type)
         self.assertEqual(1, node.rev)
         self.assertEqual(1112349652, node.last_modified)
         node = self.repos.get_node('/trunk/README.txt', 2)
         self.assertEqual('README.txt', node.name)
         self.assertEqual('/trunk/README.txt', node.path)
-        self.assertEqual(Node.FILE, node.kind)
+        self.assertEqual(Node.FILE, node.node_type)
         self.assertEqual(2, node.rev)
         self.assertEqual(1112361138, node.last_modified)
 
@@ -370,13 +370,13 @@
         node = self.repos.get_node('/dir1')
         self.assertEqual('dir1', node.name)
         self.assertEqual('/dir1', node.path)
-        self.assertEqual(Node.DIRECTORY, node.kind)
+        self.assertEqual(Node.DIRECTORY, node.node_type)
         self.assertEqual(5, node.rev)
         self.assertEqual(1112372739, node.last_modified)
         node = self.repos.get_node('/README.txt')
         self.assertEqual('README.txt', node.name)
         self.assertEqual('/README.txt', node.path)
-        self.assertEqual(Node.FILE, node.kind)
+        self.assertEqual(Node.FILE, node.node_type)
         self.assertEqual(3, node.rev)
         self.assertEqual(1112361898, node.last_modified)
 
@@ -384,13 +384,13 @@
         node = self.repos.get_node('/dir1', 4)
         self.assertEqual('dir1', node.name)
         self.assertEqual('/dir1', node.path)
-        self.assertEqual(Node.DIRECTORY, node.kind)
+        self.assertEqual(Node.DIRECTORY, node.node_type)
         self.assertEqual(4, node.rev)
         self.assertEqual(1112370155, node.last_modified)
         node = self.repos.get_node('/README.txt', 2)
         self.assertEqual('README.txt', node.name)
         self.assertEqual('/README.txt', node.path)
-        self.assertEqual(Node.FILE, node.kind)
+        self.assertEqual(Node.FILE, node.node_type)
         self.assertEqual(2, node.rev)
         self.assertEqual(1112361138, node.last_modified)
 
Index: trac/versioncontrol/tests/cache.py
===================================================================
--- trac/versioncontrol/tests/cache.py	(revision 2659)
+++ trac/versioncontrol/tests/cache.py	(working copy)
@@ -66,7 +66,7 @@
         self.assertEquals(('0', 41000, '', ''), cursor.fetchone())
         self.assertEquals(('1', 42000, 'joe', 'Import'), cursor.fetchone())
         self.assertEquals(None, cursor.fetchone())
-        cursor.execute("SELECT rev,path,kind,change,base_path,base_rev "
+        cursor.execute("SELECT rev,path,node_type,change_type,base_path,base_rev "
                        "FROM node_change")
         self.assertEquals(('1', 'trunk', 'D', 'A', None, None),
                           cursor.fetchone())
@@ -80,7 +80,7 @@
                        "VALUES (0,41000,'','')")
         cursor.execute("INSERT INTO revision (rev,time,author,message) "
                        "VALUES (1,42000,'joe','Import')")
-        cursor.executemany("INSERT INTO node_change (rev,path,kind,change,"
+        cursor.executemany("INSERT INTO node_change (rev,path,node_type,change_type,"
                            "base_path,base_rev) VALUES ('1',%s,%s,%s,%s,%s)",
                            [('trunk', 'D', 'A', None, None),
                             ('trunk/README', 'F', 'A', None, None)])
@@ -99,7 +99,7 @@
         cursor.execute("SELECT time,author,message FROM revision WHERE rev='2'")
         self.assertEquals((42042, 'joe', 'Update'), cursor.fetchone())
         self.assertEquals(None, cursor.fetchone())
-        cursor.execute("SELECT path,kind,change,base_path,base_rev "
+        cursor.execute("SELECT path,node_type,change_type,base_path,base_rev "
                        "FROM node_change WHERE rev='2'")
         self.assertEquals(('trunk/README', 'F', 'E', 'trunk/README', '1'),
                           cursor.fetchone())
@@ -111,7 +111,7 @@
                        "VALUES (0,41000,'','')")
         cursor.execute("INSERT INTO revision (rev,time,author,message) "
                        "VALUES (1,42000,'joe','Import')")
-        cursor.executemany("INSERT INTO node_change (rev,path,kind,change,"
+        cursor.executemany("INSERT INTO node_change (rev,path,node_type,change_type,"
                            "base_path,base_rev) VALUES ('1',%s,%s,%s,%s,%s)",
                            [('trunk', 'D', 'A', None, None),
                             ('trunk/README', 'F', 'A', None, None)])
Index: trac/versioncontrol/web_ui/changeset.py
===================================================================
--- trac/versioncontrol/web_ui/changeset.py	(revision 2659)
+++ trac/versioncontrol/web_ui/changeset.py	(working copy)
@@ -182,8 +182,8 @@
 
         edits = []
         idx = 0
-        for path, kind, change, base_path, base_rev in chgset.get_changes():
-            info = {'change': change}
+        for path, node_type, change_type, base_path, base_rev in chgset.get_changes():
+            info = {'change': change_type}
             if base_path:
                 info['path.old'] = base_path
                 info['rev.old'] = base_rev
@@ -194,8 +194,8 @@
                 info['rev.new'] = chgset.rev
                 info['browser_href.new'] = self.env.href.browser(path,
                                                                  rev=chgset.rev)
-            if change in (Changeset.COPY, Changeset.EDIT, Changeset.MOVE):
-                edits.append((idx, path, kind, base_path, base_rev))
+            if change_type in (Changeset.COPY, Changeset.EDIT, Changeset.MOVE):
+                edits.append((idx, path, node_type, base_path, base_rev))
             req.hdf['changeset.changes.%d' % idx] = info
             idx += 1
 
@@ -205,7 +205,7 @@
 
         mimeview = Mimeview(self.env)
             
-        for idx, path, kind, base_path, base_rev in edits:
+        for idx, path, node_type, base_path, base_rev in edits:
             old_node = repos.get_node(base_path or path, base_rev)
             new_node = repos.get_node(path, chgset.rev)
 
@@ -227,7 +227,7 @@
                         del changed_props[k]
                 req.hdf['changeset.changes.%d.props' % idx] = changed_props
 
-            if kind == Node.DIRECTORY:
+            if node_type == Node.DIRECTORY:
                 continue
 
             # Content changes
@@ -269,13 +269,12 @@
         req.end_headers()
 
         mimeview = Mimeview(self.env)
-
-        for path, kind, change, base_path, base_rev in chgset.get_changes():
-            if change == Changeset.ADD:
+        for path, node_type, change_type, base_path, base_rev in chgset.get_changes():
+            if change_type == Changeset.ADD:
                 old_node = None
             else:
                 old_node = repos.get_node(base_path or path, base_rev)
-            if change == Changeset.DELETE:
+            if change_type == Changeset.DELETE:
                 new_node = None
             else:
                 new_node = repos.get_node(path, chgset.rev)
@@ -283,7 +282,7 @@
             # TODO: Property changes
 
             # Content changes
-            if kind == 'dir':
+            if node_type == 'dir':
                 continue
 
             new_content = old_content = ''
@@ -338,8 +337,8 @@
 
         buf = StringIO()
         zipfile = ZipFile(buf, 'w', ZIP_DEFLATED)
-        for path, kind, change, base_path, base_rev in chgset.get_changes():
-            if kind == Node.FILE and change != Changeset.DELETE:
+        for path, node_type, change_type, base_path, base_rev in chgset.get_changes():
+            if node_type == Node.FILE and change_type != Changeset.DELETE:
                 node = repos.get_node(path, chgset.rev)
                 zipinfo = ZipInfo()
                 zipinfo.filename = node.path
@@ -383,7 +382,14 @@
             return
         authzperm = SubversionAuthorizer(self.env, req.authname)
         db = self.env.get_db_cnx()
-        sql, args = query_to_sql(db, query, 'message||author')
+        query_fields = ['message', 'author']
+        sql_seq = []
+        args = []
+        for f in query_fields:
+            subsql, subarg = query_to_sql(db, query, f)
+            sql_seq.append(subsql) 
+            args += subarg
+        sql = ' OR '.join(sql_seq)
         cursor = db.cursor()
         cursor.execute("SELECT rev,time,author,message "
                        "FROM revision WHERE " + sql, args)
Index: trac/versioncontrol/cache.py
===================================================================
--- trac/versioncontrol/cache.py	(revision 2659)
+++ trac/versioncontrol/cache.py	(working copy)
@@ -18,8 +18,8 @@
 from trac.versioncontrol import Changeset, Node, Repository, Authorizer
 
 
-_kindmap = {'D': Node.DIRECTORY, 'F': Node.FILE}
-_actionmap = {'A': Changeset.ADD, 'C': Changeset.COPY,
+_node_typemap = {'D': Node.DIRECTORY, 'F': Node.FILE}
+_change_typemap = {'A': Changeset.ADD, 'C': Changeset.COPY,
               'D': Changeset.DELETE, 'E': Changeset.EDIT,
               'M': Changeset.MOVE}
 
@@ -64,8 +64,8 @@
             authz = self.repos.authz
             self.repos.authz = Authorizer() # remove permission checking
 
-            kindmap = dict(zip(_kindmap.values(), _kindmap.keys()))
-            actionmap = dict(zip(_actionmap.values(), _actionmap.keys()))
+            node_typemap = dict(zip(_node_typemap.values(), _node_typemap.keys()))
+            change_typemap = dict(zip(_change_typemap.values(), _change_typemap.keys()))
             self.log.info("Syncing with repository (%s to %s)"
                           % (youngest_stored, self.repos.youngest_rev))
             if youngest_stored:
@@ -78,16 +78,16 @@
                                "VALUES (%s,%s,%s,%s)", (str(current_rev),
                                changeset.date, changeset.author,
                                changeset.message))
-                for path,kind,action,base_path,base_rev in changeset.get_changes():
+                for path,node_type,change_type,base_path,base_rev in changeset.get_changes():
                     self.log.debug("Caching node change in [%s]: %s"
-                                   % (current_rev, (path, kind, action,
+                                   % (current_rev, (path, node_type, change_type,
                                       base_path, base_rev)))
-                    kind = kindmap[kind]
-                    action = actionmap[action]
-                    cursor.execute("INSERT INTO node_change (rev,path,kind,"
-                                   "change,base_path,base_rev) "
+                    node_type = node_typemap[node_type]
+                    change_type = change_typemap[change_type]
+                    cursor.execute("INSERT INTO node_change (rev,path,node_type,"
+                                   "change_type,base_path,base_rev) "
                                    "VALUES (%s,%s,%s,%s,%s,%s)",
-                                   (str(current_rev), path, kind, action,
+                                   (str(current_rev), path, node_type, change_type,
                                    base_path, base_rev))
                 current_rev = self.repos.next_rev(current_rev)
             self.db.commit()
@@ -141,13 +141,13 @@
 
     def get_changes(self):
         cursor = self.db.cursor()
-        cursor.execute("SELECT path,kind,change,base_path,base_rev "
+        cursor.execute("SELECT path,node_type,change_type,base_path,base_rev "
                        "FROM node_change WHERE rev=%s "
                        "ORDER BY path", (self.rev,))
-        for path, kind, change, base_path, base_rev in cursor:
+        for path, node_type, change_type, base_path, base_rev in cursor:
             if not self.authz.has_permission(path):
                 # FIXME: what about the base_path?
                 continue
-            kind = _kindmap[kind]
-            change = _actionmap[change]
-            yield path, kind, change, base_path, base_rev
+            node_type = _node_typemap[node_type]
+            change_type = _change_typemap[change_type]
+            yield path, node_type, change_type, base_path, base_rev
Index: trac/upgrades/db17.py
===================================================================
--- trac/upgrades/db17.py	(revision 0)
+++ trac/upgrades/db17.py	(revision 0)
@@ -0,0 +1,22 @@
+from trac.db import Table, Column, Index, DatabaseManager
+
+def do_upgrade(env, ver, cursor):
+    cursor.execute("CREATE TEMP TABLE node_change_old AS SELECT * FROM node_change")
+    cursor.execute("DROP TABLE node_change")
+
+    db = env.get_db_cnx()
+    node_change_table = Table('node_change', key=('rev', 'path', 'change_type'))[
+        Column('rev'),
+        Column('path'),
+        Column('node_type', size=1),
+        Column('change_type', size=1),
+        Column('base_path'),
+        Column('base_rev'),
+        Index(['rev'])]
+    db_backend, _ = DatabaseManager(env)._get_connector()
+    for stmt in db_backend.to_sql(node_change_table):
+        cursor.execute(stmt)
+
+    cursor.execute("INSERT INTO node_change (rev,path,node_type,change_type,base_path,base_rev) "
+                   "SELECT rev,path,kind,change,base_path,base_rev "
+                   "FROM node_change_old")
Index: trac/web/session.py
===================================================================
--- trac/web/session.py	(revision 2659)
+++ trac/web/session.py	(working copy)
@@ -181,9 +181,19 @@
             # changed as to minimize the purging.
             mintime = now - PURGE_AGE
             self.env.log.debug('Purging old, expired, sessions.')
-            cursor.execute("DELETE FROM session WHERE authenticated=0 AND "
-                           "sid IN (SELECT sid FROM session WHERE "
-                           "var_name='last_visit' AND var_value < %s)",
+            # This is a temporary hack because MySQL doesn't support
+            # a delete statement with the subquery coming from the same
+            # table
+            cursor.execute("SELECT sid from session WHERE "
+                           "var_name='last_visit' AND var_value < %s",
                            (mintime,))
-
+            sids = cursor.fetchall() 
+            if sids and len(sids) >= 1:
+                vals = []
+                sids_args = []
+                for t in sids:
+                    vals.append(t[0])
+                    sids_args.append("sid = %s")
+                sql = "DELETE FROM session WHERE authenticated = 0 AND (%s)" % ' OR '.join(sids_args)
+                cursor.execute(sql, vals)
             db.commit()
Index: templates/changeset.cs
===================================================================
--- templates/changeset.cs	(revision 2659)
+++ templates/changeset.cs	(working copy)
@@ -70,7 +70,7 @@
  </div>
 </form><?cs /if ?>
 
-<?cs def:node_change(item,cl,kind) ?><?cs 
+<?cs def:node_change(item,cl,node_type) ?><?cs 
   set:ndiffs = len(item.diff) ?><?cs
   set:nprops = len(item.props) ?>
   <div class="<?cs var:cl ?>"></div><?cs 
@@ -81,9 +81,9 @@
    <a title="Show entry in browser" href="<?cs
      var:item.browser_href.new ?>"><?cs var:item.path.new ?></a><?cs
   /if ?>
-  <span class="comment">(<?cs var:kind ?>)</span><?cs
+  <span class="comment">(<?cs var:node_type ?>)</span><?cs
   if:item.path.old && item.change == 'copy' || item.change == 'move' ?>
-   <small><em>(<?cs var:kind ?> from <a href="<?cs
+   <small><em>(<?cs var:node_type ?> from <a href="<?cs
     var:item.browser_href.old ?>" title="Show original file (rev. <?cs
     var:item.rev.old ?>)"><?cs var:item.path.old ?></a>)</em></small><?cs
   /if ?><?cs
