Ticket #2304: 2304-proposed-changes-r8038.patch
| File 2304-proposed-changes-r8038.patch, 7.5 KB (added by rblank, 3 years ago) |
|---|
-
trac/db/mysql_backend.py
diff --git a/trac/db/mysql_backend.py b/trac/db/mysql_backend.py
a b 15 15 # history and logs, available at http://trac.edgewall.org/log/. 16 16 17 17 import re, sys, os, time 18 from subprocess import call 18 19 19 20 from trac.core import * 20 21 from trac.config import Option 21 22 from trac.db.api import IDatabaseConnector, _parse_db_str 22 23 from trac.db.util import ConnectionWrapper 23 from trac.util import get_pkginfo 24 from trac.util.translation import _ 25 from subprocess import Popen, PIPE 24 from trac.util import Null, get_pkginfo 26 25 from trac.util.compat import close_fds 27 26 28 27 _like_escape_re = re.compile(r'([/_%])') … … 59 58 60 59 implements(IDatabaseConnector) 61 60 62 dump_bin = Option('trac', 'mysqldump_bin', 'mysqldump',61 mysqldump_path = Option('trac', 'mysqldump_path', 'mysqldump', 63 62 """Location of mysqldump for MySQL database backups""") 64 63 65 64 def __init__(self): … … 148 147 '_'.join(index.columns), table.name, 149 148 self._collist(table, index.columns)) 150 149 151 152 150 def backup(self, dest_file): 153 # msyqldump -n schemaname dbname | gzip > filename.gz154 151 db_url = self.env.config.get('trac', 'database') 155 152 scheme, db_prop = _parse_db_str(db_url) 156 153 db_name = os.path.basename(db_prop['path']) 157 args = [self. dump_bin,154 args = [self.mysqldump_path, 158 155 '-u%s' % db_prop['user'], 159 156 '-h%s' % db_prop['host']] 160 157 if db_prop['port']: 161 158 args.append('-P%s' % str(db_prop['port'])) 162 159 args.append(db_name) 163 160 164 args.extend(['>', dest_file])165 if sys.platform == 'win':166 # XXX TODO verify on windows167 args = ['cmd', '/c', ' '.join(args)]168 else:169 args = ['bash', '-c', ' '.join(args)]170 171 161 environ = os.environ.copy() 172 162 environ['MYSQL_PWD'] = db_prop['password'] 173 #print >> sys.stderr, "backup command %r" % (args,) 174 #print >> sys.stderr, "backup props %r" % (db_prop,) 175 #print >> sys.stderr, "backup to %s" % dest_file 176 p = Popen(args, env=environ, shell=False, bufsize=0, stdin=None, 177 stdout=PIPE, stderr=PIPE, close_fds=close_fds) 178 err = p.wait() 179 if err: 180 raise TracError("Backup attempt exited with error code %s." % err) 181 p.stdout.close() 182 p.stderr.close() 183 if not os.path.exists(dest_file): 184 raise TracError("Backup attempt failed") 163 out = open(dest_file, "wb") 164 try: 165 ret = call(args, env=environ, shell=False, bufsize=0, stdin=None, 166 stdout=out, stderr=Null(), close_fds=close_fds) 167 finally: 168 out.close() 169 if ret != 0: 170 raise TracError("Backup attempt failed " 171 "(mysqldump return code: %d)" % ret) 185 172 return dest_file 186 173 187 174 class MySQLConnection(ConnectionWrapper): -
trac/db/postgres_backend.py
diff --git a/trac/db/postgres_backend.py b/trac/db/postgres_backend.py
a b 15 15 # Author: Christopher Lenz <cmlenz@gmx.de> 16 16 17 17 import re, sys, os, time 18 from subprocess import call 18 19 19 20 from trac.core import * 20 21 from trac.config import Option 21 22 from trac.db.api import IDatabaseConnector, _parse_db_str 22 23 from trac.db.util import ConnectionWrapper 23 from trac.util import get_pkginfo 24 from subprocess import Popen, PIPE 24 from trac.util import Null, get_pkginfo 25 25 from trac.util.compat import close_fds 26 26 27 27 psycopg = None … … 36 36 37 37 implements(IDatabaseConnector) 38 38 39 pg_dump_ bin = Option('trac', 'pg_dump_bin', 'pg_dump',39 pg_dump_path = Option('trac', 'pg_dump_path', 'pg_dump', 40 40 """Location of pg_dump for Postgres database backups""") 41 compression = Option('trac', 'backup_compression', '8',42 """Gzip backup compression level (if supported by backend)""")43 41 44 42 def __init__(self): 45 43 self._version = None … … 102 100 '_'.join(index.columns), table.name, '","'.join(index.columns)) 103 101 104 102 def backup(self, dest_file): 105 # pg_dump -n schemaname dbname | gzip > filename.gz106 103 db_url = self.env.config.get('trac', 'database') 107 104 scheme, db_prop = _parse_db_str(db_url) 108 105 db_name = os.path.basename(db_prop['path']) 109 args = [self.pg_dump_ bin, '-C', '-d', '-x', '-Z', self.compression,106 args = [self.pg_dump_path, '-C', '-d', '-x', '-Z', '8', 110 107 '-U', db_prop['user'],] 111 108 port = db_prop.get('port', '5432') 112 109 if 'host' in db_prop['params']: … … 124 121 else: 125 122 args.extend([db_name]) 126 123 127 dest_file = "%s.gz" % (dest_file,) 128 args.extend(['>', dest_file]) 129 if sys.platform == 'win': 130 # XXX TODO verify on windows 131 args = ['cmd', '/c', ' '.join(args)] 132 else: 133 args = ['bash', '-c', ' '.join(args)] 134 124 dest_file += ".gz" 135 125 environ = os.environ.copy() 136 126 if 'password' in db_prop: 137 127 environ['PGPASSWORD'] = db_prop['password'] 138 p = Popen(args, env=environ, shell=False, bufsize=0, stdin=None,139 stdout=PIPE, stderr=PIPE, close_fds=close_fds)140 err = p.wait()141 if err:142 raise TracError("Backup attempt exited with error code %s." % err)143 p.stdout.close()144 p.stderr.close()145 if not os.path.exists(dest_file):146 raise TracError("Backup attempt failed")128 out = open(dest_file, "wb") 129 try: 130 ret = call(args, env=environ, shell=False, bufsize=0, stdin=None, 131 stdout=out, stderr=Null(), close_fds=close_fds) 132 finally: 133 out.close() 134 if ret != 0: 135 raise TracError("Backup attempt failed " 136 "(pg_dump return code: %d)" % ret) 147 137 return dest_file 148 138 149 139 -
trac/util/__init__.py
diff --git a/trac/util/__init__.py b/trac/util/__init__.py
a b 542 542 else: 543 543 return 0 544 544 545 546 class Null(object): 547 """A class that does nothing, but does it well""" 548 def __new__(cls, *args, **kwargs): 549 try: 550 return cls._inst 551 except AttributeError: 552 cls._inst = object.__new__(cls, *args, **kwargs) 553 return cls._inst 554 555 def __init__(self, *args, **kwargs): 556 pass 557 558 def __call__(self, *args, **kwargs): 559 return self 560 561 def __repr__(self): 562 return 'Null()' 563 564 def __str__(self): 565 return '' 566 567 def __eq__(self, other): 568 return self is other 569 570 def __hash__(self): 571 return hash(None) 572 573 def __len__(self): 574 return 0 575 576 def __iter__(self): 577 return iter(()) 578 579 def __contains__(self, item): 580 return False 581 582 __getattr__ = __setattr__ = __delattr__ \ 583 = __getitem__ = __setitem__ = __delitem__ \ 584 = __add__ = __sub__ = __mul__ = __div__ = __truediv__ \ 585 = __floordiv__ = __mod__ = __divmod__ = __pow__ \ 586 = __lshift__ = __rshift__ = __and__ = __xor__ = __or__ \ 587 = __radd__ = __rsub__ = __rmul__ = __rdiv__ = __rtruediv__ \ 588 = __rfloordiv__ = __rmod__ = __rdivmod__ = __rpow__ \ 589 = __rlshift__ = __rrshift__ = __rand__ = __rxor__ = __ror__ \ 590 = __call__ 591 592 545 593 def content_disposition(type, filename=None): 546 594 """Generate a properly escaped Content-Disposition header""" 547 595 if isinstance(filename, unicode):
