[1.11.x] Fixed #27935 -- Fixed crash with BrinIndex name > 30 characters.

Backport of 82bb4e684f from master
This commit is contained in:
Mads Jensen 2017-03-17 19:01:25 +01:00 committed by Tim Graham
parent c548dba428
commit 0b93a992e5
3 changed files with 30 additions and 7 deletions

View File

@ -1,6 +1,8 @@
from __future__ import unicode_literals
from django.db import connection
from django.db.models import Index
from django.utils.functional import cached_property
__all__ = ['BrinIndex', 'GinIndex']
@ -36,6 +38,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'

View File

@ -6,12 +6,12 @@ from django.utils.encoding import force_bytes
__all__ = [str('Index')]
# The max length of the names of the indexes (restricted to 30 due to Oracle)
MAX_NAME_LENGTH = 30
class Index(object):
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):
@ -27,8 +27,8 @@ class Index(object):
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)
@ -102,13 +102,15 @@ class Index(object):
(('-%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?'
)

View File

@ -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()