summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMads Jensen <mje@inducks.org>2017-03-17 19:01:25 +0100
committerTim Graham <timograham@gmail.com>2017-03-18 08:12:10 -0400
commit82bb4e684f9d0e5939cb0596c249954df0888e2d (patch)
tree52c5efbb4bd23a159134b3bda654dfcefc6e5098
parent93eca976c15dceaf445ab4739bb2f635a2021db0 (diff)
Fixed #27935 -- Fixed crash with BrinIndex name > 30 characters.
-rw-r--r--django/contrib/postgres/indexes.py10
-rw-r--r--django/db/models/indexes.py14
-rw-r--r--tests/postgres_tests/test_indexes.py13
3 files changed, 30 insertions, 7 deletions
diff --git a/django/contrib/postgres/indexes.py b/django/contrib/postgres/indexes.py
index 60578e6c16..feb7053ed8 100644
--- a/django/contrib/postgres/indexes.py
+++ b/django/contrib/postgres/indexes.py
@@ -1,4 +1,6 @@
+from django.db import connection
from django.db.models import Index
+from django.utils.functional import cached_property
__all__ = ['BrinIndex', 'GinIndex']
@@ -34,6 +36,14 @@ class BrinIndex(Index):
schema_editor.quote_value(self.pages_per_range)) + parameters['extra']
return parameters
+ @cached_property
+ def max_name_length(self):
+ # Allow an index name longer than 30 characters since the suffix
+ # is 4 characters (usual limit is 3). Since this index can only be
+ # used on PostgreSQL, the 30 character limit for cross-database
+ # compatibility isn't applicable.
+ return connection.ops.max_name_length()
+
class GinIndex(Index):
suffix = 'gin'
diff --git a/django/db/models/indexes.py b/django/db/models/indexes.py
index 1370463beb..3bb73cf16a 100644
--- a/django/db/models/indexes.py
+++ b/django/db/models/indexes.py
@@ -4,12 +4,12 @@ from django.utils.encoding import force_bytes
__all__ = ['Index']
-# The max length of the names of the indexes (restricted to 30 due to Oracle)
-MAX_NAME_LENGTH = 30
-
class Index:
suffix = 'idx'
+ # The max length of the name of the index (restricted to 30 for
+ # cross-database compatibility with Oracle)
+ max_name_length = 30
def __init__(self, fields=[], name=None):
if not isinstance(fields, list):
@@ -25,8 +25,8 @@ class Index:
self.name = name or ''
if self.name:
errors = self.check_name()
- if len(self.name) > MAX_NAME_LENGTH:
- errors.append('Index names cannot be longer than %s characters.' % MAX_NAME_LENGTH)
+ if len(self.name) > self.max_name_length:
+ errors.append('Index names cannot be longer than %s characters.' % self.max_name_length)
if errors:
raise ValueError(errors)
@@ -100,13 +100,15 @@ class Index:
(('-%s' if order else '%s') % column_name)
for column_name, (field_name, order) in zip(column_names, self.fields_orders)
]
+ # The length of the parts of the name is based on the default max
+ # length of 30 characters.
hash_data = [table_name] + column_names_with_order + [self.suffix]
self.name = '%s_%s_%s' % (
table_name[:11],
column_names[0][:7],
'%s_%s' % (self._hash_generator(*hash_data), self.suffix),
)
- assert len(self.name) <= MAX_NAME_LENGTH, (
+ assert len(self.name) <= self.max_name_length, (
'Index too long for multiple database support. Is self.suffix '
'longer than 3 characters?'
)
diff --git a/tests/postgres_tests/test_indexes.py b/tests/postgres_tests/test_indexes.py
index db61561200..e26d96f003 100644
--- a/tests/postgres_tests/test_indexes.py
+++ b/tests/postgres_tests/test_indexes.py
@@ -3,7 +3,7 @@ from django.db import connection
from django.test import skipUnlessDBFeature
from . import PostgreSQLTestCase
-from .models import CharFieldModel, IntegerArrayModel
+from .models import CharFieldModel, DateTimeArrayModel, IntegerArrayModel
@skipUnlessDBFeature('has_brin_index_support')
@@ -23,6 +23,17 @@ class BrinIndexTests(PostgreSQLTestCase):
index_with_page_range = BrinIndex(fields=['title'], pages_per_range=16)
self.assertNotEqual(index, index_with_page_range)
+ def test_name_auto_generation(self):
+ """
+ A name longer than 30 characters (since len(BrinIndex.suffix) is 4
+ rather than usual limit of 3) is okay for PostgreSQL. For this test,
+ the name of the field ('datetimes') must be at least 7 characters to
+ generate a name longer than 30 characters.
+ """
+ index = BrinIndex(fields=['datetimes'])
+ index.set_name_with_model(DateTimeArrayModel)
+ self.assertEqual(index.name, 'postgres_te_datetim_abf104_brin')
+
def test_deconstruction(self):
index = BrinIndex(fields=['title'], name='test_title_brin')
path, args, kwargs = index.deconstruct()