diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 95918a77a1..b0570c33b6 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -57,8 +57,7 @@ static Oid OperatorShellMake(const char *operatorName, static Oid get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, const char *operatorName, Oid operatorNamespace, - Oid leftTypeId, Oid rightTypeId, - bool isCommutator); + Oid leftTypeId, Oid rightTypeId); /* @@ -442,8 +441,7 @@ OperatorCreate(const char *operatorName, commutatorId = get_other_operator(commutatorName, rightTypeId, leftTypeId, operatorName, operatorNamespace, - leftTypeId, rightTypeId, - true); + leftTypeId, rightTypeId); /* Permission check: must own other operator */ if (OidIsValid(commutatorId) && @@ -467,8 +465,19 @@ OperatorCreate(const char *operatorName, negatorId = get_other_operator(negatorName, leftTypeId, rightTypeId, operatorName, operatorNamespace, - leftTypeId, rightTypeId, - false); + leftTypeId, rightTypeId); + + /* + * Prevent self negation as it doesn't make sense. When self negating, + * get_other_operator returns InvalidOid when creating a new operator + * and the OID of the operator we are 'creating' when upgrading a + * shell operator. + */ + + if (!OidIsValid(negatorId) || operatorObjectId == negatorId) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("operator cannot be its own negator"))); /* Permission check: must own other operator */ if (OidIsValid(negatorId) && @@ -584,7 +593,7 @@ OperatorCreate(const char *operatorName, static Oid get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, const char *operatorName, Oid operatorNamespace, - Oid leftTypeId, Oid rightTypeId, bool isCommutator) + Oid leftTypeId, Oid rightTypeId) { Oid other_oid; bool otherDefined; @@ -611,14 +620,7 @@ get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, otherLeftTypeId == leftTypeId && otherRightTypeId == rightTypeId) { - /* - * self-linkage to this operator; caller will fix later. Note that - * only self-linkage for commutation makes sense. - */ - if (!isCommutator) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("operator cannot be its own negator or sort operator"))); + /* self-linkage to this operator; caller will fix later. */ return InvalidOid; } diff --git a/src/test/regress/expected/create_operator.out b/src/test/regress/expected/create_operator.out index f71b601f2d..49070ce90f 100644 --- a/src/test/regress/expected/create_operator.out +++ b/src/test/regress/expected/create_operator.out @@ -260,6 +260,42 @@ CREATE OPERATOR #*# ( ); ERROR: permission denied for type type_op6 ROLLBACK; +-- Should fail. An operator cannot have a self negator. +BEGIN TRANSACTION; +CREATE FUNCTION create_op_test_fn(boolean, boolean) +RETURNS boolean AS $$ + SELECT NULL::BOOLEAN; +$$ LANGUAGE sql IMMUTABLE; +CREATE OPERATOR === ( + leftarg = boolean, + rightarg = boolean, + procedure = create_op_test_fn, + negator = === +); +ERROR: operator cannot be its own negator +ROLLBACK; +-- Should fail. An operator cannot have a self negator. Here we test that when +-- 'creating' an existing shell operator, it checks the negator is not self. +BEGIN TRANSACTION; +CREATE FUNCTION create_op_test_fn(boolean, boolean) +RETURNS boolean AS $$ + SELECT NULL::BOOLEAN; +$$ LANGUAGE sql IMMUTABLE; +-- create a shell operator for ===!!! by referencing it as a commutator +CREATE OPERATOR === ( + leftarg = boolean, + rightarg = boolean, + procedure = create_op_test_fn, + commutator = ===!!! +); +CREATE OPERATOR ===!!! ( + leftarg = boolean, + rightarg = boolean, + procedure = create_op_test_fn, + negator = ===!!! +); +ERROR: operator cannot be its own negator +ROLLBACK; -- invalid: non-lowercase quoted identifiers CREATE OPERATOR === ( diff --git a/src/test/regress/sql/create_operator.sql b/src/test/regress/sql/create_operator.sql index f53e24db3c..168cad3814 100644 --- a/src/test/regress/sql/create_operator.sql +++ b/src/test/regress/sql/create_operator.sql @@ -210,6 +210,42 @@ CREATE OPERATOR #*# ( ); ROLLBACK; +-- Should fail. An operator cannot have a self negator. +BEGIN TRANSACTION; +CREATE FUNCTION create_op_test_fn(boolean, boolean) +RETURNS boolean AS $$ + SELECT NULL::BOOLEAN; +$$ LANGUAGE sql IMMUTABLE; +CREATE OPERATOR === ( + leftarg = boolean, + rightarg = boolean, + procedure = create_op_test_fn, + negator = === +); +ROLLBACK; + +-- Should fail. An operator cannot have a self negator. Here we test that when +-- 'creating' an existing shell operator, it checks the negator is not self. +BEGIN TRANSACTION; +CREATE FUNCTION create_op_test_fn(boolean, boolean) +RETURNS boolean AS $$ + SELECT NULL::BOOLEAN; +$$ LANGUAGE sql IMMUTABLE; +-- create a shell operator for ===!!! by referencing it as a commutator +CREATE OPERATOR === ( + leftarg = boolean, + rightarg = boolean, + procedure = create_op_test_fn, + commutator = ===!!! +); +CREATE OPERATOR ===!!! ( + leftarg = boolean, + rightarg = boolean, + procedure = create_op_test_fn, + negator = ===!!! +); +ROLLBACK; + -- invalid: non-lowercase quoted identifiers CREATE OPERATOR === (