Opened 3 years ago
Closed 3 years ago
#33607 closed Cleanup/optimization (fixed)
PostgresIndex.create_sql() doesn't respect the using argument.
Reported by: | Alexandru Mărășteanu | Owned by: | Alexandru Mărășteanu |
---|---|---|---|
Component: | contrib.postgres | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | yes | UI/UX: | no |
Description
django.contrib.postgres.indexes.PostgresIndex
uses the suffix
attribute for both generating the index name (in Index.set_name_with_model
see https://github.com/django/django/blob/fac662f4798f7e4e0ed9be6b4fb4a87a80810a68/django/db/models/indexes.py#L153-L186) and for deciding the indexing method (in PostgresIndex.create_sql
i.e. CREATE INDEX ... USING {suffix} ...
see https://github.com/django/django/blob/fac662f4798f7e4e0ed9be6b4fb4a87a80810a68/django/contrib/postgres/indexes.py#L25-L36). A developer using and extending PostgresIndex
for use with multiple opclasses is unable to specify a custom suffix for the naming scheme.
For example, given two custom indexes like:
class Foo(GinIndex): def __init__(self, *args, **kwargs): super().__init__(*args, opclasses=[some opclass], **kwargs) class Bar(GinIndex): def __init__(self, *args, **kwargs): super().__init__(*args, opclasses=[some opclass], **kwargs)
If set on the same model fields, both Foo.set_name_with_model
and Bar.set_name_with_model
would then generate the same index name à la dbtable_field_hash_gin
which is not ok because index names need to be unique.
Setting custom suffix
es on Foo
and Bar
is not possible with the current code because PostgresIndex.create_sql
uses the suffix as the index method. If the suffix doesn't match any of the supported opclasses, then PostgreSQL complains about it.
To workaround that, an option would be to override the create_sql
method:
class Foo(GinIndex): suffix = "foo" def __init__(self, *args, **kwargs): super().__init__(*args, opclasses=[some opclass], **kwargs) def create_sql(self, model, schema_editor, using="", **kwargs): return super().create_sql(model, schema_editor, using="gin", **kwargs) class Bar(GinIndex): suffix = "bar" def __init__(self, *args, **kwargs): super().__init__(*args, opclasses=[some opclass], **kwargs) def create_sql(self, model, schema_editor, using="", **kwargs): return super().create_sql(model, schema_editor, using="gin", **kwargs)
The problem is, even though PostgresIndex.create_sql
accepts a using
argument, it currently ignores it. From the source code:
class PostgresIndex(Index): ... def create_sql(self, model, schema_editor, using="", **kwargs): ... statement = super().create_sql( model, schema_editor, using=" USING %s" % self.suffix, **kwargs ) ...
So in the end there's no way to extend the PostgresIndex
with a custom suffix. I was able to do it by temporarily setting a custom suffix in set_name_with_model
but that's more of a hack rather than a solution.
--
That said, I believe there are two things which need to be addressed here:
- Distinguish between the index name suffix and the index method
PostgresIndex
would then declare another property which indicated the method to use when defining the index i.e. using
.
PostgresIndex.create_sql
respects theusing
argument
E.g. (also considering point 1 above):
class PostgresIndex(Index): ... def create_sql(self, model, schema_editor, using="", **kwargs): ... statement = super().create_sql( model, schema_editor, using=using or self.using, **kwargs ) ...
---
If this is accepted, I'm available to work on it.
Change History (11)
comment:1 by , 3 years ago
follow-up: 3 comment:2 by , 3 years ago
Summary: | `PostgresIndex` uses `suffix` for both index name and method → PostgresIndex.create_sql() doesn't respect the using argument. |
---|---|
Triage Stage: | Unreviewed → Accepted |
Type: | Bug → Cleanup/optimization |
Thanks for the report. PostgresIndex
is an internal, undocumented API. However, I agree that create_sql()
should respect the using
argument, so the following diff should be sufficient:
-
django/contrib/postgres/indexes.py
diff --git a/django/contrib/postgres/indexes.py b/django/contrib/postgres/indexes.py index a0c03681b2..97377d8cb1 100644
a b class PostgresIndex(Index): 25 25 def create_sql(self, model, schema_editor, using="", **kwargs): 26 26 self.check_supported(schema_editor) 27 27 statement = super().create_sql( 28 model, schema_editor, using=" USING %s" % self.suffix, **kwargs28 model, schema_editor, using=" USING %s" % (using or self.suffix), **kwargs 29 29 ) 30 30 with_params = self.get_with_params() 31 31 if with_params:
follow-up: 4 comment:3 by , 3 years ago
Replying to Mariusz Felisiak:
I agree that
create_sql()
should respect theusing
argument, so the following diff should be sufficient:
Fine, that works for me. What are the next steps?
--
Also, is it possible to make this available to all (2+) Django versions?
comment:4 by , 3 years ago
Fine, that works for me. What are the next steps?
You can assign this ticket to yourself and prepare PR (test is required).
Also, is it possible to make this available to all (2+) Django versions?
No, we don't backport cleanups and new features.
comment:5 by , 3 years ago
Owner: | set to |
---|---|
Status: | new → assigned |
follow-up: 7 comment:6 by , 3 years ago
Easy pickings: | set |
---|
Alexandru, Do you have time to keep working on this?
comment:7 by , 3 years ago
Replying to Mariusz Felisiak:
Alexandru, Do you have time to keep working on this?
Yes, I'll take care of it this week. Sorry for the delay, I was on vacation.
comment:9 by , 3 years ago
Patch needs improvement: | set |
---|
comment:10 by , 3 years ago
Patch needs improvement: | unset |
---|---|
Triage Stage: | Accepted → Ready for checkin |
Note I'm aware that the index class requires an explicit name when using opclasses, but I think if these were addressed, then it would be easier in the future to leave the name generation to Django.