Ticket #986: trac-mysql-r2946.2.patch
| File trac-mysql-r2946.2.patch, 11.9 KB (added by Andres Salomon <dilinger@…>, 3 years ago) |
|---|
-
(a) /dev/null vs. (b) trac/db/mysql_backend.py
=== 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 21 from trac.core import * 22 from trac.db.api import IDatabaseConnector 23 from trac.db.util import ConnectionWrapper 24 25 class MySQLConnector(Component): 26 """MySQL database support. Still extremely experimental! 27 Currently only supports database urls of the following formats: 28 mysql://user:password@host/database 29 mysql://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 93 class 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'
40 40 'path': '/trac'}), 41 41 _parse_db_str('postgres://john:letmein@localhost:9431/trac')) 42 42 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')) 43 52 44 53 def suite(): 45 54 return unittest.makeSuite(ParseConnectionStringTestCase,'test') -
trac/db_default.py
=== modified file 'trac/db_default.py'
148 148 Column('id', auto_increment=True), 149 149 Column('author'), 150 150 Column('title'), 151 Column(' sql'),151 Column('query'), 152 152 Column('description')], 153 153 ] 154 154 … … 379 379 ('name', 'value'), 380 380 (('database_version', str(db_version)),)), 381 381 ('report', 382 ('author', 'title', ' sql', 'description'),382 ('author', 'title', 'query', 'description'), 383 383 __mkreports(reports))) 384 384 385 385 default_config = \ … … 442 442 443 443 default_components = ('trac.About', 'trac.attachment', 444 444 'trac.db.postgres_backend', 'trac.db.sqlite_backend', 445 'trac.db.mysql_backend', 445 446 'trac.mimeview.enscript', 'trac.mimeview.patch', 446 447 'trac.mimeview.php', 'trac.mimeview.rst', 447 448 'trac.mimeview.silvercity', 'trac.mimeview.txtl', -
trac/ticket/report.py
=== modified file 'trac/ticket/report.py'
148 148 req.redirect(self.env.href.report()) 149 149 150 150 title = req.args.get('title', '') 151 sql = req.args.get('sql', '')151 query = req.args.get('query', '') 152 152 description = req.args.get('description', '') 153 153 cursor = db.cursor() 154 cursor.execute("INSERT INTO report (title, sql,description) "155 "VALUES (%s,%s,%s)", (title, sql, description))154 cursor.execute("INSERT INTO report (title,query,description) " 155 "VALUES (%s,%s,%s)", (title, query, description)) 156 156 id = db.get_last_id(cursor, 'report') 157 157 db.commit() 158 158 req.redirect(self.env.href.report(id)) … … 176 176 177 177 if not req.args.has_key('cancel'): 178 178 title = req.args.get('title', '') 179 sql = req.args.get('sql', '')179 query = req.args.get('query', '') 180 180 description = req.args.get('description', '') 181 181 cursor = db.cursor() 182 cursor.execute("UPDATE report SET title=%s, sql=%s,description=%s "183 "WHERE id=%s", (title, sql, description, id))182 cursor.execute("UPDATE report SET title=%s,query=%s,description=%s " 183 "WHERE id=%s", (title, query, description, id)) 184 184 db.commit() 185 185 req.redirect(self.env.href.report(id)) 186 186 … … 204 204 def _render_editor(self, req, db, id, copy=False): 205 205 if id == -1: 206 206 req.perm.assert_permission('REPORT_CREATE') 207 title = sql= description = ''207 title = query = description = '' 208 208 else: 209 209 req.perm.assert_permission('REPORT_MODIFY') 210 210 cursor = db.cursor() 211 cursor.execute("SELECT title,description, sqlFROM report "211 cursor.execute("SELECT title,description,query FROM report " 212 212 "WHERE id=%s", (id,)) 213 213 row = cursor.fetchone() 214 214 if not row: … … 216 216 'Invalid Report Number') 217 217 title = row[0] or '' 218 218 description = row[1] or '' 219 sql= row[2] or ''219 query = row[2] or '' 220 220 221 221 if copy: 222 222 title += ' (copy)' … … 233 233 req.hdf['report.id'] = id 234 234 req.hdf['report.mode'] = 'edit' 235 235 req.hdf['report.title'] = title 236 req.hdf['report.sql'] = sql236 req.hdf['report.sql'] = query 237 237 req.hdf['report.description'] = description 238 238 239 239 def _render_view(self, req, db, id): … … 426 426 description = 'This is a list of reports available.' 427 427 else: 428 428 cursor = db.cursor() 429 cursor.execute("SELECT title, sql,description from report "429 cursor.execute("SELECT title,query,description from report " 430 430 "WHERE id=%s", (id,)) 431 431 row = cursor.fetchone() 432 432 if not row: -
trac/upgrades/db17.py
=== modified file 'trac/upgrades/db17.py'
2 2 3 3 def do_upgrade(env, ver, cursor): 4 4 """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. 6 6 """ 7 db_connector, _ = DatabaseManager(env)._get_connector() 8 9 # alter node_change table 7 10 cursor.execute("CREATE TEMP TABLE nc_old AS SELECT * FROM node_change") 8 11 cursor.execute("DROP TABLE node_change") 9 12 … … 16 19 Column('base_rev'), 17 20 Index(['rev']) 18 21 ] 19 db_connector, _ = DatabaseManager(env)._get_connector()20 22 for stmt in db_connector.to_sql(table): 21 23 cursor.execute(stmt) 22 24 23 25 cursor.execute("INSERT INTO node_change (rev,path,node_type,change_type," 24 26 "base_path,base_rev) SELECT rev,path,kind,change," 25 27 "base_path,base_rev FROM nc_old") 28 29 # alter report table 30 cursor.execute("CREATE TEMP 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'
181 181 # changed as to minimize the purging. 182 182 mintime = now - PURGE_AGE 183 183 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 cursor.execute("SELECT DISTINCT sid FROM session WHERE " 185 "var_name='last_visit' AND var_value < %s", 187 186 (mintime,)) 188 187 args = cursor.fetchall() 188 if args: 189 sql = 'DELETE FROM session WHERE authenticated = 0 AND (' 190 sql += ' OR '.join(['sid = %s'] * len(args)) + ')' 191 cursor.execute(sql, args) 189 192 db.commit()
