PostgresqlPatch: pgsql_patch_12.diff
| File pgsql_patch_12.diff, 54.5 kB (added by brad <brad@dsource.org>, 7 years ago) |
|---|
-
scripts/trac-admin
old new 27 27 import time 28 28 import cmd 29 29 import shlex 30 import sqlite31 30 import StringIO 31 import traceback 32 32 33 33 from trac import perm 34 34 from trac import util … … 96 96 return 0 97 97 return 1 98 98 99 def env_create(self ):99 def env_create(self, db_str): 100 100 try: 101 self.__env = trac.Environment.Environment (self.envname, create=1)101 self.__env = trac.Environment.Environment (self.envname, 1, db_str) 102 102 return self.__env 103 103 except Exception, e: 104 104 print 'Failed to create environment.', e 105 print traceback.print_exc() 105 106 sys.exit(1) 106 107 107 108 def db_open(self): … … 115 116 116 117 def db_execsql (self, sql, cursor=None): 117 118 data = [] 119 row = None 118 120 if not cursor: 119 121 cnx=self.db_open() 120 122 cursor = cnx.cursor() … … 122 124 cnx = None 123 125 cursor.execute(sql) 124 126 while 1: 125 row = cursor.fetchone() 127 try: 128 row = cursor.fetchone() 129 except: 130 pass 126 131 if row == None: 127 132 break 128 133 data.append(row) … … 455 460 456 461 ## Initenv 457 462 _help_initenv = [('initenv', 'Create and initialize a new environment interactively'), 458 ('initenv <projectname> <repospath> <templatepath> ',463 ('initenv <projectname> <repospath> <templatepath> <dbms> [dbms options]', 459 464 'Create and initialize a new environment from arguments')] 460 465 461 466 def do_initdb(self, line): … … 488 493 dt = trac.siteconfig.__default_templates_dir__ 489 494 prompt = 'Templates directory [%s]> ' % dt 490 495 returnvals.append(raw_input(prompt) or dt) 496 print 497 print " Please enter the database in which you wish to store Trac data." 498 print " Default is 'sqlite', but others are supported:" 499 print " 'sqlite' - SQLite http://www.sqlite.org" 500 print " 'pypgsql' - PostgreSQL http://www.postgresql.org" 501 print 502 ddb = "sqlite" 503 prompt = 'database system [%s]> ' % ddb 504 dbms = raw_input(prompt) or ddb 505 returnvals.append(dbms) 506 507 # PostgreSQL - additional questions 508 if dbms == "pypgsql": 509 print 510 print " Please enter the host of your PostgreSQL database." 511 print " Default is 'localhost'" 512 print 513 host = "localhost" 514 prompt = "PostgreSQL host [%s]> " % host 515 returnvals.append(raw_input(prompt) or host) 516 517 print 518 print " Please enter the port of your PostgreSQL database." 519 print " Default is '5432'" 520 print 521 port = "5432" 522 prompt = "PostgreSQL port [%s]> " % port 523 returnvals.append(raw_input(prompt) or port) 524 525 print 526 print " Please enter the name of your PostgreSQL database." 527 print " Default is 'trac'" 528 print " Note: This database should not exist, as trac-admin will" \ 529 " attempt to create it." 530 print 531 name = "trac" 532 prompt = "PostgreSQL database [%s]> " % name 533 returnvals.append(raw_input(prompt) or name) 534 535 print 536 print " Please enter the user of your PostgreSQL database." 537 print " Default is 'trac'" 538 print 539 user = "trac" 540 prompt = "PostgreSQL user [%s]> " % user 541 returnvals.append(raw_input(prompt) or user) 542 543 print 544 print " Please enter the password of your PostgreSQL database." 545 print " Default is 'trac'" 546 print 547 password = "trac" 548 prompt = "PostgreSQL password [%s]> " % password 549 returnvals.append(raw_input(prompt) or password) 550 491 551 return returnvals 492 552 493 553 def do_initenv(self, line): … … 498 558 project_name = None 499 559 repository_dir = None 500 560 templates_dir = None 561 db_str = None 501 562 if len(arg) == 1: 502 563 returnvals = self.get_initenv_args() 503 564 project_name = returnvals[0] 504 565 repository_dir = returnvals[1] 505 566 templates_dir = returnvals[2] 506 elif len(arg)!= 3: 567 db_str = returnvals[3] 568 elif len(arg) < 4: 507 569 print 'Wrong number of arguments to initenv %d' % len(arg) 508 570 return 509 571 else: 510 572 project_name = arg[0] 511 573 repository_dir = arg[1] 512 574 templates_dir = arg[2] 575 db_str = arg[3] 576 577 # sqlite-specific stuff 578 if db_str.lower() == "sqlite": 579 db_str = 'sqlite:db/trac.db' 580 581 # postgres-specific stuff 582 if db_str.lower() == "pypgsql": 583 if len(arg) == 1: 584 host = returnvals[4] 585 port = returnvals[5] 586 database = returnvals[6] 587 user = returnvals[7] 588 password = returnvals[8] 589 elif len(arg) != 9: 590 print 'Wrong number of arguments to initenv %d' % len(arg) 591 print 'For PostgreSQL (after pypgsql): <host> <port> <database>' \ 592 ' <user> <password>' 593 return 594 else: 595 host = arg[4] 596 port = arg[5] 597 database = arg[6] 598 user = arg[7] 599 password = arg[8] 600 db_str = "pypgsql:host='%s:%s',database='%s',user='%s'," \ 601 "password='%s'" \ 602 % (host, port, database, user, password) 603 createdb_str = "createdb --host=%s --port=%s --owner=%s " \ 604 "--username=%s %s\n" \ 605 % (host, port, user, user, database) 606 print createdb_str 607 try: 608 # TODO: this is not cross-platform. 609 # for Windows, look at win32pipe.popen() 610 os.popen(createdb_str) 611 except Exception, e: 612 print "Error creating PostgreSQL database: %s" % e 613 print "command: %s" % createdb_str 614 513 615 from svn import util, repos, core 514 616 core.apr_initialize() 515 617 pool = core.svn_pool_create(None) … … 528 630 return 529 631 try: 530 632 print 'Creating and Initializing Project' 531 self.env_create() 633 634 # create db 635 self.env_create(db_str) 532 636 cnx = self.__env.get_db_cnx() 533 637 print ' Inserting default data' 534 638 self.__env.insert_default_data() … … 541 645 print ' project.name' 542 646 self.__env.set_config('project', 'name', project_name) 543 647 self.__env.save_config() 648 544 649 # Add a few default wiki pages 545 650 print ' Installing wiki pages' 546 651 cursor = cnx.cursor() … … 550 655 sync.sync(cnx, rep, fs_ptr, pool) 551 656 except Exception, e: 552 657 print 'Failed to initialize database.', e 658 print traceback.print_exc() 553 659 sys.exit(2) 554 660 555 661 … … 671 777 self.do_help ('wiki') 672 778 except Exception, e: 673 779 print 'Wiki %s failed:' % arg[0], e 780 print traceback.print_exc() 781 674 782 675 783 def _do_wiki_list(self): 676 784 data = self.db_execsql('SELECT name,max(version),time' -
setup.py
old new 199 199 author_email="info@edgewall.com", 200 200 license=LICENSE, 201 201 url=URL, 202 packages=['trac', 'trac.web', 'trac.upgrades', 'trac.wikimacros', 'trac.mimeviewers'], 202 packages=['trac', 'trac.dbms', 'trac.web', 'trac.upgrades', 203 'trac.wikimacros', 'trac.mimeviewers'], 203 204 data_files=[(_p('share/trac/templates'), glob('templates/*')), 204 205 (_p('share/trac/htdocs'), glob(_p('htdocs/*.*')) + [_p('htdocs/README')]), 205 206 (_p('share/trac/htdocs/css'), glob(_p('htdocs/css/*'))), -
trac/db_default.py
old new 37 37 ## Default data 38 38 ## 39 39 40 schema = """ 41 CREATE TABLE revision ( 42 rev integer PRIMARY KEY, 43 time integer, 44 author text, 45 message text 46 ); 47 CREATE TABLE node_change ( 48 rev integer, 49 name text, 50 change char(1), 51 UNIQUE(rev, name, change) 52 ); 53 CREATE TABLE auth_cookie ( 54 cookie text, 55 name text, 56 ipnr text, 57 time integer, 58 UNIQUE(cookie, name, ipnr) 59 ); 60 CREATE TABLE enum ( 61 type text, 62 name text, 63 value text, 64 UNIQUE(name,type) 65 ); 66 CREATE TABLE system ( 67 name text PRIMARY KEY, 68 value text, 69 UNIQUE(name) 70 ); 71 CREATE TABLE ticket ( 72 id integer PRIMARY KEY, 73 time integer, -- the time it was created 74 changetime integer, 75 component text, 76 severity text, 77 priority text, 78 owner text, -- who is this ticket assigned to 79 reporter text, 80 cc text, -- email addresses to notify 81 url text, -- url related to this ticket 82 version text, -- 83 milestone text, -- 84 status text, 85 resolution text, 86 summary text, -- one-line summary 87 description text, -- problem description (long) 88 keywords text 89 ); 90 CREATE TABLE ticket_change ( 91 ticket integer, 92 time integer, 93 author text, 94 field text, 95 oldvalue text, 96 newvalue text, 97 UNIQUE(ticket, time, field) 98 ); 99 CREATE TABLE ticket_custom ( 100 ticket integer, 101 name text, 102 value text, 103 UNIQUE(ticket,name) 104 ); 105 CREATE TABLE report ( 106 id integer PRIMARY KEY, 107 author text, 108 title text, 109 sql text, 110 description text 111 ); 112 CREATE TABLE permission ( 113 username text, -- 114 action text, -- allowable activity 115 UNIQUE(username,action) 116 ); 117 CREATE TABLE component ( 118 name text PRIMARY KEY, 119 owner text 120 ); 121 CREATE TABLE milestone ( 122 name text PRIMARY KEY, 123 due integer, 124 completed integer, 125 description text 126 ); 127 CREATE TABLE version ( 128 name text PRIMARY KEY, 129 time integer 130 ); 131 CREATE TABLE wiki ( 132 name text, 133 version integer, 134 time integer, 135 author text, 136 ipnr text, 137 text text, 138 comment text, 139 readonly integer, 140 UNIQUE(name,version) 141 ); 142 CREATE TABLE attachment ( 143 type text, 144 id text, 145 filename text, 146 size integer, 147 time integer, 148 description text, 149 author text, 150 ipnr text, 151 UNIQUE(type,id,filename) 152 ); 40 schema = ( 41 ('revision', ( 42 ('rev', 'auto', '', 'u'), 43 ('time', 'int', '', ''), 44 ('author', 'text', '', ''), 45 ('message', 'text', '', ''))), 46 ('node_change', ( 47 ('rev', 'int', '', 'u'), 48 ('name', 'text', '', 'u'), 49 ('change', 'text', '1', 'u'))), 50 ('auth_cookie', ( 51 ('cookie', 'text', '', 'u'), 52 ('name', 'text', '', 'u'), 53 ('ipnr', 'text', '', 'u'), 54 ('time', 'int', '', ''))), 55 ('enum', ( 56 ('type', 'text', '', 'u'), 57 ('name', 'text', '', 'u'), 58 ('value', 'text', '', ''))), 59 ('system', ( 60 ('name', 'text', '', 'u'), 61 ('value', 'text', '', ''))), 62 ('ticket', ( 63 ('id', 'auto', '', 'u'), 64 ('time', 'int', '', ''), 65 ('changetime', 'int', '', ''), 66 ('component', 'text', '', ''), 67 ('severity', 'text', '', ''), 68 ('priority', 'text', '', ''), 69 ('owner', 'text', '', ''), 70 ('reporter', 'text', '', ''), 71 ('cc', 'text', '', ''), 72 ('url', 'text', '', ''), 73 ('version', 'text', '', ''), 74 ('milestone', 'text', '', ''), 75 ('status', 'text', '', ''), 76 ('resolution', 'text', '', ''), 77 ('summary', 'text', '', ''), 78 ('description', 'text', '', ''), 79 ('keywords', 'text', '', ''))), 80 ('ticket_change', ( 81 ('ticket', 'int', '', 'u'), 82 ('time', 'int', '', 'u'), 83 ('author', 'text', '', ''), 84 ('field', 'text', '', 'u'), 85 ('oldvalue', 'text', '', ''), 86 ('newvalue', 'text', '', ''))), 87 ('ticket_custom', ( 88 ('ticket', 'int', '', 'u'), 89 ('name', 'text', '', 'u'), 90 ('value', 'text', '', ''))), 91 ('report', ( 92 ('id', 'auto', '', 'u'), 93 ('author', 'text', '', ''), 94 ('title', 'text', '', ''), 95 ('sql', 'text', '', ''), 96 ('description', 'text', '', ''))), 97 ('permission', ( 98 ('username', 'text', '', 'u'), 99 ('action', 'text', '', 'u'))), 100 ('component', ( 101 ('name', 'text', '', 'u'), 102 ('owner', 'text', '', ''))), 103 ('milestone', ( 104 ('name', 'text', '', 'u'), 105 ('due', 'int', '', ''), 106 ('completed', 'int', '', ''), 107 ('description', 'text', '', ''))), 108 ('version', ( 109 ('name', 'text', '', 'u'), 110 ('time', 'int', '', ''))), 111 ('wiki', ( 112 ('name', 'text', '', 'u'), 113 ('version', 'int', '', 'u'), 114 ('time', 'int', '', ''), 115 ('author', 'text', '', ''), 116 ('ipnr', 'text', '', ''), 117 ('text', 'text', '', ''), 118 ('comment', 'text', '', ''), 119 ('readonly', 'int', '', ''))), 120 ('attachment', ( 121 ('type', 'text', '', 'u'), 122 ('id', 'text', '', 'u'), 123 ('filename', 'text', '', 'u'), 124 ('size', 'int', '', ''), 125 ('time', 'int', '', ''), 126 ('description', 'text', '', ''), 127 ('author', 'text', '', ''), 128 ('ipnr', 'text', '', ''))), 129 ('session', ( 130 ('sid', 'text', '', 'u'), 131 ('username', 'text', '', ''), 132 ('var_name', 'text', '', 'u'), 133 ('var_value', 'text', '', '')))) 134 135 # TODO: do we need these, or will the pk's and u's above do? 136 #CREATE INDEX node_change_idx ON node_change(rev); 137 #CREATE INDEX ticket_change_idx ON ticket_change(ticket, time); 138 #CREATE INDEX wiki_idx ON wiki(name,version); 139 #CREATE INDEX session_idx ON session(sid,var_name); 140 #""" 153 141 154 CREATE TABLE session (155 sid text,156 username text,157 var_name text,158 var_value text,159 UNIQUE(sid,var_name)160 );161 142 162 CREATE INDEX node_change_idx ON node_change(rev);163 CREATE INDEX ticket_change_idx ON ticket_change(ticket, time);164 CREATE INDEX wiki_idx ON wiki(name,version);165 CREATE INDEX session_idx ON session(sid,var_name);166 """167 168 143 ## 169 144 ## Default Reports 170 145 ## … … 286 261 FROM ticket t,enum p 287 262 WHERE p.name=t.priority AND p.type='priority' 288 263 ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'), 289 (CASE status WHEN 'closed' THEN modifiedELSE (-1)*p.value END) DESC264 (CASE status WHEN 'closed' THEN changetime ELSE (-1)*p.value END) DESC 290 265 """), 291 266 #---------------------------------------------------------------------------- 292 267 ('My Tickets', -
trac/dbms/db_pypgsql.py
old new 1 # -*- coding: iso8859-1 -*- 2 # 3 # Copyright (C) 2005 Edgewall Software 4 # Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de> 5 # 6 # Trac is free software; you can redistribute it and/or 7 # modify it under the terms of the GNU General Public License as 8 # published by the Free Software Foundation; either version 2 of the 9 # License, or (at your option) any later version. 10 # 11 # Trac is distributed in the hope that it will be useful, 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 # General Public License for more details. 15 # 16 # You should have received a copy of the GNU General Public License 17 # along with this program; if not, write to the Free Software 18 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 # 20 # Author: Christopher Lenz <cmlenz@gmx.de> 21 22 import os 23 from pyPgSQL import PgSQL 24 import string 25 26 from trac import db_default 27 28 from trac.db import ConnectionWrapper 29 30 from trac.util import TracError 31 32 class PyPgSQLConnection(ConnectionWrapper): 33 """ 34 Connection wrapper for PostgreSQL using the PyPgSQL driver. 35 """ 36 37 __slots__ = ['cnx'] 38 39 def __init__(self, dbargs, env, create=0): 40 if create: 41 self.create_db(env.path, dbargs) 42 43 # TODO: check to see if db exists, or throw error (see sqlite module) 44 45 args = self.parse_args(dbargs) 46 47 cnx = PgSQL.connect("", **args) 48 ConnectionWrapper.__init__(self, cnx) 49 50 def get_last_id(self, table, field): 51 cursor = self.cursor() 52 sql = "SELECT %s FROM %s " \ 53 "WHERE %s = CURRVAL('%s_%s_seq')" \ 54 % (field, table, field, table, field) 55 cursor.execute(sql) 56 id = cursor.fetchone()[0] 57 cursor.close() 58 return id 59 60 def create_db(self, path, dbargs): 61 62 # TODO: make the database 63 # Are we able to do this without su - postgres ?? 64 # Should we move what's in trac-admin to here? Probably... 65 66 args = self.parse_args(dbargs) 67 68 # get a connection 69 cnx = PgSQL.connect("", **args) 70 71 # populate the default schema 72 schema = db_default.schema 73 sql = "" 74 75 for tables in schema: 76 table = tables[0] 77 fields = tables[1] 78 sql_fields = [] 79 unique = [] 80 sql += "CREATE TABLE %s (\n" % table 81 82 for field in fields: 83 name = field[0] 84 type = field[1] 85 size = field[2] 86 pk = field[3] 87 88 if type == 'auto': 89 type = 'serial' 90 91 if pk == 'u': 92 unique.append(name) 93 94 sql_fields.append(" %s %s" % (name, type)) 95 96 if len(unique): 97 sql_fields.append(" CONSTRAINT %s_pkey PRIMARY KEY(%s)" % 98 (table, ",".join(unique))) 99 sql += ",\n".join(sql_fields) 100 sql += "\n);\n\n" 101 102 cursor = cnx.cursor() 103 cursor.execute(sql) 104 cnx.commit() 105 106 cursor.close() 107 cnx.close() 108 109 def parse_args(self, dbargs): 110 """very crude code for parsing the arguments in connect_params""" 111 112 kargs = {} 113 args = dbargs.split(",") 114 115 for arg in args: 116 pair = arg.split("=") 117 name = pair[0] 118 value = pair[1].strip("'") 119 kargs[name] = value 120 121 return kargs 122 123 def get_search_fields(self): 124 return {'changeset' : 'cast(rev as text)', 125 'ticket' : 'cast(a.id as text)'} 126 127 -
trac/dbms/db_sqlite.py
old new 1 # -*- coding: iso8859-1 -*- 2 # 3 # Copyright (C) 2005 Edgewall Software 4 # Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de> 5 # 6 # Trac is free software; you can redistribute it and/or 7 # modify it under the terms of the GNU General Public License as 8 # published by the Free Software Foundation; either version 2 of the 9 # License, or (at your option) any later version. 10 # 11 # Trac is distributed in the hope that it will be useful, 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 # General Public License for more details. 15 # 16 # You should have received a copy of the GNU General Public License 17 # along with this program; if not, write to the Free Software 18 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 # 20 # Author: Christopher Lenz <cmlenz@gmx.de> 21 22 import os 23 import sqlite 24 import string 25 import traceback 26 27 from trac import db_default 28 29 from trac.db import ConnectionWrapper 30 31 from trac.util import TracError 32 33 class SQLiteConnection(ConnectionWrapper): 34 """ 35 Connection wrapper for SQLite. 36 """ 37 38 __slots__ = ['cnx'] 39 40 def __init__(self, dbpath, env, create=0, timeout=10000): 41 42 if create: 43 self.create_db(env.path, dbpath) 44 45 if dbpath != ':memory:': 46 if not os.access(dbpath, os.F_OK): 47 raise TracError, traceback.print_exc() 48 raise TracError, 'Database "%s" not found.' % dbpath 49 50 dbdir = os.path.dirname(dbpath) 51 if not os.access(dbpath, os.R_OK + os.W_OK) or \ 52 not os.access(dbdir, os.R_OK + os.W_OK): 53 raise TracError, 'The web server user requires read _and_ ' \ 54 'write permission to the database %s and ' \ 55 'the directory this file is located in.' \ 56 % dbpath 57 58 import sqlite 59 cnx = sqlite.connect(dbpath, timeout=timeout) 60 ConnectionWrapper.__init__(self, cnx) 61 62 def get_last_id(self, table, field): 63 return self.cnx.db.sqlite_last_insert_rowid() 64 65 def create_db(self, path, dbpath): 66 67 # make the directory to hold the database 68 os.mkdir(os.path.join(path, 'db')) 69 70 # create the database by getting a connection 71 cnx = sqlite.connect(dbpath, timeout=100) 72 73 # populate the default schema 74 schema = db_default.schema 75 sql = "" 76 77 for tables in schema: 78 table = tables[0] 79 fields = tables[1] 80 sql_fields = [] 81 unique = [] 82 sql += "CREATE TABLE %s (\n" % table 83 84 for field in fields: 85 name = field[0] 86 type = field[1] 87 size = field[2] 88 pk = field[3] 89 90 if type == 'auto': 91 type = 'INTEGER PRIMARY KEY' 92 93 if pk == 'u': 94 unique.append(name) 95 96 sql_fields.append(" %s %s" % (name, type)) 97 98 if len(unique): 99 sql_fields.append(" UNIQUE(%s)" % ",".join(unique)) 100 sql += ",\n".join(sql_fields) 101 sql += "\n);\n\n" 102 103 cursor = cnx.cursor() 104 cursor.execute(sql) 105 cnx.commit() 106 107 cursor.close() 108 cnx.close() 109 110 def get_search_fields(self): 111 return {'changeset' : 'rev', 112 'ticket' : 'a.id'} 113 -
trac/Search.py
old new 135 135 'Search Error') 136 136 137 137 cursor = self.db.cursor () 138 138 search_fields = self.db.get_search_fields() 139 139 140 q = [] 140 141 if changeset: 141 142 q.append("SELECT 1 as type, message AS title, message, author, " 142 "'' AS keywords, revAS data, time,0 AS ver "143 "'' AS keywords, %s AS data, time,0 AS ver " 143 144 "FROM revision WHERE %s OR %s" % 144 (self.query_to_sql(query, 'message'), 145 (search_fields['changeset'], 146 self.query_to_sql(query, 'message'), 145 147 self.query_to_sql(query, 'author'))) 146 148 if tickets: 147 149 q.append("SELECT DISTINCT 2 as type, a.summary AS title, " 148 150 "a.description AS message, a.reporter AS author, " 149 "a.keywords as keywords, a.idAS data, a.time as time, 0 AS ver "151 "a.keywords as keywords, %s AS data, a.time as time, 0 AS ver " 150 152 "FROM ticket a LEFT JOIN ticket_change b ON a.id = b.ticket " 151 153 "WHERE (b.field='comment' AND %s ) OR " 152 154 "%s OR %s OR %s OR %s OR %s" % 153 (self.query_to_sql(query, 'b.newvalue'), 155 (search_fields['ticket'], 156 self.query_to_sql(query, 'b.newvalue'), 154 157 self.query_to_sql(query, 'summary'), 155 158 self.query_to_sql(query, 'keywords'), 156 159 self.query_to_sql(query, 'description'), -
trac/Query.py
old new 154 154 sql.append("\nFROM ticket") 155 155 for k in [k for k in cols if k in custom_fields]: 156 156 sql.append("\n LEFT OUTER JOIN ticket_custom AS %s ON " \ 157 "(id=%s.ticket AND %s.name='%s') " % (k, k, k, k))157 "(id=%s.ticket AND %s.name='%s') tc" % (k, k, k, k)) 158 158 159 159 for col in [c for c in ['status', 'resolution', 'priority', 'severity'] 160 160 if c == self.order or c == self.group]: 161 161 sql.append("\n LEFT OUTER JOIN (SELECT name AS %s_name, " \ 162 162 "value AS %s_value " \ 163 "FROM enum WHERE type='%s') " \163 "FROM enum WHERE type='%s') e" \ 164 164 " ON %s_name=%s" % (col, col, col, col, col)) 165 165 for col in [c for c in ['milestone', 'version'] 166 166 if c == self.order or c == self.group]: 167 167 time_col = col == 'milestone' and 'due' or 'time' 168 168 sql.append("\n LEFT OUTER JOIN (SELECT name AS %s_name, " \ 169 "%s AS %s_time FROM %s) " \169 "%s AS %s_time FROM %s) a" \ 170 170 " ON %s_name=%s" % (col, time_col, col, col, col, col)) 171 171 172 172 def get_constraint_sql(name, value, mode, neg): -
trac/Report.py
old new 112 112 cursor = self.db.cursor() 113 113 cursor.execute("INSERT INTO report (title,sql,description) " 114 114 "VALUES (%s,%s,%s)", (title, sql, description)) 115 id = self.db.get_last_id( )115 id = self.db.get_last_id("report", "id") 116 116 self.db.commit() 117 117 req.redirect(self.env.href.report(id)) 118 118 -
trac/tests/tracadmin-tests.txt
old new 1 1 ===== test_help_ok ===== 2 trac-admin - The Trac Administration Console %(version)s2 trac-admin - The Trac Administration Console 0.8 3 3 4 4 Usage: trac-admin </path/to/projenv> [command [subcommand] [option ...]] 5 5 6 6 Invoking trac-admin without command starts interactive mode. 7 7 8 about -- Shows information about trac-admin 9 help -- Show documentation 10 initenv -- Create and initialize a new environment interactively 11 initenv <projectname> <repospath> <templatepath> -- Create and initialize a new environment from arguments 12 hotcopy <backupdir> -- Make a hot backup copy of an environment 13 resync -- Re-synchronize trac with the repository 14 upgrade -- Upgrade database to current version 15 wiki list -- List wiki pages 16 wiki remove <name> -- Remove wiki page 17 wiki export <page> [file] -- Export wiki page to file or stdout 18 wiki import <page> [file] -- Import wiki page from file or stdin 19 wiki dump <directory> -- Export all wiki pages to files named by title 20 wiki load <directory> -- Import all wiki pages from directory 21 wiki upgrade -- Upgrade default wiki pages to current version 22 permission list -- List permission rules 23 permission add <user> <action> [action] [...] -- Add a new permission rule 24 permission remove <user> <action> [action] [...] -- Remove permission rule 25 component list -- Show available components 26 component add <name> <owner> -- Add a new component 27 component rename <name> <newname> -- Rename a component 28 component remove <name> -- Remove/uninstall component 29 component chown <name> <owner> -- Change component ownership 30 priority list -- Show possible ticket priorities 31 priority add <value> -- Add a priority value option 32 priority change <value> <newvalue> -- Change a priority value 33 priority remove <value> -- Remove priority value 34 severity list -- Show possible ticket severities 35 severity add <value> -- Add a severity value option 36 severity change <value> <newvalue> -- Change a severity value 37 severity remove <value> -- Remove severity value 38 version list -- Show versions 39 version add <name> [time] -- Add version 40 version rename <name> <newname> -- Rename version 41 version time <name> <time> -- Set version date (Format: "%(date_format_hint)s" or "now") 42 version remove <name> -- Remove version 43 milestone list -- Show milestones 44 milestone add <name> [due] -- Add milestone 45 milestone rename <name> <newname> -- Rename milestone 46 milestone due <name> <due> -- Set milestone due date (Format: "%(date_format_hint)s" or "now") 47 milestone completed <name> <completed> -- Set milestone completed date (Format: "%(date_format_hint)s" or "now") 48 milestone remove <name> -- Remove milestone 8 about -- Shows information about trac-admin 9 help -- Show documentation 10 initenv -- Create and initialize a new environment interactively 11 initenv <projectname> <repospath> <templatepath> <dbms> [dbms options] -- Create and initialize a new environment from arguments 12 hotcopy <backupdir> -- Make a hot backup copy of an environment 13 resync -- Re-synchronize trac with the repository 14 upgrade -- Upgrade database to current version 15 wiki list -- List wiki pages 16 wiki export <page> [file] -- Export wiki page to file or stdout 17 wiki import <page> [file] -- Import wiki page from file or stdin 18 wiki dump <directory> -- Export all wiki pages to files named by title 19 wiki load <directory> -- Import all wiki pages from directory 20 wiki upgrade -- Upgrade default wiki pages to current version 21 permission list -- List permission rules 22 permission add <user> <action> [action] [...] -- Add a new permission rule 23 permission remove <user> <action> [action] [...] -- Remove permission rule 24 component list -- Show available components 25 component add <name> <owner> -- Add a new component 26 component rename <name> <newname> -- Rename a component 27 component remove <name> -- Remove/uninstall component 28 component chown <name> <owner> -- Change component ownership 29 priority list -- Show possible ticket priorities 30 priority add <value> -- Add a priority value option 31 priority change <value> <newvalue> -- Change a priority value 32 priority remove <value> -- Remove priority value 33 severity list -- Show possible ticket priorities 34 severity add <value> -- Add a severity value option 35 severity change <value> <newvalue> -- Change a severity value 36 severity remove <value> -- Remove severity value 37 version list -- Show versions 38 version add <name> [time] -- Add version 39 version rename <name> <newname> -- Rename version 40 version time <name> <time> -- Set version date (Format: "MM/DD/YY" or "now") 41 version remove <name> -- Remove version 42 milestone list -- Show milestones 43 milestone add <name> [due] -- Add milestone 44 milestone rename <name> <newname> -- Rename milestone 45 milestone due <name> <due> -- Set milestone due date (Format: "MM/DD/YY" or "now") 46 milestone completed <name> <completed> -- Set milestone completed date (Format: "MM/DD/YY" or "now") 47 milestone remove <name> -- Remove milestone 49 48 50 49 Visit the Trac Project at http://trac.edgewall.com/ 51 50 -
trac/tests/environment.py
old new 39 39 40 40 def test_config(self): 41 41 """Testing env.get/set_config""" 42 assert self.env.get_config('trac', 'database') == 'sqlite:db/trac.db' 42 # Any way to do this with multiple backends now supported? 43 # assert self.env.get_config('trac', 'database') == 'sqlite:db/trac.db' 43 44 self.env.set_config('foo', 'bar', 'baz') 44 45 self.env.save_config() 45 46 assert self.env.get_config('foo', 'bar') == 'baz' -
trac/tests/query.py
old new 45 45 self.assertEqual(sql, 46 46 """SELECT id,summary,status,owner,priority,milestone,component,time,changetime 47 47 FROM ticket 48 LEFT OUTER JOIN (SELECT name AS priority_name, value AS priority_value FROM enum WHERE type='priority') ON priority_name=priority48 LEFT OUTER JOIN (SELECT name AS priority_name, value AS priority_value FROM enum WHERE type='priority') e ON priority_name=priority 49 49 ORDER BY COALESCE(priority,'')='',priority_value,id""") 50 50 51 51 def test_all_ordered_by_priority_desc(self): … … 54 54 self.assertEqual(sql, 55 55 """SELECT id,summary,status,owner,priority,milestone,component,time,changetime 56 56 FROM ticket 57 LEFT OUTER JOIN (SELECT name AS priority_name, value AS priority_value FROM enum WHERE type='priority') ON priority_name=priority57 LEFT OUTER JOIN (SELECT name AS priority_name, value AS priority_value FROM enum WHERE type='priority') e ON priority_name=priority 58 58 ORDER BY COALESCE(priority,'')='' DESC,priority_value DESC,id""") 59 59 60 60 def test_all_ordered_by_version(self): … … 63 63 self.assertEqual(sql, 64 64 """SELECT id,summary,status,owner,priority,milestone,version,time,changetime 65 65 FROM ticket 66 LEFT OUTER JOIN (SELECT name AS version_name, time AS version_time FROM version) ON version_name=version66 LEFT OUTER JOIN (SELECT name AS version_name, time AS version_time FROM version) a ON version_name=version 67 67 ORDER BY COALESCE(version,'')='',COALESCE(version_time,0)=0,version_time,version,id""") 68 68 69 69 def test_all_ordered_by_version_desc(self): … … 72 72 self.assertEqual(sql, 73 73 """SELECT id,summary,status,owner,priority,milestone,version,time,changetime 74 74 FROM ticket 75 LEFT OUTER JOIN (SELECT name AS version_name, time AS version_time FROM version) ON version_name=version75 LEFT OUTER JOIN (SELECT name AS version_name, time AS version_time FROM version) a ON version_name=version 76 76 ORDER BY COALESCE(version,'')='' DESC,COALESCE(version_time,0)=0 DESC,version_time DESC,version DESC,id""") 77 77 78 78 def test_constrained_by_milestone(self): … … 91 91 self.assertEqual(sql, 92 92 """SELECT id,summary,status,owner,priority,component,version,milestone,time,changetime 93 93 FROM ticket 94 LEFT OUTER JOIN (SELECT name AS milestone_name, due AS milestone_time FROM milestone) ON milestone_name=milestone94 LEFT OUTER JOIN (SELECT name AS milestone_name, due AS milestone_time FROM milestone) a ON milestone_name=milestone 95 95 ORDER BY COALESCE(milestone,'')='',COALESCE(milestone_time,0)=0,milestone_time,milestone,COALESCE(id,0)=0,id""") 96 96 97 97 def test_all_grouped_by_milestone_desc(self): … … 100 100 self.assertEqual(sql, 101 101 """SELECT id,summary,status,owner,priority,component,version,milestone,time,changetime 102 102 FROM ticket 103 LEFT OUTER JOIN (SELECT name AS milestone_name, due AS milestone_time FROM milestone) ON milestone_name=milestone103 LEFT OUTER JOIN (SELECT name AS milestone_name, due AS milestone_time FROM milestone) a ON milestone_name=milestone 104 104 ORDER BY COALESCE(milestone,'')='' DESC,COALESCE(milestone_time,0)=0 DESC,milestone_time DESC,milestone DESC,COALESCE(id,0)=0,id""") 105 105 106 106 def test_grouped_by_priority(self): … … 109 109 self.assertEqual(sql, 110 110 """SELECT id,summary,status,owner,milestone,component,version,priority,time,changetime 111 111 FROM ticket 112 LEFT OUTER JOIN (SELECT name AS priority_name, value AS priority_value FROM enum WHERE type='priority') ON priority_name=priority112 LEFT OUTER JOIN (SELECT name AS priority_name, value AS priority_value FROM enum WHERE type='priority') e ON priority_name=priority 113 113 ORDER BY COALESCE(priority,'')='',priority_value,id""") 114 114 115 115 def test_constrained_by_milestone_not(self): … … 180 180 self.assertEqual(sql, 181 181 """SELECT id,summary,status,owner,priority,milestone,component,time,changetime, foo.value AS foo 182 182 FROM ticket 183 LEFT OUTER JOIN ticket_custom AS foo ON (id=foo.ticket AND foo.name='foo') 183 LEFT OUTER JOIN ticket_custom AS foo ON (id=foo.ticket AND foo.name='foo') tc 184 184 WHERE COALESCE(foo,'')='something' 185 185 ORDER BY COALESCE(id,0)=0,id""") 186 186 -
trac/tests/db.py
old new 1 2 import unittest 3 from trac.Environment import Environment # TODO: take out trac. when being run 4 # from normal trac/test.py 5 6 class DBTestBase: 7 def setUp(self): 8 pass 9 10 def tearDown(self): 11 pass 12 13 class DBTestCase(DBTestBase, unittest.TestCase): 14 def test_connect(self): 15 """Testing db.connect""" 16 env = Environment('/var/trac/test') # TODO: make more generic 17 # for now, it's my local pgsql env 18 cnx = env.get_db_cnx() 19 cursor = cnx.cursor() 20 cursor.execute("select * from system") 21 row = cursor.fetchone() 22 print row 23 cnx.close() 24 25 def test_parse_args(self): 26 """Testing db.parse_args""" 27 28 def suite(): 29 return unittest.makeSuite(DBTestCase,'test') 30 31 # TODO: take this out when we put this in with the other tests 32 # Q: will we be able to do these tests for different dbms's in the test suite? 33 if __name__ == '__main__': 34 unittest.main(defaultTest='suite') 35 -
trac/db.py
old new 81 81 return IterableCursor(cursor) 82 82 83 83 84 class SQLiteConnection(ConnectionWrapper):85 """86 Connection wrapper for SQLite.87 """88 84 89 __slots__ = ['cnx'] 90 91 def __init__(self, dbpath, timeout=10000): 92 if dbpath != ':memory:': 93 if not os.access(dbpath, os.F_OK): 94 raise TracError, 'Database "%s" not found.' % dbpath 95 96 dbdir = os.path.dirname(dbpath) 97 if not os.access(dbpath, os.R_OK + os.W_OK) or \ 98 not os.access(dbdir, os.R_OK + os.W_OK): 99 raise TracError, 'The web server user requires read _and_ ' \ 100 'write permission to the database %s and ' \ 101 'the directory this file is located in.' \ 102 % dbpath 103 104 import sqlite 105 cnx = sqlite.connect(dbpath, timeout=timeout) 106 ConnectionWrapper.__init__(self, cnx) 107 108 def get_last_id(self): 109 return self.cnx.db.sqlite_last_insert_rowid() 110 111 112 def get_cnx(env): 85 def get_cnx(env, create=0): 113 86 db_str = env.get_config('trac', 'database', 'sqlite:db/trac.db') 114 87 scheme, rest = db_str.split(':', 1) 115 88 116 89 if scheme == 'sqlite': 117 90 if not rest.startswith('/'): 118 91 rest = os.path.join(env.path, rest) 119 return SQLiteConnection(rest) 92 import trac.dbms.db_sqlite 93 return trac.dbms.db_sqlite.SQLiteConnection(rest, env, create) 120 94 95 if scheme == 'pypgsql': 96 import trac.dbms.db_pypgsql 97 return trac.dbms.db_pypgsql.PyPgSQLConnection(rest, env, create) 98 121 99 raise TracError, 'Unsupported database type "%s"' % scheme 122 100 123 101 -
trac/Environment.py
old new 19 19 # 20 20 # Author: Jonas Borgström <jonas@edgewall.com> 21 21 # 22 # Todo: Move backup and upgrade from db.py23 #24 22 25 23 from trac import db, db_default, Logging, Mimeview, util 24 from types import * 26 25 27 26 import ConfigParser 28 27 import os … … 41 40 A Trac environment consists of a directory structure containing 42 41 among other things: 43 42 * a configuration file. 44 * a sqlitedatabase (stores tickets, wiki pages...)43 * a database (stores tickets, wiki pages...) 45 44 * Project specific templates and wiki macros. 46 45 * wiki and ticket attachments. 47 46 """ 48 def __init__(self, path, create=0 ):47 def __init__(self, path, create=0, db_str='sqlite:db/trac.db'): 49 48 self.path = path 50 49 if create: 51 self.create( )50 self.create(db_str) 52 51 self.verify() 53 52 self.load_config() 54 53 try: # Use binary I/O on Windows … … 69 68 def get_db_cnx(self): 70 69 return db.get_cnx(self) 71 70 72 def create(self ):71 def create(self, db_str): 73 72 def _create_file(fname, data=None): 74 73 fd = open(fname, 'w') 75 74 if data: fd.write(data) … … 108 107 # Site CSS - Place custom CSS, including overriding styles here. 109 108 ?> 110 109 """) 111 # Create default database 112 import sqlite 113 os.mkdir(os.path.join(self.path, 'db')) 114 cnx = sqlite.connect(os.path.join(self.path, 'db', 'trac.db')) 115 cursor = cnx.cursor() 116 cursor.execute(db_default.schema) 117 cnx.commit() 110 111 # set up the config file 112 self.load_config() 113 self.setup_default_config() 114 self.set_config('trac', 'database', db_str) 115 self.save_config() 118 116 117 # Create default database by attempting to get connection 118 cnx = db.get_cnx(self, create=1) 119 cnx.close 120 119 121 def insert_default_data(self): 120 122 def prep_value(v): 121 123 if v == None: 122 124 return 'NULL' 123 125 else: 124 return '"%s"' % v 126 prepped = v 127 if type(v) is StringType: 128 prepped = util.sql_escape(prepped) 129 return "'%s'" % prepped 125 130 cnx = self.get_db_cnx() 126 131 cursor = cnx.cursor() 127 132 … … 131 136 for row in db_default.data[t][2]: 132 137 values = ','.join(map(prep_value, row)) 133 138 sql = "INSERT INTO %s (%s) VALUES(%s);" % (table, cols, values) 134 cursor.execute(sql) 139 cursor.execute(sql) 140 cnx.commit() 141 142 def setup_default_config(self): 135 143 for s,n,v in db_default.default_config: 136 144 if not self.cfg.has_section(s): 137 145 self.cfg.add_section(s) 138 146 self.cfg.set(s, n, v) 139 147 self.save_config() 140 cnx.commit()141 148 142 149 def get_version(self): 143 150 cnx = self.get_db_cnx() … … 295 302 """Simple SQLite-specific backup. Copy the database file.""" 296 303 db_str = self.get_config('trac', 'database', 'sqlite:db/trac.db') 297 304 if db_str[:7] != 'sqlite:': 298 raise EnvironmentError, 'Can only backup sqlite databases' 305 print 'BACKUP FAILED: Can only backup sqlite databases.' 306 # TODO: should probably ask if they want to continue 307 # or at least handle this better by shelling out to backup 308 # other dbs like pgsql... 309 return 299 310 db_name = os.path.join(self.path, db_str[7:]) 300 311 if not dest: 301 312 dest = '%s.%i.bak' % (db_name, self.get_version()) -
trac/Ticket.py
old new 107 107 % (','.join(std_fields), 108 108 ','.join(['%s'] * len(std_fields))), 109 109 map(lambda n, self=self: self[n], std_fields)) 110 id = db.get_last_id( )110 id = db.get_last_id("ticket", "id") 111 111 112 112 custom_fields = filter(lambda n: n[:7] == 'custom_', self.keys()) 113 113 for name in custom_fields: … … 151 151 cursor.execute("SELECT * FROM ticket_custom " 152 152 "WHERE ticket=%s and name=%s", (id, fname)) 153 153 if cursor.fetchone(): 154 # TODO: is this correct below? no where clause? 155 raise TracError, ("is this correct? - UPDATE ticket_custom SET value=%s", (self[name],)) 154 156 cursor.execute("UPDATE ticket_custom SET value=%s", 155 157 (self[name],)) 156 158 else: -
contrib/tracdb-sqlite2pg.py
old new 1 #!/usr/bin/env python 2 3 """ 4 Import a Trac database from sqlite to postgresql. 5 6 Requires: Trac from http://trac.edgewall.com/ 7 Python 2.3 from http://www.python.org/ 8 PostgreSQL from http://www.postgresql.org/ 9 PySQLITE ? 10 others? 11 12 Author: Brad Anderson <brad@dsource.org> 13 14 15 """ 16 17 import sys, os 18 import sqlite 19 import pgdb 20 from trac.util import sql_escape 21 22 tables=['revision','node_change', 'auth_cookie', 'enum', 'system', 'lock', 23 'ticket', 'ticket_change', 'ticket_custom', 'report', 'permission', 24 'component', 'milestone', 'version', 'attachment', 'session', 'wiki', 25 'forums', 'topics', 'posts'] 26 27 # for debugging, one table at a time 28 # tables=['wiki'] 29 30 31 ## simple field translation mapping. if string not in 32 ## mapping, just return string, otherwise return value 33 #class FieldTranslator(dict): 34 # def __getitem__(self, item): 35 # if not dict.has_key(self, item): 36 # return item 37 # 38 # return dict.__getitem__(self, item) 39 40 41 42 def convert(_env, _host, _db, _user, _password, _clean): 43 44 45 # init PostgreSQL environment 46 print "PostgreSQL('%s':'%s':'%s'): connecting..." \ 47 % (_host, _db, _user) 48 pg_con = pgdb.connect(host=_host, database=_db, 49 user=_user, password=_password) 50 pg_cur = pg_con.cursor() 51 52 # init Trac environment 53 print "Trac SQLite('%s'): connecting..." % (_env) 54 trac_con = sqlite.connect(os.path.join(_env, "db/trac.db"), 55 timeout=10000) 56 trac_cur = trac_con.cursor() 57 print 58 59 # fieldtypes = FieldTranslator() 60 # fieldtypes['0'] = 'int' 61 # fieldtypes['6'] = 'text' 62 63 # loop thru tables, converting them 64 for t in tables: 65 print 66 print "Converting Table: '%s'" % t 67 68 # clear out existing table 69 # if _clean == 1: 70 # pg_cur.execute("truncate table %s;" % t) 71 pg_cur.execute("truncate table %s;" % t) 72 73 # get table info 74 sql = "SELECT * FROM %s" % t 75 trac_cur.execute(sql) 76 cols = trac_cur.rs.col_defs 77 counter = 0 78 79 # loop thru rows, moving data from sqlite to pgsql 80 while 1: 81 row = trac_cur.fetchone() 82 counter += 1 83 if not row: 84 break 85 flds="" 86 vals="" 87 for c in cols: 88 field = c[0] 89 type = c[1] 90 value = row[field] 91 if value != None: 92 flds += ", %s" % field 93 if type in [6]: 94 value = "'%s'" % sql_escape(value) 95 vals += ", %s" % value 96 flds = flds[2:] 97 vals = vals[2:] 98 sql = "INSERT INTO %s " % t 99 sql += "(%s) " % flds 100 sql += "VALUES (%s);" % vals 101 102 pg_cur.execute(sql) 103 pg_con.commit() 104 105 if counter % 100 == 0: 106 print "%s rows" % counter 107 108 trac_cur.close() 109 trac_con.close() 110 pg_cur.close() 111 pg_con.close() 112 print 113 114 115 def usage(): 116 print "tracdb-sqlite2pg - Converts a Trac database from sqlite to " \ 117 "postgresql." 118 print 119 print "This script is designed to transfer an existing Trac SQLite " 120 print "database to an existing Trac PosgreSQL database. The two " 121 print "databases should be of the same version. Check in the 'system' " 122 print "table in each database to verify. This script runs on version 8 " 123 print "and above. This is for Trac pre 0.9 (revision 1169 and higher)." 124 print 125 print "Note: you can make a Trac PosgreSQL database with trac-admin " 126 print "(the version that accompanies this script)" 127 print 128 print "THIS WILL DESTROY ALL DATA IN THE POSTGRESQL DATABASE !! " 129 print 130 print 131 print "Usage: tracdb-sqlite2pg.py [options]" 132 print 133 print "Available Options:" 134 print " -t | --tracenv /path/to/trac/env - full path to Trac environment" 135 print " -h | --host <pgsql hostname> - PostgreSQL hostname" 136 print " -db | --database <pgsql database> - PostgreSQL database name" 137 print " -u | --user <pgsql username> - PostgreSQL username" 138 print " -p | --passwd <pgsql password> - PostgreSQL password" 139 # print " -c | --clean - clean out PG database " \ 140 # "before converting. (recommended)" 141 print " --help | help - this help info" 142 print 143 print "Additional configuration options can be defined directly in the " \ 144 "script." 145 print 146 sys.exit(0) 147 148 def main(): 149 global TRAC_ENV, PG_HOST, PG_DB, PG_USER, PG_PASSWORD, PG_CLEAN 150 if len (sys.argv) > 1: 151 if sys.argv[1] in ['--help','help'] or len(sys.argv) < 4: 152 usage() 153 iter = 1 154 while iter < len(sys.argv): 155 if sys.argv[iter] in ['-t', '--tracenv'] and iter+1 < len(sys.argv): 156 TRAC_ENV = sys.argv[iter+1] 157 iter = iter + 1 158 elif sys.argv[iter] in ['-h', '--host'] and iter+1 < len(sys.argv): 159 PG_HOST = sys.argv[iter+1] 160 iter = iter + 1 161 elif sys.argv[iter] in ['-db', '--database'] and \ 162 iter+1 < len(sys.argv): 163 PG_DB = sys.argv[iter+1] 164 iter = iter + 1 165 elif sys.argv[iter] in ['-u', '--user'] and iter+1 < len(sys.argv): 166 PG_USER = sys.argv[iter+1] 167 iter = iter + 1 168 elif sys.argv[iter] in ['-p', '--passwd'] and \ 169 iter+1 < len(sys.argv): 170 PG_PASSWORD = sys.argv[iter+1] 171 iter = iter + 1 172 # elif sys.argv[iter] in ['-c', '--clean']: 173 # PG_CLEAN = 1 174 else: 175 print "Error: unknown parameter: " + sys.argv[iter] 176 sys.exit(0) 177 iter = iter + 1 178 else: 179 usage() 180 181 convert(TRAC_ENV, PG_HOST, PG_DB, PG_USER, PG_PASSWORD, PG_CLEAN) 182 183 184 if __name__ == '__main__': 185 main()
