Fixed #10842 -- Corrected parsing of version numbers for PostgreSQL 8.4beta series. Thanks to hgdeoro for the report and fix.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10730 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2009-05-10 09:22:06 +00:00
parent 5663258de1
commit b97178f7ec
5 changed files with 49 additions and 24 deletions

View File

@ -121,7 +121,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
cursor.execute("SET TIME ZONE %s", [settings_dict['TIME_ZONE']]) cursor.execute("SET TIME ZONE %s", [settings_dict['TIME_ZONE']])
if not hasattr(self, '_version'): if not hasattr(self, '_version'):
self.__class__._version = get_version(cursor) self.__class__._version = get_version(cursor)
if self._version < (8, 0): if self._version[0:2] < [8, 0]:
# No savepoint support for earlier version of PostgreSQL. # No savepoint support for earlier version of PostgreSQL.
self.features.uses_savepoints = False self.features.uses_savepoints = False
cursor.execute("SET client_encoding to 'UNICODE'") cursor.execute("SET client_encoding to 'UNICODE'")

View File

@ -2,8 +2,6 @@ import re
from django.db.backends import BaseDatabaseOperations from django.db.backends import BaseDatabaseOperations
server_version_re = re.compile(r'PostgreSQL (\d{1,2})\.(\d{1,2})\.?(\d{1,2})?')
# This DatabaseOperations class lives in here instead of base.py because it's # This DatabaseOperations class lives in here instead of base.py because it's
# used by both the 'postgresql' and 'postgresql_psycopg2' backends. # used by both the 'postgresql' and 'postgresql_psycopg2' backends.
@ -14,13 +12,9 @@ class DatabaseOperations(BaseDatabaseOperations):
def _get_postgres_version(self): def _get_postgres_version(self):
if self._postgres_version is None: if self._postgres_version is None:
from django.db import connection from django.db import connection
from django.db.backends.postgresql.version import get_version
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute("SELECT version()") self._postgres_version = get_version(cursor)
version_string = cursor.fetchone()[0]
m = server_version_re.match(version_string)
if not m:
raise Exception('Unable to determine PostgreSQL version from version() function string: %r' % version_string)
self._postgres_version = [int(val) for val in m.groups() if val]
return self._postgres_version return self._postgres_version
postgres_version = property(_get_postgres_version) postgres_version = property(_get_postgres_version)
@ -72,7 +66,7 @@ class DatabaseOperations(BaseDatabaseOperations):
def sql_flush(self, style, tables, sequences): def sql_flush(self, style, tables, sequences):
if tables: if tables:
if self.postgres_version[0] >= 8 and self.postgres_version[1] >= 1: if self.postgres_version[0:2] >= [8,1]:
# Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to*
# in order to be able to truncate tables referenced by a foreign # in order to be able to truncate tables referenced by a foreign
# key in any other table. The result is a single SQL TRUNCATE # key in any other table. The result is a single SQL TRUNCATE
@ -157,5 +151,6 @@ class DatabaseOperations(BaseDatabaseOperations):
NotImplementedError if this is the database in use. NotImplementedError if this is the database in use.
""" """
if aggregate.sql_function == 'STDDEV_POP' or aggregate.sql_function == 'VAR_POP': if aggregate.sql_function == 'STDDEV_POP' or aggregate.sql_function == 'VAR_POP':
if self.postgres_version[0] == 8 and self.postgres_version[1] == 2 and self.postgres_version[2] <= 4: 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) 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)

View File

@ -4,20 +4,28 @@ Extracts the version of the PostgreSQL server.
import re import re
# This reg-exp is intentionally fairly flexible here. Require only the major # This reg-exp is intentionally fairly flexible here.
# and minor version numbers, but need to be able to handle standard stuff like: # Needs to be able to handle stuff like:
# PostgreSQL 8.3.6 # PostgreSQL 8.3.6
# EnterpriseDB 8.3 # EnterpriseDB 8.3
# PostgreSQL 8.3 beta4 # PostgreSQL 8.3 beta4
VERSION_RE = re.compile(r'\S+ (\d+)\.(\d+)') # PostgreSQL 8.4beta1
VERSION_RE = re.compile(r'\S+ (\d+)\.(\d+)\.?(\d+)?')
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)
except (ValueError, TypeError):
return int(major), int(major2), None
def get_version(cursor): def get_version(cursor):
""" """
Returns a tuple representing the major and minor version number of the Returns a tuple representing the major, minor and revision number of the
server. For example, (7, 4) or (8, 3). 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').
""" """
cursor.execute("SELECT version()") cursor.execute("SELECT version()")
version = cursor.fetchone()[0] return _parse_version(cursor.fetchone()[0])
major, minor = VERSION_RE.search(version).groups()
return int(major), int(minor)

View File

@ -105,11 +105,11 @@ class DatabaseWrapper(BaseDatabaseWrapper):
cursor.execute("SET TIME ZONE %s", [settings_dict['TIME_ZONE']]) cursor.execute("SET TIME ZONE %s", [settings_dict['TIME_ZONE']])
if not hasattr(self, '_version'): if not hasattr(self, '_version'):
self.__class__._version = get_version(cursor) self.__class__._version = get_version(cursor)
if self._version < (8, 0): if self._version[0:2] < [8, 0]:
# No savepoint support for earlier version of PostgreSQL. # No savepoint support for earlier version of PostgreSQL.
self.features.uses_savepoints = False self.features.uses_savepoints = False
if self.features.uses_autocommit: if self.features.uses_autocommit:
if self._version < (8, 2): if self._version[0:2] < [8, 2]:
# FIXME: Needs extra code to do reliable model insert # FIXME: Needs extra code to do reliable model insert
# handling, so we forbid it for now. # handling, so we forbid it for now.
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured

View File

@ -21,7 +21,29 @@ class Callproc(unittest.TestCase):
def connection_created_test(sender, **kwargs): def connection_created_test(sender, **kwargs):
print 'connection_created signal' print 'connection_created signal'
__test__ = {'API_TESTS': ''} __test__ = {'API_TESTS': """
# Check Postgres version parsing
>>> from django.db.backends.postgresql import version as pg_version
>>> pg_version._parse_version("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)
>>> pg_version._parse_version("PostgreSQL 8.3.6")
(8, 3, 6)
>>> pg_version._parse_version("PostgreSQL 8.3")
(8, 3, None)
>>> pg_version._parse_version("EnterpriseDB 8.3")
(8, 3, None)
>>> pg_version._parse_version("PostgreSQL 8.3 beta4")
(8, 3, None)
>>> pg_version._parse_version("PostgreSQL 8.4beta1")
(8, 4, None)
"""}
# Unfortunately with sqlite3 the in-memory test database cannot be # Unfortunately with sqlite3 the in-memory test database cannot be
# closed, and so it cannot be re-opened during testing, and so we # closed, and so it cannot be re-opened during testing, and so we