------------------------------------------------------------
revno: 40
committer: Amgad Molseh <amgad.mosleh@rezayat.net>
branch nick: trac-mysql
timestamp: Tue 2006-04-04 14:30:36 -0500
message:
  I fixed the select statement for retrieving the tickets by enforcing the use 
  of utf8 as the character set.
------------------------------------------------------------
revno: 39
committer: Andres Salomon <dilinger@lith>
branch nick: trac-mysql
timestamp: Tue 2006-03-21 19:28:36 -0500
message:
  r37 was terribly broken; it stripped out quotes when it shouldn't have.
  This version only strips out quotes there's a variable in them.
------------------------------------------------------------
revno: 38
committer: Andres Salomon <dilinger@jack>
branch nick: trac-mysql
timestamp: Tue 2006-03-21 16:28:34 -0500
message:
  Merge in upstream.
    ------------------------------------------------------------
    merged: dilinger@jack-20060321194024-7ec90d6fc8019b81
    committer: Andres Salomon <dilinger@jack>
    branch nick: trac.bzr
    timestamp: Tue 2006-03-21 14:40:24 -0500
    message:
       Updated to revision 3029.
------------------------------------------------------------
revno: 37
committer: Andres Salomon <dilinger@lith>
branch nick: trac-mysql
timestamp: Mon 2006-03-20 15:49:51 -0500
message:
  replace a silly unnecessary concat that causes trouble w/ mysql4.1
------------------------------------------------------------
revno: 36
committer: Andres Salomon <dilinger@lith>
branch nick: trac-mysql
timestamp: Mon 2006-03-20 13:08:57 -0500
message:
  Make the mysql client connection return results in unicode.  Note that the
  python-mysqldb docs mention two ways of doing this:
  
  use_unicode
  
      If True, CHAR and VARCHAR and TEXT columns are returned as Unicode strings, using the configured character set. It is best to set the default encoding in the server configuration, or client configuration (read with read_default_file). If you change the character set after connecting (MySQL-4.1 and later), you'll need to put the correct character set name in connection.charset.
  
      If False, text-like columns are returned as normal strings, but you can always write Unicode strings.
  
  charset
  
      If present, the connection character set will be changed to this character set, if they are not equal. Support for changing the character set requires MySQL-4.1 and later server; if the server is too old, UnsupportedError will be raised. This option implies use_unicode=True, but you can override this with use_unicode=False, though you probably shouldn't.
  
      If not present, the default character set is used.
  
  
  We stick w/ use_unicode for now, since it's supported by the version of
  python-mysqldb that's in debian sarge.
------------------------------------------------------------
revno: 35
committer: Andres Salomon <dilinger@lith>
branch nick: trac-mysql
timestamp: Mon 2006-03-20 11:50:55 -0500
message:
  revert r29, as that was a mysql4.0 fix, and we're not going to bother
  supporting 4.0.
------------------------------------------------------------
revno: 34
committer: Andres Salomon <dilinger@lith>
branch nick: trac-mysql
timestamp: Mon 2006-03-20 11:18:06 -0500
message:
  Add support for not specifying the password in the mysql url.  This should
  not be used in production, but it's handy for testing.
------------------------------------------------------------
revno: 33
committer: Andres Salomon <dilinger@jack>
branch nick: trac-mysql
timestamp: Mon 2006-03-20 10:51:54 -0500
message:
  Merge in upstream.
    ------------------------------------------------------------
    merged: dilinger@jack-20060320155046-c8f36fca93bd45ce
    committer: Andres Salomon <dilinger@jack>
    branch nick: trac.bzr
    timestamp: Mon 2006-03-20 10:50:46 -0500
    message:
      Update to revision r3025.
------------------------------------------------------------
revno: 32
committer: Andres Salomon <dilinger@jack>
branch nick: trac-mysql
timestamp: Fri 2006-03-10 11:42:59 -0500
message:
  arg, fix a buglet
------------------------------------------------------------
revno: 31
committer: Andres Salomon <dilinger@jack>
branch nick: trac-mysql
timestamp: Fri 2006-03-10 11:21:23 -0500
message:
  mysql4.0 doesn't support the syntax "TEMP TABLE";
  use "TEMPORARY TABLE" instead.
------------------------------------------------------------
revno: 30
committer: Andres Salomon <dilinger@jack>
branch nick: trac-mysql
timestamp: Fri 2006-03-10 11:19:21 -0500
message:
  sqlite doesn't support 'DROP TEMP TABLE'.
------------------------------------------------------------
revno: 29
committer: Andres Salomon <dilinger@jack>
branch nick: trac-mysql
timestamp: Fri 2006-03-10 11:15:51 -0500
message:
  mysql 4.0 fix; use a temporary table instead of a subselect.
------------------------------------------------------------
revno: 28
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Wed 2006-03-08 16:39:12 -0500
message:
  Fix a sql query that works in sqlite but not in mysql.   Apparently, mysql
  cannot order by a function call; but it can order by results.  So, order by
  max(time) becomes max_time.
------------------------------------------------------------
revno: 27
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Wed 2006-03-08 16:19:28 -0500
message:
  Merge from upstream.
    ------------------------------------------------------------
    merged: dilinger@sticky-20060308210914-ae5799a553d22fdf
    committer: Andres Salomon <dilinger@sticky>
    branch nick: trac.bzr
    timestamp: Wed 2006-03-08 16:09:14 -0500
    message:
        Updated to revision 2975.
------------------------------------------------------------
revno: 26
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Thu 2006-03-02 13:58:31 -0500
message:
  bug fix; don't attempt to delete sessions if there are none that require
  deleting.
------------------------------------------------------------
revno: 25
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Wed 2006-03-01 16:10:06 -0500
message:
  Merge from upstream.
    ------------------------------------------------------------
    merged: dilinger@sticky-20060301183359-32fc32f3af06db89
    committer: Andres Salomon <dilinger@sticky>
    branch nick: trac.bzr
    timestamp: Wed 2006-03-01 13:33:59 -0500
    message:
      Updated to revision 2946.
------------------------------------------------------------
revno: 24
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Wed 2006-03-01 15:57:02 -0500
message:
  Update for mysql 4.0, which has different limits from 5.0.
------------------------------------------------------------
revno: 23
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Wed 2006-03-01 02:43:02 -0500
message:
  merge in wiki fix
    ------------------------------------------------------------
    merged: dilinger@sticky-20060301073759-1564545412d6e1b5
    committer: Andres Salomon <dilinger@sticky>
    branch nick: trac-wikicap
    timestamp: Wed 2006-03-01 02:37:59 -0500
    message:
      Fix a minor bug with a sql statement; the table name is 'wiki', not 'WIKI'.
------------------------------------------------------------
revno: 22
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Wed 2006-03-01 02:28:27 -0500
message:
  Fix an off-by-one error, and force the column type to 'int' if it's an
  auto_increment column.
------------------------------------------------------------
revno: 21
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Wed 2006-03-01 02:14:57 -0500
message:
  fix missing space between column name and attributes
------------------------------------------------------------
revno: 20
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Wed 2006-03-01 02:14:24 -0500
message:
  make the _collist() stuff more generic, and make it work for primary keys
  as well (since they use indexes internally)
------------------------------------------------------------
revno: 19
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Wed 2006-03-01 01:50:13 -0500
message:
  fix a minor typo; ',' instead of '.'.
------------------------------------------------------------
revno: 18
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Wed 2006-03-01 01:45:51 -0500
message:
  clean up the indices handling and other table-creation related stuff.
------------------------------------------------------------
revno: 17
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Tue 2006-02-28 16:49:23 -0500
message:
  fix a buglet in the sql rename
------------------------------------------------------------
revno: 16
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Tue 2006-02-28 16:44:58 -0500
message:
  Merge in 'sql' column rename.
    ------------------------------------------------------------
    merged: dilinger@sticky-20060228213408-099adca595b49c8f
    committer: Andres Salomon <dilinger@sticky>
    branch nick: trac-sqlcolfix
    timestamp: Tue 2006-02-28 16:34:08 -0500
    message:
      whoopsie, buglet
    ------------------------------------------------------------
    merged: dilinger@sticky-20060228210621-a9e71c84b349d7f5
    committer: Andres Salomon <dilinger@sticky>
    branch nick: trac-sqlcolfix
    timestamp: Tue 2006-02-28 16:06:21 -0500
    message:
      include the `sql` column rename in the db17 upgrade.
    ------------------------------------------------------------
    merged: dilinger@sticky-20060228205749-5743f07063ec2395
    committer: Andres Salomon <dilinger@sticky>
    branch nick: trac-sqlcolfix
    timestamp: Tue 2006-02-28 15:57:49 -0500
    message:
      Rename 'sql' column in report table to 'query', so it doesn't conflict w/
      mysql keyword.
------------------------------------------------------------
revno: 15
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Tue 2006-02-28 14:20:39 -0500
message:
  fix an issue w/ the port type; it requires an int, and was getting None.
------------------------------------------------------------
revno: 14
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Tue 2006-02-28 02:40:33 -0500
message:
  fix a string quoting issue in session stuff.
------------------------------------------------------------
revno: 13
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Tue 2006-02-28 02:32:39 -0500
message:
  fix quoting on sql query; oops!
------------------------------------------------------------
revno: 12
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Tue 2006-02-28 01:59:23 -0500
message:
  Merge from svn trunk.
    ------------------------------------------------------------
    merged: dilinger@sticky-20060227174735-49d5ae65e3431d91
    committer: Andres Salomon <dilinger@sticky>
    branch nick: trac.bzr
    timestamp: Mon 2006-02-27 12:47:35 -0500
    message:
      Updated to revision 2944.
    ------------------------------------------------------------
    merged: dilinger@sticky-20060227071606-94a113e7b55325fe
    committer: Andres Salomon <dilinger@sticky>
    branch nick: trac.bzr
    timestamp: Mon 2006-02-27 02:16:06 -0500
    message:
        Updated to revision 2943.
      
      This includes merges from my trac-search branch.
------------------------------------------------------------
revno: 11
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Mon 2006-02-27 03:43:37 -0500
message:
  oops, missed a spot for default args
------------------------------------------------------------
revno: 10
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Mon 2006-02-27 03:41:55 -0500
message:
  Clean up the connection function; expand names, specify a default host and
  port, etc.
------------------------------------------------------------
revno: 9
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Mon 2006-02-27 03:29:42 -0500
message:
  Add mysql to copyright file
------------------------------------------------------------
revno: 8
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Mon 2006-02-27 03:28:58 -0500
message:
  Only import MySQL python module if actually loading/initializing the trac
  mysql backend.
------------------------------------------------------------
revno: 7
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Mon 2006-02-27 03:27:26 -0500
message:
  Original implementation of get_last_id() was racy; if two db connections
  insert at the same time, one of the two will get the incorrect last id.
  This fixes it so that it's the last inserted id in the current connection,
  not globally.
------------------------------------------------------------
revno: 6
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Mon 2006-02-27 03:21:21 -0500
message:
  Begin w/ Jeff Weiss's mysql backend implementation.
------------------------------------------------------------
revno: 5
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-mysql
timestamp: Fri 2006-02-17 01:57:23 -0500
message:
  Break apart a SQL query that includes a subselect that mysql doesn't support.
------------------------------------------------------------
revno: 4
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-db-changes
timestamp: Fri 2006-02-10 14:14:14 -0500
message:
  svn update;
  Updated to revision 2894.
  
  Prior patch (revno 3) was merged into svn, but db17.py changes were not
  accepted.
------------------------------------------------------------
revno: 3
committer: Andres Salomon <dilinger@sticky>
branch nick: trac-db-changes
timestamp: Thu 2006-02-09 19:08:20 -0500
message:
  Rename a few node_change column names, as documented in #986.  The problem
  is that 'changes' is a mysql keyword.
------------------------------------------------------------
revno: 2
committer: Andres Salomon <dilinger@sticky>
branch nick: trac
timestamp: Thu 2006-02-09 16:18:36 -0500
message:
  svn update:
  Updated to revision 2881.
------------------------------------------------------------
revno: 1
committer: Andres Salomon <dilinger@sticky>
branch nick: trac
timestamp: Wed 2006-02-08 17:26:52 -0500
message:
  SVN snapshot of trac;
  
  "Updated to revision 2880."
=== added file 'trac/db/mysql_backend.py'
--- /dev/null	
+++ trac/db/mysql_backend.py	
@@ -0,0 +1,123 @@
+# -*- coding: iso8859-1 -*-
+#
+# Copyright (C) 2005 Edgewall Software
+# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
+# Copyright (C) 2005 Jeff Weiss <trac@jeffweiss.org>
+# Copyright (C) 2006 Andres Salomon <dilinger@athenacr.com>
+# 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
+
+class MySQLConnector(Component):
+    """MySQL database support for version 4.1 and greater.
+Database urls should be of the form:
+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 _collist(self, table, columns):
+        """
+        Take a list of columns and impose limits on each so that indexing
+        works properly.  Some Versions of MySQL limit each index prefix to
+        500 bytes total, with a max of 255 bytes per column.
+        """
+        cols = []
+        limit = 500 / len(columns)
+        if limit > 255:
+            limit = 255
+        for c in columns:
+            name = '`%s`' % c
+            table_col = filter((lambda x: x.name == c), table.columns)
+            if len(table_col) == 1 and table_col[0].type.lower() == 'text':
+                name += '(%s)' % limit
+            # For non-text columns, we simply throw away the extra bytes.
+            # That could certainly be optimized better, but for now let's KISS.
+            cols.append(name)
+        return ','.join(cols)
+
+    def to_sql(self, table):
+        sql = ['CREATE TABLE %s (' % table.name]
+        coldefs = []
+        for column in table.columns:
+            ctype = column.type
+            if column.auto_increment:
+                ctype = 'INT UNSIGNED NOT NULL AUTO_INCREMENT'
+                # Override the column type, as a text field cannot
+                # use auto_increment.
+                column.type = 'int'
+            coldefs.append('    `%s` %s' % (column.name, ctype))
+        if len(table.key) > 0:
+            coldefs.append('    PRIMARY KEY (%s)' %
+                           self._collist(table, table.key))
+        sql.append(',\n'.join(coldefs) + '\n)')
+        yield '\n'.join(sql)
+        for index in table.indices:
+            yield 'CREATE INDEX %s_%s_idx ON %s (%s);' % (table.name,
+                  '_'.join(index.columns), table.name,
+                  self._collist(table, index.columns))
+
+class MySQLConnection(ConnectionWrapper):
+    """Connection wrapper for MySQL."""
+
+    poolable = True
+
+    def __init__(self, path, user=None, password=None, host=None,
+                 port=None, params={}):
+        import MySQLdb
+
+        if path.startswith('/'):
+            path = path[1:]
+        if password == None:
+            password = ''
+        if port == None:
+            port = 3306
+        # XXX: or do we want "charset='UNICODE'"?  it's unsupported in debian
+        # 3.1's python-mysqldb
+        cnx = MySQLdb.connect(db=path, user=user, passwd=password, host=host,
+                              port=port, use_unicode=True)
+        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'):
+        return self.cnx.insert_id()

=== modified file 'trac/db/tests/api.py'
--- trac/db/tests/api.py	
+++ trac/db/tests/api.py	
@@ -40,6 +40,15 @@
                                        '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')

=== modified file 'trac/db_default.py'
--- trac/db_default.py	
+++ trac/db_default.py	
@@ -148,7 +148,7 @@
         Column('id', auto_increment=True),
         Column('author'),
         Column('title'),
-        Column('sql'),
+        Column('query'),
         Column('description')],
 ]
 
@@ -379,7 +379,7 @@
              ('name', 'value'),
                (('database_version', str(db_version)),)),
            ('report',
-             ('author', 'title', 'sql', 'description'),
+             ('author', 'title', 'query', 'description'),
                __mkreports(reports)))
 
 default_config = \
@@ -445,6 +445,7 @@
 
 default_components = ('trac.About', 'trac.attachment',
                       'trac.db.postgres_backend', 'trac.db.sqlite_backend',
+                      'trac.db.mysql_backend',
                       'trac.mimeview.enscript', 'trac.mimeview.patch',
                       'trac.mimeview.php', 'trac.mimeview.rst',
                       'trac.mimeview.silvercity', 'trac.mimeview.txtl',

=== modified file 'trac/ticket/report.py'
--- trac/ticket/report.py	
+++ trac/ticket/report.py	
@@ -122,11 +122,11 @@
             req.redirect(self.env.href.report())
 
         title = req.args.get('title', '')
-        sql = req.args.get('sql', '')
+        query = req.args.get('query', '')
         description = req.args.get('description', '')
         cursor = db.cursor()
-        cursor.execute("INSERT INTO report (title,sql,description) "
-                       "VALUES (%s,%s,%s)", (title, sql, description))
+        cursor.execute("INSERT INTO report (title,query,description) "
+                       "VALUES (%s,%s,%s)", (title, query, description))
         id = db.get_last_id(cursor, 'report')
         db.commit()
         req.redirect(self.env.href.report(id))
@@ -150,11 +150,11 @@
 
         if not req.args.has_key('cancel'):
             title = req.args.get('title', '')
-            sql = req.args.get('sql', '')
+            query = req.args.get('query', '')
             description = req.args.get('description', '')
             cursor = db.cursor()
-            cursor.execute("UPDATE report SET title=%s,sql=%s,description=%s "
-                           "WHERE id=%s", (title, sql, description, id))
+            cursor.execute("UPDATE report SET title=%s,query=%s,description=%s "
+                           "WHERE id=%s", (title, query, description, id))
             db.commit()
         req.redirect(self.env.href.report(id))
 
@@ -178,11 +178,11 @@
     def _render_editor(self, req, db, id, copy=False):
         if id == -1:
             req.perm.assert_permission('REPORT_CREATE')
-            title = sql = description = ''
+            title = query = description = ''
         else:
             req.perm.assert_permission('REPORT_MODIFY')
             cursor = db.cursor()
-            cursor.execute("SELECT title,description,sql FROM report "
+            cursor.execute("SELECT title,description,query FROM report "
                            "WHERE id=%s", (id,))
             row = cursor.fetchone()
             if not row:
@@ -190,7 +190,7 @@
                                      'Invalid Report Number')
             title = row[0] or ''
             description = row[1] or ''
-            sql = row[2] or ''
+            query = row[2] or ''
 
         if copy:
             title += ' (copy)'
@@ -207,7 +207,7 @@
         req.hdf['report.id'] = id
         req.hdf['report.mode'] = 'edit'
         req.hdf['report.title'] = title
-        req.hdf['report.sql'] = sql
+        req.hdf['report.sql'] = query
         req.hdf['report.description'] = description
 
     def _render_view(self, req, db, id):
@@ -404,7 +404,7 @@
             description = 'This is a list of reports available.'
         else:
             cursor = db.cursor()
-            cursor.execute("SELECT title,sql,description from report "
+            cursor.execute("SELECT title,query,description from report "
                            "WHERE id=%s", (id,))
             row = cursor.fetchone()
             if not row:
@@ -439,26 +439,18 @@
             req.hdf['report.var.' + aname] = arg
             values.append(arg)
 
-        # simple parameter substitution outside literal
+        # simple parameter substitution
         def repl(match):
             add_value(match.group(1))
             return '%s'
 
-        # inside a literal break it and concatenate with the parameter
-        def repl_literal(match):
-            add_value(match.group(1))
-            return "' || %s || '"
-
-        var_re = re.compile("[$]([A-Z]+)")
+        var_re = re.compile("'?[$]([A-Z]+)'?")
         sql_io = StringIO()
 
         # break SQL into literals and non-literals to handle replacing
         # variables within them with query parameters
         for expr in re.split("('(?:[^']|(?:''))*')", sql):
-            if expr.startswith("'"):
-                sql_io.write(var_re.sub(repl_literal, expr))
-            else:
-                sql_io.write(var_re.sub(repl, expr))
+            sql_io.write(var_re.sub(repl, expr))
         return sql_io.getvalue(), values
 
     def _render_csv(self, req, cols, rows, sep=','):

=== modified file 'trac/upgrades/db17.py'
--- trac/upgrades/db17.py	
+++ trac/upgrades/db17.py	
@@ -2,9 +2,12 @@
 
 def do_upgrade(env, ver, cursor):
     """Rename the columns `kind` and `change` in the `node_change` table for
-    compatibity with MySQL.
+    compatibity with MySQL, as well as the `sql` column in the `reports` table.
     """
-    cursor.execute("CREATE TEMP TABLE nc_old AS SELECT * FROM node_change")
+    db_connector, _ = DatabaseManager(env)._get_connector()
+
+    # alter node_change table
+    cursor.execute("CREATE TEMPORARY TABLE nc_old AS SELECT * FROM node_change")
     cursor.execute("DROP TABLE node_change")
 
     table = Table('node_change', key=('rev', 'path', 'change_type'))[
@@ -16,10 +19,26 @@
         Column('base_rev'),
         Index(['rev'])
     ]
-    db_connector, _ = DatabaseManager(env)._get_connector()
     for stmt in db_connector.to_sql(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 nc_old")
+
+    # alter report table
+    cursor.execute("CREATE TEMPORARY TABLE report_old AS SELECT * FROM report")
+    cursor.execute("DROP TABLE report")
+
+    table = Table('report', key='id')[
+        Column('id', auto_increment=True),
+        Column('author'),
+        Column('title'),
+        Column('query'),
+        Column('description')
+    ]
+    for stmt in db_connector.to_sql(table):
+        cursor.execute(stmt)
+
+    cursor.execute("INSERT INTO report (id,author,title,query,description) "
+                   "SELECT id,author,title,sql,description FROM report_old")

=== modified file 'trac/web/session.py'
--- trac/web/session.py	
+++ trac/web/session.py	
@@ -182,9 +182,12 @@
             # 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)",
+            cursor.execute("SELECT DISTINCT sid FROM session WHERE "
+                           "var_name='last_visit' AND var_value < %s",
                            (mintime,))
-
+            args = cursor.fetchall()
+            if args:
+                sql = 'DELETE FROM session WHERE authenticated = 0 AND ('
+                sql += ' OR '.join(['sid = %s'] * len(args)) + ')'
+                cursor.execute(sql, args)
             db.commit()

=== modified file 'trac/wiki/macros.py'
--- trac/wiki/macros.py	
+++ trac/wiki/macros.py	
@@ -98,12 +98,12 @@
         db = self.env.get_db_cnx()
         cursor = db.cursor()
 
-        sql = 'SELECT name, max(time) FROM wiki'
+        sql = 'SELECT name, max(time) AS max_time FROM wiki'
         args = []
         if prefix:
             sql += ' WHERE name LIKE %s'
             args.append(prefix + '%')
-        sql += ' GROUP BY name ORDER BY max(time) DESC'
+        sql += ' GROUP BY name ORDER BY max_time DESC'
         if limit:
             sql += ' LIMIT %s'
             args.append(limit)

=== modified file 'trac/ticket/model.py'
--- trac/ticket/model.py	
+++ trac/ticket/model.py	
@@ -251,8 +251,8 @@
        if when:
            cursor.execute("SELECT time,author,field,oldvalue,newvalue "
                           "FROM ticket_change WHERE ticket=%s AND time=%s "
                           "UNION "
-                          "SELECT time,author,'attachment',null,filename "
+                         "SELECT _utf8 'time' COLLATE utf8_general_ci,_utf8 'author' COLLATE utf8_general_ci,_utf8 'attachment' COLLATE utf8_general_ci,_utf8 'null' COLLATE utf8_general_ci,_utf8 'filename' COLLATE utf8_general_ci "
                           "FROM attachment WHERE id=%s AND time=%s "
                           "UNION "
-                          "SELECT time,author,'comment',null,description "
+                         "SELECT _utf8 'time' COLLATE utf8_general_ci,_utf8 'author' COLLATE utf8_general_ci,_utf8 'comment' COLLATE utf8_general_ci,_utf8 'null' COLLATE utf8_general_ci,_utf8 'description' COLLATE utf8_general_ci "
                           "FROM attachment WHERE id=%s AND time=%s "
                           "ORDER BY time",
                           (self.id, when, str(self.id), when, self.id, when))
        else:
            cursor.execute("SELECT time,author,field,oldvalue,newvalue "
                           "FROM ticket_change WHERE ticket=%s "
                           "UNION "
-                          "SELECT time,author,'attachment',null,filename "
+                         "SELECT _utf8 'time' COLLATE utf8_general_ci,_utf8 'author' COLLATE utf8_general_ci,_utf8 'attachment' COLLATE utf8_general_ci,_utf8 'null' COLLATE utf8_general_ci,_utf8 'filename' COLLATE utf8_general_ci "
                           "FROM attachment WHERE id=%s "
                           "UNION "
-                          "SELECT time,author,'comment',null,description "
+                         "SELECT _utf8 'time' COLLATE utf8_general_ci,_utf8 'author' COLLATE utf8_general_ci,_utf8 'comment' COLLATE utf8_general_ci,_utf8 'null' COLLATE utf8_general_ci,_utf8 'description' COLLATE utf8_general_ci "
                           "FROM attachment WHERE id=%s "
                           "ORDER BY time", (self.id,  str(self.id), self.id))
