diff options
| author | Tom Lane | 2023-10-20 16:28:38 +0000 |
|---|---|---|
| committer | Tom Lane | 2023-10-20 16:28:46 +0000 |
| commit | 2b5154beab794eae6e624c162d497df927ec9d27 (patch) | |
| tree | 0b5dc8a146130bb47be791ecce3d174b1d81e341 /src/backend | |
| parent | dcd4454590e77dc90c28ce4b4a4b62369bbc03e2 (diff) | |
Extend ALTER OPERATOR to allow setting more optimization attributes.
Allow the COMMUTATOR, NEGATOR, MERGES, and HASHES attributes to be set
by ALTER OPERATOR. However, we don't allow COMMUTATOR/NEGATOR to be
changed once set, nor allow the MERGES/HASHES flags to be unset once
set. Changes like that might invalidate plans already made, and
dealing with the consequences seems like more trouble than it's worth.
The main use-case we foresee for this is to allow addition of missed
properties in extension update scripts, such as extending an existing
operator to support hashing. So only transitions from not-set to set
states seem very useful.
This patch also causes us to reject some incorrect cases that formerly
resulted in inconsistent catalog state, such as trying to set the
commutator of an operator to be some other operator that already has a
(different) commutator.
While at it, move the InvokeObjectPostCreateHook call for CREATE
OPERATOR to not occur until after we've fixed up commutator or negator
links as needed. The previous ordering could only be justified by
thinking of the OperatorUpd call as a kind of ALTER OPERATOR step;
but we don't call InvokeObjectPostAlterHook therein. It seems better
to let the hook see the final state of the operator object.
In the documentation, move the discussion of how to establish
commutator pairs from xoper.sgml to the CREATE OPERATOR ref page.
Tommy Pavlicek, reviewed and editorialized a bit by me
Discussion: https://postgr.es/m/CAEhP-W-vGVzf4udhR5M8Bdv88UYnPrhoSkj3ieR3QNrsGQoqdg@mail.gmail.com
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/catalog/pg_operator.c | 254 | ||||
| -rw-r--r-- | src/backend/commands/operatorcmds.c | 194 | ||||
| -rw-r--r-- | src/backend/parser/gram.y | 2 | ||||
| -rw-r--r-- | src/backend/parser/parse_oper.c | 38 |
4 files changed, 351 insertions, 137 deletions
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 95918a77a15..4d1b9758fa7 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -44,11 +44,6 @@ static Oid OperatorGet(const char *operatorName, Oid rightObjectId, bool *defined); -static Oid OperatorLookup(List *operatorName, - Oid leftObjectId, - Oid rightObjectId, - bool *defined); - static Oid OperatorShellMake(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, @@ -57,8 +52,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); /* @@ -166,7 +160,7 @@ OperatorGet(const char *operatorName, * * *defined is set true if defined (not a shell) */ -static Oid +Oid OperatorLookup(List *operatorName, Oid leftObjectId, Oid rightObjectId, @@ -361,53 +355,17 @@ OperatorCreate(const char *operatorName, errmsg("\"%s\" is not a valid operator name", operatorName))); - if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId))) - { - /* If it's not a binary op, these things mustn't be set: */ - if (commutatorName) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only binary operators can have commutators"))); - if (OidIsValid(joinId)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only binary operators can have join selectivity"))); - if (canMerge) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only binary operators can merge join"))); - if (canHash) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only binary operators can hash"))); - } - operResultType = get_func_rettype(procedureId); - if (operResultType != BOOLOID) - { - /* If it's not a boolean op, these things mustn't be set: */ - if (negatorName) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only boolean operators can have negators"))); - if (OidIsValid(restrictionId)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only boolean operators can have restriction selectivity"))); - if (OidIsValid(joinId)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only boolean operators can have join selectivity"))); - if (canMerge) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only boolean operators can merge join"))); - if (canHash) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only boolean operators can hash"))); - } + OperatorValidateParams(leftTypeId, + rightTypeId, + operResultType, + commutatorName != NIL, + negatorName != NIL, + OidIsValid(restrictionId), + OidIsValid(joinId), + canMerge, + canHash); operatorObjectId = OperatorGet(operatorName, operatorNamespace, @@ -442,8 +400,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) && @@ -452,8 +409,9 @@ OperatorCreate(const char *operatorName, NameListToString(commutatorName)); /* - * self-linkage to this operator; will fix below. Note that only - * self-linkage for commutation makes sense. + * If self-linkage to the new operator is requested, we'll fix it + * below. (In case of self-linkage to an existing shell operator, we + * need do nothing special.) */ if (!OidIsValid(commutatorId)) selfCommutator = true; @@ -467,14 +425,24 @@ OperatorCreate(const char *operatorName, negatorId = get_other_operator(negatorName, leftTypeId, rightTypeId, operatorName, operatorNamespace, - leftTypeId, rightTypeId, - false); + leftTypeId, rightTypeId); /* Permission check: must own other operator */ if (OidIsValid(negatorId) && !object_ownercheck(OperatorRelationId, negatorId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR, NameListToString(negatorName)); + + /* + * Prevent self negation, as it doesn't make sense. It's self + * negation if result is InvalidOid (negator would be the same + * operator but it doesn't exist yet) or operatorObjectId (we are + * replacing a shell that would need to be its own negator). + */ + if (!OidIsValid(negatorId) || negatorId == operatorObjectId) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("operator cannot be its own negator"))); } else negatorId = InvalidOid; @@ -548,11 +516,6 @@ OperatorCreate(const char *operatorName, /* Add dependencies for the entry */ address = makeOperatorDependencies(tup, true, isUpdate); - /* Post creation hook for new operator */ - InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0); - - table_close(pg_operator_desc, RowExclusiveLock); - /* * If a commutator and/or negator link is provided, update the other * operator(s) to point at this one, if they don't already have a link. @@ -570,21 +533,95 @@ OperatorCreate(const char *operatorName, if (OidIsValid(commutatorId) || OidIsValid(negatorId)) OperatorUpd(operatorObjectId, commutatorId, negatorId, false); + /* Post creation hook for new operator */ + InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0); + + table_close(pg_operator_desc, RowExclusiveLock); + return address; } /* - * Try to lookup another operator (commutator, etc) + * OperatorValidateParams * - * If not found, check to see if it is exactly the operator we are trying - * to define; if so, return InvalidOid. (Note that this case is only - * sensible for a commutator, so we error out otherwise.) If it is not - * the same operator, create a shell operator. + * Check that an operator with argument types leftTypeId and rightTypeId, + * returning operResultType, can have the attributes that are set to true. + * Raise an error for any disallowed attribute. + * + * Note: in ALTER OPERATOR, we only bother to pass "true" for attributes + * the command is trying to set, not those that may already be set. + * This is OK as long as the attribute checks are independent. + */ +void +OperatorValidateParams(Oid leftTypeId, + Oid rightTypeId, + Oid operResultType, + bool hasCommutator, + bool hasNegator, + bool hasRestrictionSelectivity, + bool hasJoinSelectivity, + bool canMerge, + bool canHash) +{ + if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId))) + { + /* If it's not a binary op, these things mustn't be set: */ + if (hasCommutator) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only binary operators can have commutators"))); + if (hasJoinSelectivity) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only binary operators can have join selectivity"))); + if (canMerge) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only binary operators can merge join"))); + if (canHash) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only binary operators can hash"))); + } + + if (operResultType != BOOLOID) + { + /* If it's not a boolean op, these things mustn't be set: */ + if (hasNegator) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can have negators"))); + if (hasRestrictionSelectivity) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can have restriction selectivity"))); + if (hasJoinSelectivity) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can have join selectivity"))); + if (canMerge) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can merge join"))); + if (canHash) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can hash"))); + } +} + +/* + * Try to lookup another operator (commutator, etc); return its OID + * + * If not found, check to see if it would be the same operator we are trying + * to define; if so, return InvalidOid. (Caller must decide whether + * that is sensible.) If it is not the same operator, create a shell + * operator. */ 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 +648,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 new operator; caller must handle this */ return InvalidOid; } @@ -643,7 +673,7 @@ get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, * For a given operator, look up its negator and commutator operators. * When isDelete is false, update their negator and commutator fields to * point back to the given operator; when isDelete is true, update those - * fields to no longer point back to the given operator. + * fields to be InvalidOid. * * The !isDelete case solves a problem for users who need to insert two new * operators that are the negator or commutator of each other, while the @@ -681,17 +711,40 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete) bool update_commutator = false; /* - * Out of due caution, we only change the commutator's oprcom field if - * it has the exact value we expected: InvalidOid when creating an - * operator, or baseId when dropping one. + * We can skip doing anything if the commutator's oprcom field is + * already what we want. While that's not expected in the isDelete + * case, it's perfectly possible when filling in a shell operator. */ - if (isDelete && t->oprcom == baseId) + if (isDelete && OidIsValid(t->oprcom)) { t->oprcom = InvalidOid; update_commutator = true; } - else if (!isDelete && !OidIsValid(t->oprcom)) + else if (!isDelete && t->oprcom != baseId) { + /* + * If commutator's oprcom field is already set to point to some + * third operator, it's an error. Changing its link would be + * unsafe, and letting the inconsistency stand would not be good + * either. This might be indicative of catalog corruption, so + * don't assume t->oprcom is necessarily a valid operator. + */ + if (OidIsValid(t->oprcom)) + { + char *thirdop = get_opname(t->oprcom); + + if (thirdop != NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("commutator operator %s is already the commutator of operator %s", + NameStr(t->oprname), thirdop))); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("commutator operator %s is already the commutator of operator %u", + NameStr(t->oprname), t->oprcom))); + } + t->oprcom = baseId; update_commutator = true; } @@ -726,17 +779,40 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete) bool update_negator = false; /* - * Out of due caution, we only change the negator's oprnegate field if - * it has the exact value we expected: InvalidOid when creating an - * operator, or baseId when dropping one. + * We can skip doing anything if the negator's oprnegate field is + * already what we want. While that's not expected in the isDelete + * case, it's perfectly possible when filling in a shell operator. */ - if (isDelete && t->oprnegate == baseId) + if (isDelete && OidIsValid(t->oprnegate)) { t->oprnegate = InvalidOid; update_negator = true; } - else if (!isDelete && !OidIsValid(t->oprnegate)) + else if (!isDelete && t->oprnegate != baseId) { + /* + * If negator's oprnegate field is already set to point to some + * third operator, it's an error. Changing its link would be + * unsafe, and letting the inconsistency stand would not be good + * either. This might be indicative of catalog corruption, so + * don't assume t->oprnegate is necessarily a valid operator. + */ + if (OidIsValid(t->oprnegate)) + { + char *thirdop = get_opname(t->oprnegate); + + if (thirdop != NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("negator operator %s is already the negator of operator %s", + NameStr(t->oprname), thirdop))); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("negator operator %s is already the negator of operator %u", + NameStr(t->oprname), t->oprnegate))); + } + t->oprnegate = baseId; update_negator = true; } diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index cd7f83136f7..df84b839f53 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -54,6 +54,9 @@ static Oid ValidateRestrictionEstimator(List *restrictionName); static Oid ValidateJoinEstimator(List *joinName); +static Oid ValidateOperatorReference(List *name, + Oid leftTypeId, + Oid rightTypeId); /* * DefineOperator @@ -360,6 +363,53 @@ ValidateJoinEstimator(List *joinName) } /* + * Look up and return the OID of an operator, + * given a possibly-qualified name and left and right type IDs. + * + * Verifies that the operator is defined (not a shell) and owned by + * the current user, so that we have permission to associate it with + * the operator being altered. Rejecting shell operators is a policy + * choice to help catch mistakes, rather than something essential. + */ +static Oid +ValidateOperatorReference(List *name, + Oid leftTypeId, + Oid rightTypeId) +{ + Oid oid; + bool defined; + + oid = OperatorLookup(name, + leftTypeId, + rightTypeId, + &defined); + + /* These message strings are chosen to match parse_oper.c */ + if (!OidIsValid(oid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("operator does not exist: %s", + op_signature_string(name, + leftTypeId, + rightTypeId)))); + + if (!defined) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("operator is only a shell: %s", + op_signature_string(name, + leftTypeId, + rightTypeId)))); + + if (!object_ownercheck(OperatorRelationId, oid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR, + NameListToString(name)); + + return oid; +} + + +/* * Guts of operator deletion. */ void @@ -406,6 +456,10 @@ RemoveOperatorById(Oid operOid) * routine implementing ALTER OPERATOR <operator> SET (option = ...). * * Currently, only RESTRICT and JOIN estimator functions can be changed. + * COMMUTATOR, NEGATOR, MERGES, and HASHES attributes can be set if they + * have not been set previously. (Changing or removing one of these + * attributes could invalidate existing plans, which seems more trouble + * than it's worth.) */ ObjectAddress AlterOperator(AlterOperatorStmt *stmt) @@ -426,6 +480,14 @@ AlterOperator(AlterOperatorStmt *stmt) List *joinName = NIL; /* optional join sel. function */ bool updateJoin = false; Oid joinOid; + List *commutatorName = NIL; /* optional commutator operator name */ + Oid commutatorOid; + List *negatorName = NIL; /* optional negator operator name */ + Oid negatorOid; + bool canMerge = false; + bool updateMerges = false; + bool canHash = false; + bool updateHashes = false; /* Look up the operator */ oprId = LookupOperWithArgs(stmt->opername, false); @@ -456,6 +518,24 @@ AlterOperator(AlterOperatorStmt *stmt) joinName = param; updateJoin = true; } + else if (strcmp(defel->defname, "commutator") == 0) + { + commutatorName = defGetQualifiedName(defel); + } + else if (strcmp(defel->defname, "negator") == 0) + { + negatorName = defGetQualifiedName(defel); + } + else if (strcmp(defel->defname, "merges") == 0) + { + canMerge = defGetBoolean(defel); + updateMerges = true; + } + else if (strcmp(defel->defname, "hashes") == 0) + { + canHash = defGetBoolean(defel); + updateHashes = true; + } /* * The rest of the options that CREATE accepts cannot be changed. @@ -464,11 +544,7 @@ AlterOperator(AlterOperatorStmt *stmt) else if (strcmp(defel->defname, "leftarg") == 0 || strcmp(defel->defname, "rightarg") == 0 || strcmp(defel->defname, "function") == 0 || - strcmp(defel->defname, "procedure") == 0 || - strcmp(defel->defname, "commutator") == 0 || - strcmp(defel->defname, "negator") == 0 || - strcmp(defel->defname, "hashes") == 0 || - strcmp(defel->defname, "merges") == 0) + strcmp(defel->defname, "procedure") == 0) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -488,7 +564,7 @@ AlterOperator(AlterOperatorStmt *stmt) NameStr(oprForm->oprname)); /* - * Look up restriction and join estimators if specified + * Look up OIDs for any parameters specified */ if (restrictionName) restrictionOid = ValidateRestrictionEstimator(restrictionName); @@ -499,28 +575,79 @@ AlterOperator(AlterOperatorStmt *stmt) else joinOid = InvalidOid; - /* Perform additional checks, like OperatorCreate does */ - if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright))) + if (commutatorName) { - /* If it's not a binary op, these things mustn't be set: */ - if (OidIsValid(joinOid)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only binary operators can have join selectivity"))); + /* commutator has reversed arg types */ + commutatorOid = ValidateOperatorReference(commutatorName, + oprForm->oprright, + oprForm->oprleft); + + /* + * We don't need to do anything extra for a self commutator as in + * OperatorCreate, since the operator surely exists already. + */ } + else + commutatorOid = InvalidOid; - if (oprForm->oprresult != BOOLOID) + if (negatorName) { - if (OidIsValid(restrictionOid)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only boolean operators can have restriction selectivity"))); - if (OidIsValid(joinOid)) + negatorOid = ValidateOperatorReference(negatorName, + oprForm->oprleft, + oprForm->oprright); + + /* Must reject self-negation */ + if (negatorOid == oprForm->oid) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("only boolean operators can have join selectivity"))); + errmsg("operator cannot be its own negator"))); + } + else + { + negatorOid = InvalidOid; } + /* + * Check that we're not changing any attributes that might be depended on + * by plans, while allowing no-op updates. + */ + if (OidIsValid(commutatorOid) && OidIsValid(oprForm->oprcom) && + commutatorOid != oprForm->oprcom) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("operator attribute \"%s\" cannot be changed if it has already been set", + "commutator"))); + + if (OidIsValid(negatorOid) && OidIsValid(oprForm->oprnegate) && + negatorOid != oprForm->oprnegate) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("operator attribute \"%s\" cannot be changed if it has already been set", + "negator"))); + + if (updateMerges && oprForm->oprcanmerge && !canMerge) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("operator attribute \"%s\" cannot be changed if it has already been set", + "merges"))); + + if (updateHashes && oprForm->oprcanhash && !canHash) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("operator attribute \"%s\" cannot be changed if it has already been set", + "hashes"))); + + /* Perform additional checks, like OperatorCreate does */ + OperatorValidateParams(oprForm->oprleft, + oprForm->oprright, + oprForm->oprresult, + OidIsValid(commutatorOid), + OidIsValid(negatorOid), + OidIsValid(restrictionOid), + OidIsValid(joinOid), + canMerge, + canHash); + /* Update the tuple */ for (i = 0; i < Natts_pg_operator; ++i) { @@ -531,12 +658,32 @@ AlterOperator(AlterOperatorStmt *stmt) if (updateRestriction) { replaces[Anum_pg_operator_oprrest - 1] = true; - values[Anum_pg_operator_oprrest - 1] = restrictionOid; + values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid); } if (updateJoin) { replaces[Anum_pg_operator_oprjoin - 1] = true; - values[Anum_pg_operator_oprjoin - 1] = joinOid; + values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid); + } + if (OidIsValid(commutatorOid)) + { + replaces[Anum_pg_operator_oprcom - 1] = true; + values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorOid); + } + if (OidIsValid(negatorOid)) + { + replaces[Anum_pg_operator_oprnegate - 1] = true; + values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorOid); + } + if (updateMerges) + { + replaces[Anum_pg_operator_oprcanmerge - 1] = true; + values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge); + } + if (updateHashes) + { + replaces[Anum_pg_operator_oprcanhash - 1] = true; + values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash); } tup = heap_modify_tuple(tup, RelationGetDescr(catalog), @@ -546,6 +693,9 @@ AlterOperator(AlterOperatorStmt *stmt) address = makeOperatorDependencies(tup, false, true); + if (OidIsValid(commutatorOid) || OidIsValid(negatorOid)) + OperatorUpd(oprId, commutatorOid, negatorOid, false); + InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0); table_close(catalog, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 50ed504e5a0..c224df4eccc 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10151,6 +10151,8 @@ operator_def_elem: ColLabel '=' NONE { $$ = makeDefElem($1, NULL, @1); } | ColLabel '=' operator_def_arg { $$ = makeDefElem($1, (Node *) $3, @1); } + | ColLabel + { $$ = makeDefElem($1, NULL, @1); } ; /* must be similar enough to def_arg to avoid reduce/reduce conflicts */ diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index bdc8f8e26af..d08fc23f5b6 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -70,9 +70,7 @@ static FuncDetailCode oper_select_candidate(int nargs, Oid *input_typeids, FuncCandidateList candidates, Oid *operOid); -static const char *op_signature_string(List *op, char oprkind, - Oid arg1, Oid arg2); -static void op_error(ParseState *pstate, List *op, char oprkind, +static void op_error(ParseState *pstate, List *op, Oid arg1, Oid arg2, FuncDetailCode fdresult, int location); static bool make_oper_cache_key(ParseState *pstate, OprCacheKey *key, @@ -110,26 +108,16 @@ LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright, /* we don't use op_error here because only an exact match is wanted */ if (!noError) { - char oprkind; - - if (!OidIsValid(oprleft)) - oprkind = 'l'; - else if (OidIsValid(oprright)) - oprkind = 'b'; - else - { + if (!OidIsValid(oprright)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("postfix operators are not supported"), parser_errposition(pstate, location))); - oprkind = 0; /* keep compiler quiet */ - } ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operator does not exist: %s", - op_signature_string(opername, oprkind, - oprleft, oprright)), + op_signature_string(opername, oprleft, oprright)), parser_errposition(pstate, location))); } @@ -446,7 +434,7 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId, make_oper_cache_entry(&key, operOid); } else if (!noError) - op_error(pstate, opname, 'b', ltypeId, rtypeId, fdresult, location); + op_error(pstate, opname, ltypeId, rtypeId, fdresult, location); return (Operator) tup; } @@ -483,7 +471,7 @@ compatible_oper(ParseState *pstate, List *op, Oid arg1, Oid arg2, ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operator requires run-time type coercion: %s", - op_signature_string(op, 'b', arg1, arg2)), + op_signature_string(op, arg1, arg2)), parser_errposition(pstate, location))); return (Operator) NULL; @@ -597,7 +585,7 @@ left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location) make_oper_cache_entry(&key, operOid); } else if (!noError) - op_error(pstate, op, 'l', InvalidOid, arg, fdresult, location); + op_error(pstate, op, InvalidOid, arg, fdresult, location); return (Operator) tup; } @@ -610,14 +598,14 @@ left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location) * This is typically used in the construction of operator-not-found error * messages. */ -static const char * -op_signature_string(List *op, char oprkind, Oid arg1, Oid arg2) +const char * +op_signature_string(List *op, Oid arg1, Oid arg2) { StringInfoData argbuf; initStringInfo(&argbuf); - if (oprkind != 'l') + if (OidIsValid(arg1)) appendStringInfo(&argbuf, "%s ", format_type_be(arg1)); appendStringInfoString(&argbuf, NameListToString(op)); @@ -631,7 +619,7 @@ op_signature_string(List *op, char oprkind, Oid arg1, Oid arg2) * op_error - utility routine to complain about an unresolvable operator */ static void -op_error(ParseState *pstate, List *op, char oprkind, +op_error(ParseState *pstate, List *op, Oid arg1, Oid arg2, FuncDetailCode fdresult, int location) { @@ -639,7 +627,7 @@ op_error(ParseState *pstate, List *op, char oprkind, ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("operator is not unique: %s", - op_signature_string(op, oprkind, arg1, arg2)), + op_signature_string(op, arg1, arg2)), errhint("Could not choose a best candidate operator. " "You might need to add explicit type casts."), parser_errposition(pstate, location))); @@ -647,7 +635,7 @@ op_error(ParseState *pstate, List *op, char oprkind, ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operator does not exist: %s", - op_signature_string(op, oprkind, arg1, arg2)), + op_signature_string(op, arg1, arg2)), (!arg1 || !arg2) ? errhint("No operator matches the given name and argument type. " "You might need to add an explicit type cast.") : @@ -713,7 +701,6 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operator is only a shell: %s", op_signature_string(opname, - opform->oprkind, opform->oprleft, opform->oprright)), parser_errposition(pstate, location))); @@ -827,7 +814,6 @@ make_scalar_array_op(ParseState *pstate, List *opname, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operator is only a shell: %s", op_signature_string(opname, - opform->oprkind, opform->oprleft, opform->oprright)), parser_errposition(pstate, location))); |
