Edgewall Software

Ticket #986: trac-mysql-r2975.patch

File trac-mysql-r2975.patch, 15.7 kB (added by anonymous, 2 years ago)

updated mysql patch, w/ additional mysql4.0 fixes; against r2975.

  • (a) /dev/null vs. (b) trac/db/mysql_backend.py

    ------------------------------------------------------------
    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.
    === added file 'trac/db/mysql_backend.py'
    a b  
     1# -*- coding: iso8859-1 -*- 
     2# 
     3# Copyright (C) 2005 Edgewall Software 
     4# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de> 
     5# Copyright (C) 2005 Jeff Weiss <trac@jeffweiss.org> 
     6# Copyright (C) 2006 Andres Salomon <dilinger@athenacr.com> 
     7# All rights reserved. 
     8# 
     9# This software is licensed as described in the file COPYING, which 
     10# you should have received as part of this distribution. The terms 
     11# are also available at http://trac.edgewall.com/license.html. 
     12# 
     13# This software consists of voluntary contributions made by many 
     14# individuals. For the exact contribution history, see the revision 
     15# history and logs, available at http://projects.edgewall.com/trac/. 
     16# 
     17# Derived from postgres_backend.py 
     18# Author: Christopher Lenz <cmlenz@gmx.de>  
     19# Author: Jeff Weiss <trac@jeffweiss.org> 
     20 
     21from trac.core import * 
     22from trac.db.api import IDatabaseConnector 
     23from trac.db.util import ConnectionWrapper 
     24 
     25class MySQLConnector(Component): 
     26    """MySQL database support.  Still extremely experimental! 
     27Currently only supports database urls of the following formats: 
     28mysql://user:password@host/database 
     29mysql://user:password@host:port/database 
     30""" 
     31 
     32    implements(IDatabaseConnector) 
     33 
     34    def get_supported_schemes(self): 
     35        return [('mysql', 1)] 
     36 
     37    def get_connection(self, path, user=None, password=None, host=None, 
     38                       port=None, params={}): 
     39        return MySQLConnection(path, user, password, host, port, params) 
     40 
     41    def init_db(self, path, user=None, password=None, host=None, port=None, 
     42                params={}): 
     43        cnx = self.get_connection(path, user, password, host, port, params) 
     44        cursor = cnx.cursor() 
     45        from trac.db_default import schema 
     46        for table in schema: 
     47            for stmt in self.to_sql(table): 
     48                self.env.log.debug(stmt) 
     49                cursor.execute(stmt) 
     50        cnx.commit() 
     51 
     52    def _collist(self, table, columns): 
     53        """ 
     54        Take a list of columns and impose limits on each so that indexing 
     55        works properly.  Some Versions of MySQL limit each index prefix to 
     56        500 bytes total, with a max of 255 bytes per column. 
     57        """ 
     58        cols = [] 
     59        limit = 500 / len(columns) 
     60        if limit > 255: 
     61            limit = 255 
     62        for c in columns: 
     63            name = '`%s`' % c 
     64            table_col = filter((lambda x: x.name == c), table.columns) 
     65            if len(table_col) == 1 and table_col[0].type.lower() == 'text': 
     66                name += '(%s)' % limit 
     67            # For non-text columns, we simply throw away the extra bytes. 
     68            # That could certainly be optimized better, but for now let's KISS. 
     69            cols.append(name) 
     70        return ','.join(cols) 
     71 
     72    def to_sql(self, table): 
     73        sql = ['CREATE TABLE %s (' % table.name] 
     74        coldefs = [] 
     75        for column in table.columns: 
     76            ctype = column.type 
     77            if column.auto_increment: 
     78                ctype = 'INT UNSIGNED NOT NULL AUTO_INCREMENT' 
     79                # Override the column type, as a text field cannot 
     80                # use auto_increment. 
     81                column.type = 'int' 
     82            coldefs.append('    `%s` %s' % (column.name, ctype)) 
     83        if len(table.key) > 0: 
     84            coldefs.append('    PRIMARY KEY (%s)' % 
     85                           self._collist(table, table.key)) 
     86        sql.append(',\n'.join(coldefs) + '\n)') 
     87        yield '\n'.join(sql) 
     88        for index in table.indices: 
     89            yield 'CREATE INDEX %s_%s_idx ON %s (%s);' % (table.name, 
     90                  '_'.join(index.columns), table.name, 
     91                  self._collist(table, index.columns)) 
     92 
     93class MySQLConnection(ConnectionWrapper): 
     94    """Connection wrapper for MySQL.""" 
     95 
     96    poolable = True 
     97 
     98    def __init__(self, path, user=None, password=None, host=None, 
     99                 port=None, params={}): 
     100        import MySQLdb 
     101 
     102        if path.startswith('/'): 
     103            path = path[1:] 
     104        if port == None: 
     105            port = 3306 
     106        cnx = MySQLdb.connect(db=path, user=user, passwd=password, host=host, port=port) 
     107        ConnectionWrapper.__init__(self, cnx) 
     108 
     109    def cast(self, column, type): 
     110        # Temporary hack needed for the union of selects in the search module 
     111        return 'CAST(%s AS %s)' % (column, type) 
     112 
     113    def like(self): 
     114        # Temporary hack needed for the case-insensitive string matching in the 
     115        # search module 
     116        return 'LIKE' 
     117 
     118    def get_last_id(self, cursor, table, column='id'): 
     119        return self.cnx.insert_id() 
  • trac/db/tests/api.py

    === modified file 'trac/db/tests/api.py'
     
    4040                                       'path': '/trac'}), 
    4141                         _parse_db_str('postgres://john:letmein@localhost:9431/trac')) 
    4242 
     43    def test_mysql_simple(self): 
     44        self.assertEqual(('mysql', {'host': 'localhost', 'path': '/trac'}), 
     45                         _parse_db_str('mysql://localhost/trac')) 
     46 
     47    def test_mysql_with_creds(self): 
     48        self.assertEqual(('mysql', {'user': 'john', 'password': 'letmein', 
     49                                    'host': 'localhost', 'port': 3306, 
     50                                    'path': '/trac'}), 
     51                         _parse_db_str('mysql://john:letmein@localhost:3306/trac')) 
    4352 
    4453def suite(): 
    4554    return unittest.makeSuite(ParseConnectionStringTestCase,'test') 
  • trac/db_default.py

    === modified file 'trac/db_default.py'
     
    148148        Column('id', auto_increment=True), 
    149149        Column('author'), 
    150150        Column('title'), 
    151         Column('sql'), 
     151        Column('query'), 
    152152        Column('description')], 
    153153] 
    154154 
     
    379379             ('name', 'value'), 
    380380               (('database_version', str(db_version)),)), 
    381381           ('report', 
    382              ('author', 'title', 'sql', 'description'), 
     382             ('author', 'title', 'query', 'description'), 
    383383               __mkreports(reports))) 
    384384 
    385385default_config = \ 
     
    445445 
    446446default_components = ('trac.About', 'trac.attachment', 
    447447                      'trac.db.postgres_backend', 'trac.db.sqlite_backend', 
     448                      'trac.db.mysql_backend', 
    448449                      'trac.mimeview.enscript', 'trac.mimeview.patch', 
    449450                      'trac.mimeview.php', 'trac.mimeview.rst', 
    450451                      'trac.mimeview.silvercity', 'trac.mimeview.txtl', 
  • trac/ticket/report.py

    === modified file 'trac/ticket/report.py'
     
    122122            req.redirect(self.env.href.report()) 
    123123 
    124124        title = req.args.get('title', '') 
    125         sql = req.args.get('sql', '') 
     125        query = req.args.get('query', '') 
    126126        description = req.args.get('description', '') 
    127127        cursor = db.cursor() 
    128         cursor.execute("INSERT INTO report (title,sql,description) " 
    129                        "VALUES (%s,%s,%s)", (title, sql, description)) 
     128        cursor.execute("INSERT INTO report (title,query,description) " 
     129                       "VALUES (%s,%s,%s)", (title, query, description)) 
    130130        id = db.get_last_id(cursor, 'report') 
    131131        db.commit() 
    132132        req.redirect(self.env.href.report(id)) 
     
    150150 
    151151        if not req.args.has_key('cancel'): 
    152152            title = req.args.get('title', '') 
    153             sql = req.args.get('sql', '') 
     153            query = req.args.get('query', '') 
    154154            description = req.args.get('description', '') 
    155155            cursor = db.cursor() 
    156             cursor.execute("UPDATE report SET title=%s,sql=%s,description=%s " 
    157                            "WHERE id=%s", (title, sql, description, id)) 
     156            cursor.execute("UPDATE report SET title=%s,query=%s,description=%s " 
     157                           "WHERE id=%s", (title, query, description, id)) 
    158158            db.commit() 
    159159        req.redirect(self.env.href.report(id)) 
    160160 
     
    178178    def _render_editor(self, req, db, id, copy=False): 
    179179        if id == -1: 
    180180            req.perm.assert_permission('REPORT_CREATE') 
    181             title = sql = description = '' 
     181            title = query = description = '' 
    182182        else: 
    183183            req.perm.assert_permission('REPORT_MODIFY') 
    184184            cursor = db.cursor() 
    185             cursor.execute("SELECT title,description,sql FROM report " 
     185            cursor.execute("SELECT title,description,query FROM report " 
    186186                           "WHERE id=%s", (id,)) 
    187187            row = cursor.fetchone() 
    188188            if not row: 
     
    190190                                     'Invalid Report Number') 
    191191            title = row[0] or '' 
    192192            description = row[1] or '' 
    193             sql = row[2] or '' 
     193            query = row[2] or '' 
    194194 
    195195        if copy: 
    196196            title += ' (copy)' 
     
    207207        req.hdf['report.id'] = id 
    208208        req.hdf['report.mode'] = 'edit' 
    209209        req.hdf['report.title'] = title 
    210         req.hdf['report.sql'] = sql 
     210        req.hdf['report.sql'] = query 
    211211        req.hdf['report.description'] = description 
    212212 
    213213    def _render_view(self, req, db, id): 
     
    404404            description = 'This is a list of reports available.' 
    405405        else: 
    406406            cursor = db.cursor() 
    407             cursor.execute("SELECT title,sql,description from report " 
     407            cursor.execute("SELECT title,query,description from report " 
    408408                           "WHERE id=%s", (id,)) 
    409409            row = cursor.fetchone() 
    410410            if not row: 
  • trac/upgrades/db17.py

    === modified file 'trac/upgrades/db17.py'
     
    22 
    33def do_upgrade(env, ver, cursor): 
    44    """Rename the columns `kind` and `change` in the `node_change` table for 
    5     compatibity with MySQL. 
     5    compatibity with MySQL, as well as the `sql` column in the `reports` table. 
    66    """ 
    7     cursor.execute("CREATE TEMP TABLE nc_old AS SELECT * FROM node_change") 
     7    db_connector, _ = DatabaseManager(env)._get_connector() 
     8 
     9    # alter node_change table 
     10    cursor.execute("CREATE TEMPORARY TABLE nc_old AS SELECT * FROM node_change") 
    811    cursor.execute("DROP TABLE node_change") 
    912 
    1013    table = Table('node_change', key=('rev', 'path', 'change_type'))[ 
     
    1619        Column('base_rev'), 
    1720        Index(['rev']) 
    1821    ] 
    19     db_connector, _ = DatabaseManager(env)._get_connector() 
    2022    for stmt in db_connector.to_sql(table): 
    2123        cursor.execute(stmt) 
    2224 
    2325    cursor.execute("INSERT INTO node_change (rev,path,node_type,change_type," 
    2426                   "base_path,base_rev) SELECT rev,path,kind,change," 
    2527                   "base_path,base_rev FROM nc_old") 
     28 
     29    # alter report table 
     30    cursor.execute("CREATE TEMPORARY TABLE report_old AS SELECT * FROM report") 
     31    cursor.execute("DROP TABLE report") 
     32 
     33    table = Table('report', key='id')[ 
     34        Column('id', auto_increment=True), 
     35        Column('author'), 
     36        Column('title'), 
     37        Column('query'), 
     38        Column('description') 
     39    ] 
     40    for stmt in db_connector.to_sql(table): 
     41        cursor.execute(stmt) 
     42 
     43    cursor.execute("INSERT INTO report (id,author,title,query,description) " 
     44                   "SELECT id,author,title,sql,description FROM report_old") 
  • trac/web/session.py

    === modified file 'trac/web/session.py'
     
    182182            # changed as to minimize the purging. 
    183183            mintime = now - PURGE_AGE 
    184184            self.env.log.debug('Purging old, expired, sessions.') 
    185             cursor.execute("DELETE FROM session WHERE authenticated=0 AND " 
    186                            "sid IN (SELECT sid FROM session WHERE " 
    187                            "var_name='last_visit' AND var_value < %s)", 
     185            cursor.execute("SELECT DISTINCT sid FROM session WHERE " 
     186                           "var_name='last_visit' AND var_value < %s", 
    188187                           (mintime,)) 
    189  
     188            args = cursor.fetchall() 
     189            if args: 
     190                sql = 'DELETE FROM session WHERE authenticated = 0 AND (' 
     191                sql += ' OR '.join(['sid = %s'] * len(args)) + ')' 
     192                cursor.execute(sql, args) 
    190193            db.commit() 
  • trac/wiki/macros.py

    === modified file 'trac/wiki/macros.py'
     
    9898        db = self.env.get_db_cnx() 
    9999        cursor = db.cursor() 
    100100 
    101         sql = 'SELECT name, max(time) FROM wiki' 
     101        sql = 'SELECT name, max(time) AS max_time FROM wiki' 
    102102        args = [] 
    103103        if prefix: 
    104104            sql += ' WHERE name LIKE %s' 
    105105            args.append(prefix + '%') 
    106         sql += ' GROUP BY name ORDER BY max(time) DESC' 
     106        sql += ' GROUP BY name ORDER BY max_time DESC' 
    107107        if limit: 
    108108            sql += ' LIMIT %s' 
    109109            args.append(limit) 
  • trac/wiki/web_ui.py

    === modified file 'trac/wiki/web_ui.py'
     
    393393        db = self.env.get_db_cnx() 
    394394        sql_query, args = search_to_sql(db, ['w1.name', 'w1.author', 'w1.text'], terms) 
    395395        cursor = db.cursor() 
    396         cursor.execute("SELECT w1.name,w1.time,w1.author,w1.text " 
    397                        "FROM wiki w1," 
    398                        "(SELECT name,max(version) AS ver " 
    399                        "FROM wiki GROUP BY name) w2 " 
    400                        "WHERE w1.version = w2.ver AND w1.name = w2.name " 
    401                        "AND " + sql_query, args) 
     396        cursor.execute('CREATE TEMPORARY TABLE w2 AS SELECT name,max(version) ' 
     397                       'AS ver FROM wiki GROUP BY name') 
     398        cursor.execute('SELECT w1.name,w1.time,w1.author,w1.text ' 
     399                       'FROM wiki w1,w2 ' 
     400                       'WHERE w1.version = w2.ver AND w1.name = w2.name ' 
     401                       'AND ' + sql_query, args) 
    402402 
    403403        for name, date, author, text in cursor: 
    404404            yield (self.env.href.wiki(name), 
    405405                   '%s: %s' % (name, shorten_line(text)), 
    406406                   date, author, 
    407407                   shorten_result(text, terms)) 
     408        cursor.execute('DROP TABLE w2')