Opened 2 years ago

Closed 2 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:

  1. 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.

  1. PostgresIndex.create_sql respects the using 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 Alexandru Mărășteanu, 2 years ago

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.

comment:2 by Mariusz Felisiak, 2 years ago

Summary: `PostgresIndex` uses `suffix` for both index name and methodPostgresIndex.create_sql() doesn't respect the using argument.
Triage Stage: UnreviewedAccepted
Type: BugCleanup/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):  
    2525    def create_sql(self, model, schema_editor, using="", **kwargs):
    2626        self.check_supported(schema_editor)
    2727        statement = super().create_sql(
    28             model, schema_editor, using=" USING %s" % self.suffix, **kwargs
     28            model, schema_editor, using=" USING %s" % (using or self.suffix), **kwargs
    2929        )
    3030        with_params = self.get_with_params()
    3131        if with_params:

in reply to:  2 ; comment:3 by Alexandru Mărășteanu, 2 years ago

Replying to Mariusz Felisiak:

I agree that create_sql() should respect the using 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?

in reply to:  3 comment:4 by Mariusz Felisiak, 2 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 Alexandru Mărășteanu, 2 years ago

Owner: set to Alexandru Mărășteanu
Status: newassigned

comment:6 by Mariusz Felisiak, 2 years ago

Easy pickings: set

Alexandru, Do you have time to keep working on this?

in reply to:  6 comment:7 by Alexandru Mărășteanu, 2 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 Mariusz Felisiak, 2 years ago

Patch needs improvement: set

comment:10 by Mariusz Felisiak, 2 years ago

Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

comment:11 by GitHub <noreply@…>, 2 years ago

Resolution: fixed
Status: assignedclosed

In a1e4e86f:

Fixed #33607 -- Made PostgresIndex.create_sql() respect the "using" argument.

Note: See TracTickets for help on using tickets.
Back to Top