Edgewall Software

Ticket #986: trac_mysql_r2625.patch

File trac_mysql_r2625.patch, 10.4 KB (added by trac@…, 3 years ago)

patch against r2625 to enable experimental MySQL support

  • trac/db_default.py

     
    9494 
    9595    # Ticket system 
    9696    Table('ticket', key='id')[ 
    97         Column('id', auto_increment=True), 
     97        Column('id', type='int', auto_increment=True), 
    9898        Column('type'), 
    9999        Column('time', type='int'), 
    100100        Column('changetime', type='int'), 
     
    145145 
    146146    # Report system 
    147147    Table('report', key='id')[ 
    148         Column('id', auto_increment=True), 
     148        Column('id', type='int', auto_increment=True), 
    149149        Column('author'), 
    150150        Column('title'), 
    151151        Column('sql'), 
     
    437437) 
    438438 
    439439default_components = ('trac.About', 'trac.attachment', 
     440                      'trac.db.mysql_backend', 
    440441                      'trac.db.postgres_backend', 'trac.db.sqlite_backend', 
    441442                      'trac.mimeview.enscript', 'trac.mimeview.patch', 
    442443                      'trac.mimeview.php', 'trac.mimeview.rst', 
  • trac/ticket/api.py

     
    167167            return 
    168168        db = self.env.get_db_cnx() 
    169169        sql, args = query_to_sql(db, query, 'b.newvalue') 
    170         sql2, args2 = query_to_sql(db, query, 'summary||keywords||description||reporter||cc') 
     170        search_fields = ['summary', 'keywords', 'description', 'reporter', 'cc'] 
     171        sql_seq = [] 
     172        for f in search_fields: 
     173            subsql, subarg = query_to_sql(db, query, f) 
     174            sql_seq.append(subsql) 
     175            args += subarg 
     176        sql2 = ' OR '.join(sql_seq) 
    171177        cursor = db.cursor() 
    172         cursor.execute("SELECT DISTINCT a.summary,a.description,a.reporter, " 
    173                        "a.keywords,a.id,a.time FROM ticket a " 
    174                        "LEFT JOIN ticket_change b ON a.id = b.ticket " 
    175                        "WHERE (b.field='comment' AND %s ) OR %s" % (sql, sql2), 
    176                        args + args2) 
     178        sql3 = "SELECT DISTINCT a.summary,a.description,a.reporter, " + \ 
     179                       "a.keywords,a.id,a.time FROM ticket a " +  \ 
     180                       "LEFT JOIN ticket_change b ON a.id = b.ticket " + \ 
     181                       "WHERE (b.field='comment' AND %s ) OR %s" % (sql, sql2) 
     182        cursor.execute(sql3, args) 
    177183        for summary,desc,author,keywords,tid,date in cursor: 
    178184            yield (self.env.href.ticket(tid), 
    179185                   '#%d: %s' % (tid, util.escape(util.shorten_line(summary))), 
  • trac/db/mysql_backend.py

     
     1# -*- coding: iso8859-1 -*- 
     2# 
     3# Copyright (C) 2005 Edgewall Software 
     4# Copyright (C) 2005 Jeff Weiss <trac@jeffweiss.org> 
     5# All rights reserved. 
     6# 
     7# This software is licensed as described in the file COPYING, which 
     8# you should have received as part of this distribution. The terms 
     9# are also available at http://trac.edgewall.com/license.html. 
     10# 
     11# This software consists of voluntary contributions made by many 
     12# individuals. For the exact contribution history, see the revision 
     13# history and logs, available at http://projects.edgewall.com/trac/. 
     14# 
     15# Author: Jeff Weiss <trac@jeffweiss.org> 
     16 
     17from trac.core import * 
     18from trac.db.api import IDatabaseConnector 
     19from trac.db.util import ConnectionWrapper 
     20 
     21import MySQLdb 
     22 
     23class MySQLConnector(Component): 
     24    """MySQL database support.  Still extremely experimental! 
     25Currently only supports database urls of the following formats: 
     26mysql://user:password@host/database 
     27mysql://user:password@host:port/database 
     28""" 
     29 
     30    implements(IDatabaseConnector) 
     31 
     32    def get_supported_schemes(self): 
     33        return [('mysql', 1)] 
     34 
     35    def get_connection(self, path, user=None, password=None, host=None, 
     36                       port=None, params={}): 
     37        return MySQLConnection(path, user, password, host, port, params) 
     38 
     39    def init_db(self, path, user=None, password=None, host=None, port=None, 
     40                params={}): 
     41        cnx = self.get_connection(path, user, password, host, port, params) 
     42        cursor = cnx.cursor() 
     43        from trac.db_default import schema 
     44        for table in schema: 
     45            for stmt in self.to_sql(table): 
     46                cursor.execute(stmt) 
     47        cnx.commit() 
     48 
     49    def to_sql(self, table): 
     50        sql = ["CREATE TABLE %s (" % table.name] 
     51        coldefs = [] 
     52        textkeys = [] 
     53        textcols = [] 
     54        mytablekey = [] 
     55        num_key_text = 0 
     56        for column in table.columns: 
     57            ctype = column.type 
     58            if column.auto_increment: 
     59                ctype += " AUTO_INCREMENT" 
     60            if column.name in table.key: 
     61                mytablekey.append(column.name) 
     62                if column.type == 'text': 
     63                    num_key_text += 1 
     64                    textkeys.append(column.name) 
     65            if column.type == 'text': 
     66                textcols.append(column.name) 
     67            coldefs.append("    `%s` %s" % (column.name, ctype)) 
     68        if len(table.key) >= 1: 
     69            if num_key_text == 0: 
     70                num_key_text = 1 
     71            size = 767 / num_key_text 
     72            keysize = "(%s)" % size 
     73            for key in textkeys: 
     74                if key in mytablekey: 
     75                    mytablekey.remove(key) 
     76                sizedkey = '`' + key + '`' + keysize 
     77                mytablekey.append(sizedkey) 
     78            coldefs.append("    CONSTRAINT %s_pk PRIMARY KEY (%s)" 
     79                           % (table.name, ','.join(mytablekey))) 
     80        sql.append(',\n'.join(coldefs) + '\n)') 
     81        yield '\n'.join(sql) 
     82# This does not work because what I need to do is not add something about 
     83# the index size for the text fields because MySQL apparrently can't index 
     84# on the entire length of the text field 
     85        for index in table.indices: 
     86            myidxcols = set([]) 
     87            processedidx = set([]) 
     88            for col in index.columns: 
     89                if col in textcols: 
     90                    myidxcols.add(col) 
     91            myidx = set(index.columns) - myidxcols 
     92            for col in myidx: 
     93               escaped_col = '`'+col+'`' 
     94               processedidx.add(escaped_col)  
     95            if len(myidxcols) > 0: 
     96                size = 767 / len(myidxcols) 
     97                idxsize  = "(%s)" % size 
     98                for col in myidxcols: 
     99                   sizedidx = '`'+col+'`'+idxsize 
     100                   processedidx.add(sizedidx)  
     101            yield "CREATE INDEX %s_%s_idx ON %s (%s)" % (table.name,  
     102                   '_'.join(index.columns), table.name, ','.join(processedidx)) 
     103 
     104 
     105class MySQLConnection(ConnectionWrapper): 
     106    """Connection wrapper for MySQL.""" 
     107 
     108    poolable = True 
     109 
     110    def __init__(self, path, usr=None, password=None, hst=None, prt=None, 
     111                 params={}): 
     112        cnx = MySQLdb.connect(db=path[1:], user=usr, passwd=password, host=hst, port=prt) 
     113        ConnectionWrapper.__init__(self, cnx) 
     114 
     115    def cast(self, column, type): 
     116        # Temporary hack needed for the union of selects in the search module 
     117        return 'CAST(%s AS %s)' % (column, type) 
     118 
     119    def like(self): 
     120        # Temporary hack needed for the case-insensitive string matching in the 
     121        # search module 
     122        return 'LIKE' 
     123 
     124    def get_last_id(self, cursor, table, column='id'): 
     125        sql = "SELECT MAX(`%s`) FROM %s" % (column, table) 
     126        cursor.execute(sql) 
     127        return cursor.fetchone()[0] 
  • trac/versioncontrol/web_ui/changeset.py

     
    384384            return 
    385385        authzperm = SubversionAuthorizer(self.env, req.authname) 
    386386        db = self.env.get_db_cnx() 
    387         sql, args = query_to_sql(db, query, 'message||author') 
     387        query_fields = ['message', 'author'] 
     388        sql_seq = [] 
     389        args = [] 
     390        for f in query_fields: 
     391            subsql, subarg = query_to_sql(db, query, f) 
     392            sql_seq.append(subsql)  
     393            args += subarg 
     394        sql = ' OR '.join(sql_seq) 
    388395        cursor = db.cursor() 
    389396        cursor.execute("SELECT rev,time,author,message " 
    390397                       "FROM revision WHERE " + sql, args) 
  • trac/versioncontrol/cache.py

     
    8585                    kind = kindmap[kind] 
    8686                    action = actionmap[action] 
    8787                    cursor.execute("INSERT INTO node_change (rev,path,kind," 
    88                                    "change,base_path,base_rev) " 
     88                                   "`change`,base_path,base_rev) " 
    8989                                   "VALUES (%s,%s,%s,%s,%s,%s)", 
    9090                                   (str(current_rev), path, kind, action, 
    9191                                   base_path, base_rev)) 
  • trac/web/session.py

     
    181181            # changed as to minimize the purging. 
    182182            mintime = now - PURGE_AGE 
    183183            self.env.log.debug('Purging old, expired, sessions.') 
    184             cursor.execute("DELETE FROM session WHERE authenticated=0 AND " 
    185                            "sid IN (SELECT sid FROM session WHERE " 
    186                            "var_name='last_visit' AND var_value < %s)", 
     184            # This is a temporary hack because MySQL doesn't support 
     185            # a delete statement with the subquery coming from the same 
     186            # table 
     187            cursor.execute("SELECT sid from session WHERE " 
     188                           "var_name='last_visit' AND var_value < %s", 
    187189                           (mintime,)) 
    188  
     190            sids = cursor.fetchall()  
     191            if len(sids) >= 1: 
     192                vals = [] 
     193                sids_args = [] 
     194                for t in sids: 
     195                    vals.append(t[0]) 
     196                    sids_args.append("sid = %s") 
     197                sql = "DELETE FROM session WHERE authenticated = 0 AND (%s)" % ' OR '.join(sids_args) 
     198                cursor.execute(sql, vals) 
    189199            db.commit()