Fixed #11065, #11285 -- Streamlined PostgreSQL version detection, fixing incompatibility with multi-db. Thanks findepi for the report.
Changed our internal representation of the PostgreSQL version from tuples to integers as used by libpq and psycopg2. This simplifies version comparison operations. Also, using the associated libpq/psycopg2 API allows to remove the need for manually issuing in-band ``SELECT version()`` SQL queries to obtain the server version (or at least reduce its number if version of psycopg2 in use is older than 2.0.12). Refs #10509. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16439 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
656360c240
commit
f2dca72afd
|
@ -105,6 +105,13 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
self.creation = DatabaseCreation(self)
|
||||
self.introspection = DatabaseIntrospection(self)
|
||||
self.validation = BaseDatabaseValidation(self)
|
||||
self._pg_version = None
|
||||
|
||||
def _get_pg_version(self):
|
||||
if self._pg_version is None:
|
||||
self._pg_version = get_version(self.connection)
|
||||
return self._pg_version
|
||||
pg_version = property(_get_pg_version)
|
||||
|
||||
def _cursor(self):
|
||||
new_connection = False
|
||||
|
@ -139,8 +146,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
if new_connection:
|
||||
if set_tz:
|
||||
cursor.execute("SET TIME ZONE %s", [settings_dict['TIME_ZONE']])
|
||||
if not hasattr(self, '_version'):
|
||||
self.__class__._version = get_version(cursor)
|
||||
self._get_pg_version()
|
||||
if self.features.uses_autocommit:
|
||||
# FIXME: Eventually we'll enable this by default for
|
||||
# versions that support it, but, right now, that's hard to
|
||||
|
|
|
@ -6,15 +6,6 @@ from django.db.backends import BaseDatabaseOperations
|
|||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
def __init__(self, connection):
|
||||
super(DatabaseOperations, self).__init__(connection)
|
||||
self._postgres_version = None
|
||||
|
||||
def _get_postgres_version(self):
|
||||
if self._postgres_version is None:
|
||||
from django.db.backends.postgresql_psycopg2.version import get_version
|
||||
cursor = self.connection.cursor()
|
||||
self._postgres_version = get_version(cursor)
|
||||
return self._postgres_version
|
||||
postgres_version = property(_get_postgres_version)
|
||||
|
||||
def date_extract_sql(self, lookup_type, field_name):
|
||||
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
|
||||
|
@ -166,9 +157,9 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
NotImplementedError if this is the database in use.
|
||||
"""
|
||||
if aggregate.sql_function in ('STDDEV_POP', 'VAR_POP'):
|
||||
if self.postgres_version[0:2] == (8,2):
|
||||
if self.postgres_version[2] is None or self.postgres_version[2] <= 4:
|
||||
raise NotImplementedError('PostgreSQL 8.2 to 8.2.4 is known to have a faulty implementation of %s. Please upgrade your version of PostgreSQL.' % aggregate.sql_function)
|
||||
pg_version = self.connection.pg_version
|
||||
if pg_version >= 80200 and pg_version <= 80204:
|
||||
raise NotImplementedError('PostgreSQL 8.2 to 8.2.4 is known to have a faulty implementation of %s. Please upgrade your version of PostgreSQL.' % aggregate.sql_function)
|
||||
|
||||
def max_name_length(self):
|
||||
"""
|
||||
|
|
|
@ -17,16 +17,27 @@ def _parse_version(text):
|
|||
"Internal parsing method. Factored out for testing purposes."
|
||||
major, major2, minor = VERSION_RE.search(text).groups()
|
||||
try:
|
||||
return int(major), int(major2), int(minor)
|
||||
return int(major) * 10000 + int(major2) * 100 + int(minor)
|
||||
except (ValueError, TypeError):
|
||||
return int(major), int(major2), None
|
||||
return int(major) * 10000 + int(major2) * 100
|
||||
|
||||
def get_version(cursor):
|
||||
def get_version(connection):
|
||||
"""
|
||||
Returns a tuple representing the major, minor and revision number of the
|
||||
server. For example, (7, 4, 1) or (8, 3, 4). The revision number will be
|
||||
None in the case of initial releases (e.g., 'PostgreSQL 8.3') or in the
|
||||
case of beta and prereleases ('PostgreSQL 8.4beta1').
|
||||
Returns an integer representing the major, minor and revision number of the
|
||||
server. Format is the one used for the return value of libpq
|
||||
PQServerVersion()/``server_version`` connection attribute (available in
|
||||
newer psycopg2 versions.)
|
||||
|
||||
For example, 80304 for 8.3.4. The last two digits will be 00 in the case of
|
||||
releases (e.g., 80400 for 'PostgreSQL 8.4') or in the case of beta and
|
||||
prereleases (e.g. 90100 for 'PostgreSQL 9.1beta2').
|
||||
|
||||
PQServerVersion()/``server_version`` doesn't execute a query so try that
|
||||
first, then fallback to a ``SELECT version()`` query.
|
||||
"""
|
||||
cursor.execute("SELECT version()")
|
||||
return _parse_version(cursor.fetchone()[0])
|
||||
if hasattr(connection, 'server_version'):
|
||||
return connection.server_version
|
||||
else:
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT version()")
|
||||
return _parse_version(cursor.fetchone()[0])
|
||||
|
|
|
@ -196,12 +196,34 @@ class PostgresVersionTest(TestCase):
|
|||
self.assertEqual(pg_version._parse_version(version_string), version)
|
||||
|
||||
def test_parsing(self):
|
||||
self.assert_parses("PostgreSQL 8.3 beta4", (8, 3, None))
|
||||
self.assert_parses("PostgreSQL 8.3", (8, 3, None))
|
||||
self.assert_parses("EnterpriseDB 8.3", (8, 3, None))
|
||||
self.assert_parses("PostgreSQL 8.3.6", (8, 3, 6))
|
||||
self.assert_parses("PostgreSQL 8.4beta1", (8, 4, None))
|
||||
self.assert_parses("PostgreSQL 8.3.1 on i386-apple-darwin9.2.2, compiled by GCC i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5478)", (8, 3, 1))
|
||||
"""Test PostgreSQL version parsing from `SELECT version()` output"""
|
||||
self.assert_parses("PostgreSQL 8.3 beta4", 80300)
|
||||
self.assert_parses("PostgreSQL 8.3", 80300)
|
||||
self.assert_parses("EnterpriseDB 8.3", 80300)
|
||||
self.assert_parses("PostgreSQL 8.3.6", 80306)
|
||||
self.assert_parses("PostgreSQL 8.4beta1", 80400)
|
||||
self.assert_parses("PostgreSQL 8.3.1 on i386-apple-darwin9.2.2, compiled by GCC i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5478)", 80301)
|
||||
|
||||
def test_version_detection(self):
|
||||
"""Test PostgreSQL version detection"""
|
||||
|
||||
# Helper mocks
|
||||
class CursorMock(object):
|
||||
"Very simple mock of DB-API cursor"
|
||||
def execute(self, arg):
|
||||
pass
|
||||
|
||||
def fetchone(self):
|
||||
return ["PostgreSQL 8.3"]
|
||||
|
||||
class OlderConnectionMock(object):
|
||||
"Mock of psycopg2 (< 2.0.12) connection"
|
||||
def cursor(self):
|
||||
return CursorMock()
|
||||
|
||||
# psycopg2 < 2.0.12 code path
|
||||
conn = OlderConnectionMock()
|
||||
self.assertEqual(pg_version.get_version(conn), 80300)
|
||||
|
||||
# Unfortunately with sqlite3 the in-memory test database cannot be
|
||||
# closed, and so it cannot be re-opened during testing, and so we
|
||||
|
|
Loading…
Reference in New Issue