Opened 4 years ago
Closed 4 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 suffixes 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_sqlrespects theusingargument
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 , 4 years ago
follow-up: 3 comment:2 by , 4 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 , 4 years ago
Replying to Mariusz Felisiak:
I agree that
create_sql()should respect theusingargument, 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 , 4 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 , 4 years ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
follow-up: 7 comment:6 by , 4 years ago
| Easy pickings: | set |
|---|
Alexandru, Do you have time to keep working on this?
comment:7 by , 4 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 , 4 years ago
| Patch needs improvement: | set |
|---|
comment:10 by , 4 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.