diff --git a/trac/db/api.py b/trac/db/api.py
|
a
|
b
|
|
| 36 | 36 | |
| 37 | 37 | def get_supported_schemes(): |
| 38 | 38 | """Return the connection URL schemes supported by the connector, and |
| 39 | | their relative priorities as an iterable of `(scheme, priority)` tuples. |
| | 39 | their relative priorities as an iterable of `(scheme, priority)` |
| | 40 | tuples. |
| | 41 | |
| | 42 | If `priority` is a negative number, this is indicative of an |
| | 43 | error condition with the connector. An error message should be |
| | 44 | attached to the `error` attribute of the connector. |
| 40 | 45 | """ |
| 41 | 46 | |
| 42 | 47 | def get_connection(path, log=None, **kwargs): |
| … |
… |
|
| 116 | 121 | |
| 117 | 122 | def _get_connector(self): ### FIXME: Make it public? |
| 118 | 123 | scheme, args = _parse_db_str(self.connection_uri) |
| 119 | | candidates = {} |
| 120 | | for connector in self.connectors: |
| 121 | | for scheme_, priority in connector.get_supported_schemes(): |
| 122 | | if scheme_ != scheme: |
| 123 | | continue |
| 124 | | highest = candidates.get(scheme_, (None, 0))[1] |
| 125 | | if priority > highest: |
| 126 | | candidates[scheme] = (connector, priority) |
| 127 | | |
| 128 | | connector = candidates.get(scheme, [None])[0] |
| 129 | | if not connector: |
| | 124 | candidates = [ |
| | 125 | (priority, connector) |
| | 126 | for connector in self.connectors |
| | 127 | for scheme_, priority in connector.get_supported_schemes() |
| | 128 | if scheme_ == scheme |
| | 129 | ] |
| | 130 | if not candidates: |
| 130 | 131 | raise TracError('Unsupported database type "%s"' % scheme) |
| | 132 | priority, connector = max(candidates) |
| | 133 | if priority < 0: |
| | 134 | raise TracError(connector.error) |
| 131 | 135 | |
| 132 | 136 | if scheme == 'sqlite': |
| 133 | 137 | # Special case for SQLite to support a path relative to the |
diff --git a/trac/db/mysql_backend.py b/trac/db/mysql_backend.py
|
a
|
b
|
|
| 23 | 23 | from trac.util import get_pkginfo |
| 24 | 24 | from trac.util.compat import close_fds |
| 25 | 25 | from trac.util.text import to_unicode |
| | 26 | from trac.util.translation import _ |
| 26 | 27 | |
| 27 | 28 | _like_escape_re = re.compile(r'([/_%])') |
| 28 | 29 | |
| … |
… |
|
| 64 | 65 | |
| 65 | 66 | def __init__(self): |
| 66 | 67 | self._version = None |
| | 68 | self.error = None |
| 67 | 69 | |
| 68 | 70 | def get_supported_schemes(self): |
| 69 | | global has_mysqldb |
| 70 | | if has_mysqldb: |
| 71 | | return [('mysql', 1)] |
| 72 | | else: |
| 73 | | return [] |
| | 71 | if not has_mysqldb: |
| | 72 | self.error = _("Cannot load Python bindings for MySQL") |
| | 73 | yield ('mysql', self.error and -1 or 1) |
| 74 | 74 | |
| 75 | 75 | def get_connection(self, path, log=None, user=None, password=None, |
| 76 | 76 | host=None, port=None, params={}): |
diff --git a/trac/db/postgres_backend.py b/trac/db/postgres_backend.py
|
a
|
b
|
|
| 23 | 23 | from trac.util import get_pkginfo |
| 24 | 24 | from trac.util.compat import close_fds |
| 25 | 25 | from trac.util.text import to_unicode |
| | 26 | from trac.util.translation import _ |
| 26 | 27 | |
| 27 | | psycopg = None |
| 28 | | PgSQL = None |
| | 28 | has_psycopg = False |
| | 29 | has_pgsql = False |
| 29 | 30 | PGSchemaError = None |
| | 31 | try: |
| | 32 | import psycopg2 as psycopg |
| | 33 | import psycopg2.extensions |
| | 34 | from psycopg2 import ProgrammingError as PGSchemaError |
| | 35 | psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) |
| | 36 | has_psycopg = True |
| | 37 | except ImportError: |
| | 38 | try: |
| | 39 | from pyPgSQL import PgSQL |
| | 40 | from pyPgSQL.libpq import OperationalError as PGSchemaError |
| | 41 | has_pgsql = True |
| | 42 | except ImportError: |
| | 43 | pass |
| | 44 | |
| 30 | 45 | |
| 31 | 46 | _like_escape_re = re.compile(r'([/_%])') |
| 32 | 47 | |
| … |
… |
|
| 41 | 56 | |
| 42 | 57 | def __init__(self): |
| 43 | 58 | self._version = None |
| | 59 | self.error = None |
| 44 | 60 | |
| 45 | 61 | def get_supported_schemes(self): |
| 46 | | return [('postgres', 1)] |
| | 62 | if not (has_psycopg or has_pgsql): |
| | 63 | self.error = _("Cannot load Python bindings for PostgreSQL") |
| | 64 | yield ('postgres', self.error and -1 or 1) |
| 47 | 65 | |
| 48 | 66 | def get_connection(self, path, log=None, user=None, password=None, |
| 49 | 67 | host=None, port=None, params={}): |
| 50 | | global psycopg |
| 51 | | global PgSQL |
| 52 | 68 | cnx = PostgreSQLConnection(path, log, user, password, host, port, |
| 53 | 69 | params) |
| 54 | 70 | if not self._version: |
| 55 | | if psycopg: |
| | 71 | if has_psycopg: |
| 56 | 72 | self._version = get_pkginfo(psycopg).get('version', |
| 57 | 73 | psycopg.__version__) |
| 58 | 74 | name = 'psycopg2' |
| 59 | | elif PgSQL: |
| | 75 | elif has_pgsql: |
| 60 | 76 | import pyPgSQL |
| 61 | 77 | self._version = get_pkginfo(pyPgSQL).get('version', |
| 62 | 78 | pyPgSQL.__version__) |
| … |
… |
|
| 152 | 168 | port=None, params={}): |
| 153 | 169 | if path.startswith('/'): |
| 154 | 170 | path = path[1:] |
| 155 | | # We support both psycopg and PgSQL but prefer psycopg |
| 156 | | global psycopg |
| 157 | | global PgSQL |
| 158 | | global PGSchemaError |
| 159 | | |
| 160 | | if not psycopg and not PgSQL: |
| 161 | | try: |
| 162 | | import psycopg2 as psycopg |
| 163 | | import psycopg2.extensions |
| 164 | | from psycopg2 import ProgrammingError as PGSchemaError |
| 165 | | psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) |
| 166 | | except ImportError: |
| 167 | | from pyPgSQL import PgSQL |
| 168 | | from pyPgSQL.libpq import OperationalError as PGSchemaError |
| 169 | 171 | if 'host' in params: |
| 170 | 172 | host = params['host'] |
| 171 | | if psycopg: |
| | 173 | if has_psycopg: |
| 172 | 174 | dsn = [] |
| 173 | 175 | if path: |
| 174 | 176 | dsn.append('dbname=' + path) |
| … |
… |
|
| 182 | 184 | dsn.append('port=' + str(port)) |
| 183 | 185 | cnx = psycopg.connect(' '.join(dsn)) |
| 184 | 186 | cnx.set_client_encoding('UNICODE') |
| 185 | | else: |
| | 187 | elif has_pgsql: |
| 186 | 188 | # Don't use chatty, inefficient server-side cursors. |
| 187 | 189 | # http://pypgsql.sourceforge.net/pypgsql-faq.html#id2787367 |
| 188 | 190 | PgSQL.fetchReturnsList = 1 |
diff --git a/trac/db/sqlite_backend.py b/trac/db/sqlite_backend.py
|
a
|
b
|
|
| 22 | 22 | from trac.db.api import IDatabaseConnector |
| 23 | 23 | from trac.db.util import ConnectionWrapper |
| 24 | 24 | from trac.util import get_pkginfo, getuser |
| | 25 | from trac.util.translation import _ |
| 25 | 26 | |
| 26 | 27 | _like_escape_re = re.compile(r'([/_%])') |
| 27 | 28 | |
| … |
… |
|
| 112 | 113 | |
| 113 | 114 | def __init__(self): |
| 114 | 115 | self._version = None |
| | 116 | self.error = None |
| 115 | 117 | |
| 116 | 118 | def get_supported_schemes(self): |
| 117 | | return [('sqlite', 1)] |
| | 119 | if not have_pysqlite: |
| | 120 | self.error = _("Cannot load Python bindings for SQLite") |
| | 121 | elif sqlite.sqlite_version_info >= (3, 3, 3): |
| | 122 | if sqlite.version_info < (1, 0, 7): |
| | 123 | self.error = _("Need at least PySqlite 1.0.7 or higher") |
| | 124 | elif sqlite.version_info[0] == 2 and \ |
| | 125 | sqlite.version_info < (2, 0, 7): |
| | 126 | self.error = _("Need at least PySqlite 2.0.7 or higher") |
| | 127 | yield ('sqlite', self.error and -1 or 1) |
| 118 | 128 | |
| 119 | 129 | def get_connection(self, path, log=None, params={}): |
| 120 | 130 | if not self._version: |
| 121 | | global sqlite_version_string, have_pysqlite |
| 122 | 131 | self._version = get_pkginfo(sqlite).get( |
| 123 | 132 | 'version', '%d.%d.%s' % sqlite.version_info) |
| 124 | 133 | self.env.systeminfo.extend([('SQLite', sqlite_version_string), |