Ticket #986: trac-mysql-r2975.patch
| File trac-mysql-r2975.patch, 15.7 KB (added by anonymous, 3 years ago) |
|---|
-
(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 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 = \ … … 445 445 446 446 default_components = ('trac.About', 'trac.attachment', 447 447 'trac.db.postgres_backend', 'trac.db.sqlite_backend', 448 'trac.db.mysql_backend', 448 449 'trac.mimeview.enscript', 'trac.mimeview.patch', 449 450 'trac.mimeview.php', 'trac.mimeview.rst', 450 451 'trac.mimeview.silvercity', 'trac.mimeview.txtl', -
trac/ticket/report.py
=== modified file 'trac/ticket/report.py'
122 122 req.redirect(self.env.href.report()) 123 123 124 124 title = req.args.get('title', '') 125 sql = req.args.get('sql', '')125 query = req.args.get('query', '') 126 126 description = req.args.get('description', '') 127 127 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)) 130 130 id = db.get_last_id(cursor, 'report') 131 131 db.commit() 132 132 req.redirect(self.env.href.report(id)) … … 150 150 151 151 if not req.args.has_key('cancel'): 152 152 title = req.args.get('title', '') 153 sql = req.args.get('sql', '')153 query = req.args.get('query', '') 154 154 description = req.args.get('description', '') 155 155 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)) 158 158 db.commit() 159 159 req.redirect(self.env.href.report(id)) 160 160 … … 178 178 def _render_editor(self, req, db, id, copy=False): 179 179 if id == -1: 180 180 req.perm.assert_permission('REPORT_CREATE') 181 title = sql= description = ''181 title = query = description = '' 182 182 else: 183 183 req.perm.assert_permission('REPORT_MODIFY') 184 184 cursor = db.cursor() 185 cursor.execute("SELECT title,description, sqlFROM report "185 cursor.execute("SELECT title,description,query FROM report " 186 186 "WHERE id=%s", (id,)) 187 187 row = cursor.fetchone() 188 188 if not row: … … 190 190 'Invalid Report Number') 191 191 title = row[0] or '' 192 192 description = row[1] or '' 193 sql= row[2] or ''193 query = row[2] or '' 194 194 195 195 if copy: 196 196 title += ' (copy)' … … 207 207 req.hdf['report.id'] = id 208 208 req.hdf['report.mode'] = 'edit' 209 209 req.hdf['report.title'] = title 210 req.hdf['report.sql'] = sql210 req.hdf['report.sql'] = query 211 211 req.hdf['report.description'] = description 212 212 213 213 def _render_view(self, req, db, id): … … 404 404 description = 'This is a list of reports available.' 405 405 else: 406 406 cursor = db.cursor() 407 cursor.execute("SELECT title, sql,description from report "407 cursor.execute("SELECT title,query,description from report " 408 408 "WHERE id=%s", (id,)) 409 409 row = cursor.fetchone() 410 410 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 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") 8 11 cursor.execute("DROP TABLE node_change") 9 12 10 13 table = Table('node_change', key=('rev', 'path', 'change_type'))[ … … 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 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'
182 182 # changed as to minimize the purging. 183 183 mintime = now - PURGE_AGE 184 184 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", 188 187 (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) 190 193 db.commit() -
trac/wiki/macros.py
=== modified file 'trac/wiki/macros.py'
98 98 db = self.env.get_db_cnx() 99 99 cursor = db.cursor() 100 100 101 sql = 'SELECT name, max(time) FROM wiki'101 sql = 'SELECT name, max(time) AS max_time FROM wiki' 102 102 args = [] 103 103 if prefix: 104 104 sql += ' WHERE name LIKE %s' 105 105 args.append(prefix + '%') 106 sql += ' GROUP BY name ORDER BY max (time)DESC'106 sql += ' GROUP BY name ORDER BY max_time DESC' 107 107 if limit: 108 108 sql += ' LIMIT %s' 109 109 args.append(limit) -
trac/wiki/web_ui.py
=== modified file 'trac/wiki/web_ui.py'
393 393 db = self.env.get_db_cnx() 394 394 sql_query, args = search_to_sql(db, ['w1.name', 'w1.author', 'w1.text'], terms) 395 395 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) 402 402 403 403 for name, date, author, text in cursor: 404 404 yield (self.env.href.wiki(name), 405 405 '%s: %s' % (name, shorten_line(text)), 406 406 date, author, 407 407 shorten_result(text, terms)) 408 cursor.execute('DROP TABLE w2')
