On 17.03.23 17:03, Paul Jungwirth wrote:
Thank you for taking a look! I did some research on the history of the code here, and I think I understand Tom's concern about making sure the index uses the same equality operator as the partition. I was confused about his remarks about the opfamily, but I agree with you that if the operator is the same, we should be okay.

I added the code about RTEqualStrategyNumber because that's what we need to find an equals operator when the index is GiST (except if it's using an opclass from btree_gist; then it needs to be BTEqual again). But then I realized that for exclusion constraints we have already figured out the operator (in RelationGetExclusionInfo) and put it in indexInfo->ii_ExclusionOps. So we can just compare against that. This works whether your index uses btree_gist or not.

Here is an updated patch with that change (also rebased).

I also included a more specific error message. If we find a matching column in the index but with the wrong operator, we should say so, and not say there is no matching column.

This looks all pretty good to me.  A few more comments:

It seems to me that many of the test cases added in indexing.sql are redundant with create_table.sql/alter_table.sql (or vice versa). Is there a reason for this?


This is not really a problem in your patch, but I think in

-   if (partitioned && (stmt->unique || stmt->primary))
+ if (partitioned && (stmt->unique || stmt->primary || stmt->excludeOpNames != NIL))

the stmt->primary is redundant and should be removed. Right now "primary" is always a subset of "unique", but presumably a future patch of yours wants to change that.


Furthermore, I think it would be more elegant in your patch if you wrote stmt->excludeOpNames without the "== NIL" or "!= NIL", so that it becomes a peer of stmt->unique. (I understand some people don't like that style. But it is already used in that file.)


I would consider rearranging some of the conditionals more as a selection of cases, like "is it a unique constraint?", "else, is it an exclusion constraint?" -- rather than the current "is it an exclusion constraint?, "else, various old code". For example, instead of

    if (stmt->excludeOpNames != NIL)
        idx_eqop = indexInfo->ii_ExclusionOps[j];
    else
        idx_eqop = get_opfamily_member(..., eq_strategy);

consider

    if (stmt->unique)
        idx_eqop = get_opfamily_member(..., eq_strategy);
    else if (stmt->excludeOpNames)
        idx_eqop = indexInfo->ii_ExclusionOps[j];
    Assert(idx_eqop);

Also, I would push the code

    if (accessMethodId == BTREE_AM_OID)
        eq_strategy = BTEqualStrategyNumber;

further down into the loop, so that you don't have to remember in which cases eq_strategy is assigned or not.

(It's also confusing that the eq_strategy variable is used for two different things in this function, and that would clean that up.)


Finally, this code

+                           att = TupleDescAttr(RelationGetDescr(rel),
+                                               key->partattrs[i] - 1);
+                           ereport(ERROR,
+                                   (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot match partition key to index on column \"%s\" using non-equal operator \"%s\".", + NameStr(att->attname), get_opname(indexInfo->ii_ExclusionOps[j]))));

could be simplified by using get_attname().


This is all just a bit of polishing. I think it would be good to go after that.


Reply via email to