summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/backends/postgresql_psycopg2/base.py10
-rw-r--r--django/db/backends/postgresql_psycopg2/operations.py15
-rw-r--r--django/db/backends/postgresql_psycopg2/version.py29
-rw-r--r--tests/regressiontests/backends/tests.py34
4 files changed, 59 insertions, 29 deletions
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index 4f6bcbf973..5a94c815bd 100644
--- a/django/db/backends/postgresql_psycopg2/base.py
+++ b/django/db/backends/postgresql_psycopg2/base.py
@@ -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
diff --git a/django/db/backends/postgresql_psycopg2/operations.py b/django/db/backends/postgresql_psycopg2/operations.py
index ecd0dde1c6..fe021b5fc6 100644
--- a/django/db/backends/postgresql_psycopg2/operations.py
+++ b/django/db/backends/postgresql_psycopg2/operations.py
@@ -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):
"""
diff --git a/django/db/backends/postgresql_psycopg2/version.py b/django/db/backends/postgresql_psycopg2/version.py
index b9298296ea..8ef516704e 100644
--- a/django/db/backends/postgresql_psycopg2/version.py
+++ b/django/db/backends/postgresql_psycopg2/version.py
@@ -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])
diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py
index 33c8763037..29db6a705a 100644
--- a/tests/regressiontests/backends/tests.py
+++ b/tests/regressiontests/backends/tests.py
@@ -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