Fixed #27935 -- Fixed crash with BrinIndex name > 30 characters.
This commit is contained in:
parent
93eca976c1
commit
82bb4e684f
|
@ -1,4 +1,6 @@
|
||||||
|
from django.db import connection
|
||||||
from django.db.models import Index
|
from django.db.models import Index
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
__all__ = ['BrinIndex', 'GinIndex']
|
__all__ = ['BrinIndex', 'GinIndex']
|
||||||
|
|
||||||
|
@ -34,6 +36,14 @@ class BrinIndex(Index):
|
||||||
schema_editor.quote_value(self.pages_per_range)) + parameters['extra']
|
schema_editor.quote_value(self.pages_per_range)) + parameters['extra']
|
||||||
return parameters
|
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):
|
class GinIndex(Index):
|
||||||
suffix = 'gin'
|
suffix = 'gin'
|
||||||
|
|
|
@ -4,12 +4,12 @@ from django.utils.encoding import force_bytes
|
||||||
|
|
||||||
__all__ = ['Index']
|
__all__ = ['Index']
|
||||||
|
|
||||||
# The max length of the names of the indexes (restricted to 30 due to Oracle)
|
|
||||||
MAX_NAME_LENGTH = 30
|
|
||||||
|
|
||||||
|
|
||||||
class Index:
|
class Index:
|
||||||
suffix = 'idx'
|
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):
|
def __init__(self, fields=[], name=None):
|
||||||
if not isinstance(fields, list):
|
if not isinstance(fields, list):
|
||||||
|
@ -25,8 +25,8 @@ class Index:
|
||||||
self.name = name or ''
|
self.name = name or ''
|
||||||
if self.name:
|
if self.name:
|
||||||
errors = self.check_name()
|
errors = self.check_name()
|
||||||
if len(self.name) > MAX_NAME_LENGTH:
|
if len(self.name) > self.max_name_length:
|
||||||
errors.append('Index names cannot be longer than %s characters.' % MAX_NAME_LENGTH)
|
errors.append('Index names cannot be longer than %s characters.' % self.max_name_length)
|
||||||
if errors:
|
if errors:
|
||||||
raise ValueError(errors)
|
raise ValueError(errors)
|
||||||
|
|
||||||
|
@ -100,13 +100,15 @@ class Index:
|
||||||
(('-%s' if order else '%s') % column_name)
|
(('-%s' if order else '%s') % column_name)
|
||||||
for column_name, (field_name, order) in zip(column_names, self.fields_orders)
|
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]
|
hash_data = [table_name] + column_names_with_order + [self.suffix]
|
||||||
self.name = '%s_%s_%s' % (
|
self.name = '%s_%s_%s' % (
|
||||||
table_name[:11],
|
table_name[:11],
|
||||||
column_names[0][:7],
|
column_names[0][:7],
|
||||||
'%s_%s' % (self._hash_generator(*hash_data), self.suffix),
|
'%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 '
|
'Index too long for multiple database support. Is self.suffix '
|
||||||
'longer than 3 characters?'
|
'longer than 3 characters?'
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.db import connection
|
||||||
from django.test import skipUnlessDBFeature
|
from django.test import skipUnlessDBFeature
|
||||||
|
|
||||||
from . import PostgreSQLTestCase
|
from . import PostgreSQLTestCase
|
||||||
from .models import CharFieldModel, IntegerArrayModel
|
from .models import CharFieldModel, DateTimeArrayModel, IntegerArrayModel
|
||||||
|
|
||||||
|
|
||||||
@skipUnlessDBFeature('has_brin_index_support')
|
@skipUnlessDBFeature('has_brin_index_support')
|
||||||
|
@ -23,6 +23,17 @@ class BrinIndexTests(PostgreSQLTestCase):
|
||||||
index_with_page_range = BrinIndex(fields=['title'], pages_per_range=16)
|
index_with_page_range = BrinIndex(fields=['title'], pages_per_range=16)
|
||||||
self.assertNotEqual(index, index_with_page_range)
|
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):
|
def test_deconstruction(self):
|
||||||
index = BrinIndex(fields=['title'], name='test_title_brin')
|
index = BrinIndex(fields=['title'], name='test_title_brin')
|
||||||
path, args, kwargs = index.deconstruct()
|
path, args, kwargs = index.deconstruct()
|
||||||
|
|
Loading…
Reference in New Issue