diff options
| author | Ian Foote <python@ian.feete.org> | 2017-10-11 22:55:52 +0530 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2018-06-29 17:00:28 -0400 |
| commit | 38cada7c94f5f73d2d47a0a730ea5d71d266fa2c (patch) | |
| tree | f7cfb69df62a07b0cda4b3c0d1c4a2f6ea5780bf /django | |
| parent | b4cba4ed625ce7c88675616b3bbb237c28a926d1 (diff) | |
Fixed #28077 -- Added support for PostgreSQL opclasses in Index.
Thanks Vinay Karanam for the initial patch.
Diffstat (limited to 'django')
| -rw-r--r-- | django/db/backends/base/schema.py | 7 | ||||
| -rw-r--r-- | django/db/backends/ddl_references.py | 18 | ||||
| -rw-r--r-- | django/db/backends/postgresql/schema.py | 12 | ||||
| -rw-r--r-- | django/db/models/indexes.py | 13 |
4 files changed, 42 insertions, 8 deletions
diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index a722e497c3..ec2cf0e5c7 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -907,7 +907,7 @@ class BaseDatabaseSchemaEditor: return '' def _create_index_sql(self, model, fields, *, name=None, suffix='', using='', - db_tablespace=None, col_suffixes=(), sql=None): + db_tablespace=None, col_suffixes=(), sql=None, opclasses=()): """ Return the SQL statement to create the index for one or several fields. `sql` can be specified if the syntax differs from the standard (GIS @@ -929,10 +929,13 @@ class BaseDatabaseSchemaEditor: table=Table(table, self.quote_name), name=IndexName(table, columns, suffix, create_index_name), using=using, - columns=Columns(table, columns, self.quote_name, col_suffixes=col_suffixes), + columns=self._index_columns(table, columns, col_suffixes, opclasses), extra=tablespace_sql, ) + def _index_columns(self, table, columns, col_suffixes, opclasses): + return Columns(table, columns, self.quote_name, col_suffixes=col_suffixes) + def _model_indexes_sql(self, model): """ Return a list of all index SQL statements (field indexes, diff --git a/django/db/backends/ddl_references.py b/django/db/backends/ddl_references.py index b894d58793..d71f6169ea 100644 --- a/django/db/backends/ddl_references.py +++ b/django/db/backends/ddl_references.py @@ -103,6 +103,24 @@ class IndexName(TableColumns): return self.create_index_name(self.table, self.columns, self.suffix) +class IndexColumns(Columns): + def __init__(self, table, columns, quote_name, col_suffixes=(), opclasses=()): + self.opclasses = opclasses + super().__init__(table, columns, quote_name, col_suffixes) + + def __str__(self): + def col_str(column, idx): + try: + col = self.quote_name(column) + self.col_suffixes[idx] + except IndexError: + col = self.quote_name(column) + # Index.__init__() guarantees that self.opclasses is the same + # length as self.columns. + return '{} {}'.format(col, self.opclasses[idx]) + + return ', '.join(col_str(column, idx) for idx, column in enumerate(self.columns)) + + class ForeignKeyName(TableColumns): """Hold a reference to a foreign key name.""" diff --git a/django/db/backends/postgresql/schema.py b/django/db/backends/postgresql/schema.py index 18388cc523..feaddfab52 100644 --- a/django/db/backends/postgresql/schema.py +++ b/django/db/backends/postgresql/schema.py @@ -1,6 +1,7 @@ import psycopg2 from django.db.backends.base.schema import BaseDatabaseSchemaEditor +from django.db.backends.ddl_references import IndexColumns class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): @@ -12,8 +13,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_set_sequence_max = "SELECT setval('%(sequence)s', MAX(%(column)s)) FROM %(table)s" sql_create_index = "CREATE INDEX %(name)s ON %(table)s%(using)s (%(columns)s)%(extra)s" - sql_create_varchar_index = "CREATE INDEX %(name)s ON %(table)s (%(columns)s varchar_pattern_ops)%(extra)s" - sql_create_text_index = "CREATE INDEX %(name)s ON %(table)s (%(columns)s text_pattern_ops)%(extra)s" sql_delete_index = "DROP INDEX IF EXISTS %(name)s" # Setting the constraint to IMMEDIATE runs any deferred checks to allow @@ -49,9 +48,9 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): if '[' in db_type: return None if db_type.startswith('varchar'): - return self._create_index_sql(model, [field], suffix='_like', sql=self.sql_create_varchar_index) + return self._create_index_sql(model, [field], suffix='_like', opclasses=['varchar_pattern_ops']) elif db_type.startswith('text'): - return self._create_index_sql(model, [field], suffix='_like', sql=self.sql_create_text_index) + return self._create_index_sql(model, [field], suffix='_like', opclasses=['text_pattern_ops']) return None def _alter_column_type_sql(self, model, old_field, new_field, new_type): @@ -132,3 +131,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): if old_field.unique and not (new_field.db_index or new_field.unique): index_to_remove = self._create_index_name(model._meta.db_table, [old_field.column], suffix='_like') self.execute(self._delete_constraint_sql(self.sql_delete_index, model, index_to_remove)) + + def _index_columns(self, table, columns, col_suffixes, opclasses): + if opclasses: + return IndexColumns(table, columns, self.quote_name, col_suffixes=col_suffixes, opclasses=opclasses) + return super()._index_columns(table, columns, col_suffixes, opclasses) diff --git a/django/db/models/indexes.py b/django/db/models/indexes.py index 9bfb9e0558..c378b13a5c 100644 --- a/django/db/models/indexes.py +++ b/django/db/models/indexes.py @@ -12,9 +12,15 @@ class Index: # cross-database compatibility with Oracle) max_name_length = 30 - def __init__(self, *, fields=(), name=None, db_tablespace=None): + def __init__(self, *, fields=(), name=None, db_tablespace=None, opclasses=()): + if opclasses and not name: + raise ValueError('An index must be named to use opclasses.') if not isinstance(fields, (list, tuple)): raise ValueError('Index.fields must be a list or tuple.') + if not isinstance(opclasses, (list, tuple)): + raise ValueError('Index.opclasses must be a list or tuple.') + if opclasses and len(fields) != len(opclasses): + raise ValueError('Index.fields and Index.opclasses must have the same number of elements.') if not fields: raise ValueError('At least one field is required to define an index.') self.fields = list(fields) @@ -31,6 +37,7 @@ class Index: if errors: raise ValueError(errors) self.db_tablespace = db_tablespace + self.opclasses = opclasses def check_name(self): errors = [] @@ -49,7 +56,7 @@ class Index: col_suffixes = [order[1] for order in self.fields_orders] return schema_editor._create_index_sql( model, fields, name=self.name, using=using, db_tablespace=self.db_tablespace, - col_suffixes=col_suffixes, + col_suffixes=col_suffixes, opclasses=self.opclasses, ) def remove_sql(self, model, schema_editor): @@ -65,6 +72,8 @@ class Index: kwargs = {'fields': self.fields, 'name': self.name} if self.db_tablespace is not None: kwargs['db_tablespace'] = self.db_tablespace + if self.opclasses: + kwargs['opclasses'] = self.opclasses return (path, (), kwargs) def clone(self): |
