mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-30 10:01:14 +02:00
dbbca2cf29
as determined by include-what-you-use (IWYU) While IWYU also suggests to *add* a bunch of #include's (which is its main purpose), this patch does not do that. In some cases, a more specific #include replaces another less specific one. Some manual adjustments of the automatic result: - IWYU currently doesn't know about includes that provide global variable declarations (like -Wmissing-variable-declarations), so those includes are being kept manually. - All includes for port(ability) headers are being kept for now, to play it safe. - No changes of catalog/pg_foo.h to catalog/pg_foo_d.h, to keep the patch from exploding in size. Note that this patch touches just *.c files, so nothing declared in header files changes in hidden ways. As a small example, in src/backend/access/transam/rmgr.c, some IWYU pragma annotations are added to handle a special case there. Discussion: https://www.postgresql.org/message-id/flat/af837490-6b2f-46df-ba05-37ea6a6653fc%40eisentraut.org
702 lines
21 KiB
C
702 lines
21 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* operatorcmds.c
|
|
*
|
|
* Routines for operator manipulation commands
|
|
*
|
|
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/commands/operatorcmds.c
|
|
*
|
|
* DESCRIPTION
|
|
* The "DefineFoo" routines take the parse tree and pick out the
|
|
* appropriate arguments/flags, passing the results to the
|
|
* corresponding "FooDefine" routines (in src/catalog) that do
|
|
* the actual catalog-munging. These routines also verify permission
|
|
* of the user to execute the command.
|
|
*
|
|
* NOTES
|
|
* These things must be defined and committed in the following order:
|
|
* "create function":
|
|
* input/output, recv/send functions
|
|
* "create type":
|
|
* type
|
|
* "create operator":
|
|
* operators
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/htup_details.h"
|
|
#include "access/table.h"
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/objectaccess.h"
|
|
#include "catalog/pg_namespace.h"
|
|
#include "catalog/pg_operator.h"
|
|
#include "catalog/pg_proc.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "commands/defrem.h"
|
|
#include "miscadmin.h"
|
|
#include "parser/parse_func.h"
|
|
#include "parser/parse_oper.h"
|
|
#include "parser/parse_type.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/syscache.h"
|
|
|
|
static Oid ValidateRestrictionEstimator(List *restrictionName);
|
|
static Oid ValidateJoinEstimator(List *joinName);
|
|
static Oid ValidateOperatorReference(List *name,
|
|
Oid leftTypeId,
|
|
Oid rightTypeId);
|
|
|
|
/*
|
|
* DefineOperator
|
|
* this function extracts all the information from the
|
|
* parameter list generated by the parser and then has
|
|
* OperatorCreate() do all the actual work.
|
|
*
|
|
* 'parameters' is a list of DefElem
|
|
*/
|
|
ObjectAddress
|
|
DefineOperator(List *names, List *parameters)
|
|
{
|
|
char *oprName;
|
|
Oid oprNamespace;
|
|
AclResult aclresult;
|
|
bool canMerge = false; /* operator merges */
|
|
bool canHash = false; /* operator hashes */
|
|
List *functionName = NIL; /* function for operator */
|
|
TypeName *typeName1 = NULL; /* first type name */
|
|
TypeName *typeName2 = NULL; /* second type name */
|
|
Oid typeId1 = InvalidOid; /* types converted to OID */
|
|
Oid typeId2 = InvalidOid;
|
|
Oid rettype;
|
|
List *commutatorName = NIL; /* optional commutator operator name */
|
|
List *negatorName = NIL; /* optional negator operator name */
|
|
List *restrictionName = NIL; /* optional restrict. sel. function */
|
|
List *joinName = NIL; /* optional join sel. function */
|
|
Oid functionOid; /* functions converted to OID */
|
|
Oid restrictionOid;
|
|
Oid joinOid;
|
|
Oid typeId[2]; /* to hold left and right arg */
|
|
int nargs;
|
|
ListCell *pl;
|
|
|
|
/* Convert list of names to a name and namespace */
|
|
oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
|
|
|
|
/* Check we have creation rights in target namespace */
|
|
aclresult = object_aclcheck(NamespaceRelationId, oprNamespace, GetUserId(), ACL_CREATE);
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, OBJECT_SCHEMA,
|
|
get_namespace_name(oprNamespace));
|
|
|
|
/*
|
|
* loop over the definition list and extract the information we need.
|
|
*/
|
|
foreach(pl, parameters)
|
|
{
|
|
DefElem *defel = (DefElem *) lfirst(pl);
|
|
|
|
if (strcmp(defel->defname, "leftarg") == 0)
|
|
{
|
|
typeName1 = defGetTypeName(defel);
|
|
if (typeName1->setof)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("SETOF type not allowed for operator argument")));
|
|
}
|
|
else if (strcmp(defel->defname, "rightarg") == 0)
|
|
{
|
|
typeName2 = defGetTypeName(defel);
|
|
if (typeName2->setof)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("SETOF type not allowed for operator argument")));
|
|
}
|
|
/* "function" and "procedure" are equivalent here */
|
|
else if (strcmp(defel->defname, "function") == 0)
|
|
functionName = defGetQualifiedName(defel);
|
|
else if (strcmp(defel->defname, "procedure") == 0)
|
|
functionName = defGetQualifiedName(defel);
|
|
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, "restrict") == 0)
|
|
restrictionName = defGetQualifiedName(defel);
|
|
else if (strcmp(defel->defname, "join") == 0)
|
|
joinName = defGetQualifiedName(defel);
|
|
else if (strcmp(defel->defname, "hashes") == 0)
|
|
canHash = defGetBoolean(defel);
|
|
else if (strcmp(defel->defname, "merges") == 0)
|
|
canMerge = defGetBoolean(defel);
|
|
/* These obsolete options are taken as meaning canMerge */
|
|
else if (strcmp(defel->defname, "sort1") == 0)
|
|
canMerge = true;
|
|
else if (strcmp(defel->defname, "sort2") == 0)
|
|
canMerge = true;
|
|
else if (strcmp(defel->defname, "ltcmp") == 0)
|
|
canMerge = true;
|
|
else if (strcmp(defel->defname, "gtcmp") == 0)
|
|
canMerge = true;
|
|
else
|
|
{
|
|
/* WARNING, not ERROR, for historical backwards-compatibility */
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("operator attribute \"%s\" not recognized",
|
|
defel->defname)));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* make sure we have our required definitions
|
|
*/
|
|
if (functionName == NIL)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("operator function must be specified")));
|
|
|
|
/* Transform type names to type OIDs */
|
|
if (typeName1)
|
|
typeId1 = typenameTypeId(NULL, typeName1);
|
|
if (typeName2)
|
|
typeId2 = typenameTypeId(NULL, typeName2);
|
|
|
|
/*
|
|
* If only the right argument is missing, the user is likely trying to
|
|
* create a postfix operator, so give them a hint about why that does not
|
|
* work. But if both arguments are missing, do not mention postfix
|
|
* operators, as the user most likely simply neglected to mention the
|
|
* arguments.
|
|
*/
|
|
if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("operator argument types must be specified")));
|
|
if (!OidIsValid(typeId2))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("operator right argument type must be specified"),
|
|
errdetail("Postfix operators are not supported.")));
|
|
|
|
if (typeName1)
|
|
{
|
|
aclresult = object_aclcheck(TypeRelationId, typeId1, GetUserId(), ACL_USAGE);
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error_type(aclresult, typeId1);
|
|
}
|
|
|
|
if (typeName2)
|
|
{
|
|
aclresult = object_aclcheck(TypeRelationId, typeId2, GetUserId(), ACL_USAGE);
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error_type(aclresult, typeId2);
|
|
}
|
|
|
|
/*
|
|
* Look up the operator's underlying function.
|
|
*/
|
|
if (!OidIsValid(typeId1))
|
|
{
|
|
typeId[0] = typeId2;
|
|
nargs = 1;
|
|
}
|
|
else if (!OidIsValid(typeId2))
|
|
{
|
|
typeId[0] = typeId1;
|
|
nargs = 1;
|
|
}
|
|
else
|
|
{
|
|
typeId[0] = typeId1;
|
|
typeId[1] = typeId2;
|
|
nargs = 2;
|
|
}
|
|
functionOid = LookupFuncName(functionName, nargs, typeId, false);
|
|
|
|
/*
|
|
* We require EXECUTE rights for the function. This isn't strictly
|
|
* necessary, since EXECUTE will be checked at any attempted use of the
|
|
* operator, but it seems like a good idea anyway.
|
|
*/
|
|
aclresult = object_aclcheck(ProcedureRelationId, functionOid, GetUserId(), ACL_EXECUTE);
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, OBJECT_FUNCTION,
|
|
NameListToString(functionName));
|
|
|
|
rettype = get_func_rettype(functionOid);
|
|
aclresult = object_aclcheck(TypeRelationId, rettype, GetUserId(), ACL_USAGE);
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error_type(aclresult, rettype);
|
|
|
|
/*
|
|
* Look up restriction and join estimators if specified
|
|
*/
|
|
if (restrictionName)
|
|
restrictionOid = ValidateRestrictionEstimator(restrictionName);
|
|
else
|
|
restrictionOid = InvalidOid;
|
|
if (joinName)
|
|
joinOid = ValidateJoinEstimator(joinName);
|
|
else
|
|
joinOid = InvalidOid;
|
|
|
|
/*
|
|
* now have OperatorCreate do all the work..
|
|
*/
|
|
return
|
|
OperatorCreate(oprName, /* operator name */
|
|
oprNamespace, /* namespace */
|
|
typeId1, /* left type id */
|
|
typeId2, /* right type id */
|
|
functionOid, /* function for operator */
|
|
commutatorName, /* optional commutator operator name */
|
|
negatorName, /* optional negator operator name */
|
|
restrictionOid, /* optional restrict. sel. function */
|
|
joinOid, /* optional join sel. function name */
|
|
canMerge, /* operator merges */
|
|
canHash); /* operator hashes */
|
|
}
|
|
|
|
/*
|
|
* Look up a restriction estimator function by name, and verify that it has
|
|
* the correct signature and we have the permissions to attach it to an
|
|
* operator.
|
|
*/
|
|
static Oid
|
|
ValidateRestrictionEstimator(List *restrictionName)
|
|
{
|
|
Oid typeId[4];
|
|
Oid restrictionOid;
|
|
AclResult aclresult;
|
|
|
|
typeId[0] = INTERNALOID; /* PlannerInfo */
|
|
typeId[1] = OIDOID; /* operator OID */
|
|
typeId[2] = INTERNALOID; /* args list */
|
|
typeId[3] = INT4OID; /* varRelid */
|
|
|
|
restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
|
|
|
|
/* estimators must return float8 */
|
|
if (get_func_rettype(restrictionOid) != FLOAT8OID)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
errmsg("restriction estimator function %s must return type %s",
|
|
NameListToString(restrictionName), "float8")));
|
|
|
|
/* Require EXECUTE rights for the estimator */
|
|
aclresult = object_aclcheck(ProcedureRelationId, restrictionOid, GetUserId(), ACL_EXECUTE);
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, OBJECT_FUNCTION,
|
|
NameListToString(restrictionName));
|
|
|
|
return restrictionOid;
|
|
}
|
|
|
|
/*
|
|
* Look up a join estimator function by name, and verify that it has the
|
|
* correct signature and we have the permissions to attach it to an
|
|
* operator.
|
|
*/
|
|
static Oid
|
|
ValidateJoinEstimator(List *joinName)
|
|
{
|
|
Oid typeId[5];
|
|
Oid joinOid;
|
|
Oid joinOid2;
|
|
AclResult aclresult;
|
|
|
|
typeId[0] = INTERNALOID; /* PlannerInfo */
|
|
typeId[1] = OIDOID; /* operator OID */
|
|
typeId[2] = INTERNALOID; /* args list */
|
|
typeId[3] = INT2OID; /* jointype */
|
|
typeId[4] = INTERNALOID; /* SpecialJoinInfo */
|
|
|
|
/*
|
|
* As of Postgres 8.4, the preferred signature for join estimators has 5
|
|
* arguments, but we still allow the old 4-argument form. Whine about
|
|
* ambiguity if both forms exist.
|
|
*/
|
|
joinOid = LookupFuncName(joinName, 5, typeId, true);
|
|
joinOid2 = LookupFuncName(joinName, 4, typeId, true);
|
|
if (OidIsValid(joinOid))
|
|
{
|
|
if (OidIsValid(joinOid2))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
|
|
errmsg("join estimator function %s has multiple matches",
|
|
NameListToString(joinName))));
|
|
}
|
|
else
|
|
{
|
|
joinOid = joinOid2;
|
|
/* If not found, reference the 5-argument signature in error msg */
|
|
if (!OidIsValid(joinOid))
|
|
joinOid = LookupFuncName(joinName, 5, typeId, false);
|
|
}
|
|
|
|
/* estimators must return float8 */
|
|
if (get_func_rettype(joinOid) != FLOAT8OID)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
errmsg("join estimator function %s must return type %s",
|
|
NameListToString(joinName), "float8")));
|
|
|
|
/* Require EXECUTE rights for the estimator */
|
|
aclresult = object_aclcheck(ProcedureRelationId, joinOid, GetUserId(), ACL_EXECUTE);
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, OBJECT_FUNCTION,
|
|
NameListToString(joinName));
|
|
|
|
return joinOid;
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
RemoveOperatorById(Oid operOid)
|
|
{
|
|
Relation relation;
|
|
HeapTuple tup;
|
|
Form_pg_operator op;
|
|
|
|
relation = table_open(OperatorRelationId, RowExclusiveLock);
|
|
|
|
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
|
|
if (!HeapTupleIsValid(tup)) /* should not happen */
|
|
elog(ERROR, "cache lookup failed for operator %u", operOid);
|
|
op = (Form_pg_operator) GETSTRUCT(tup);
|
|
|
|
/*
|
|
* Reset links from commutator and negator, if any. In case of a
|
|
* self-commutator or self-negator, this means we have to re-fetch the
|
|
* updated tuple. (We could optimize away updates on the tuple we're
|
|
* about to drop, but it doesn't seem worth convoluting the logic for.)
|
|
*/
|
|
if (OidIsValid(op->oprcom) || OidIsValid(op->oprnegate))
|
|
{
|
|
OperatorUpd(operOid, op->oprcom, op->oprnegate, true);
|
|
if (operOid == op->oprcom || operOid == op->oprnegate)
|
|
{
|
|
ReleaseSysCache(tup);
|
|
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
|
|
if (!HeapTupleIsValid(tup)) /* should not happen */
|
|
elog(ERROR, "cache lookup failed for operator %u", operOid);
|
|
}
|
|
}
|
|
|
|
CatalogTupleDelete(relation, &tup->t_self);
|
|
|
|
ReleaseSysCache(tup);
|
|
|
|
table_close(relation, RowExclusiveLock);
|
|
}
|
|
|
|
/*
|
|
* AlterOperator
|
|
* 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)
|
|
{
|
|
ObjectAddress address;
|
|
Oid oprId;
|
|
Relation catalog;
|
|
HeapTuple tup;
|
|
Form_pg_operator oprForm;
|
|
int i;
|
|
ListCell *pl;
|
|
Datum values[Natts_pg_operator];
|
|
bool nulls[Natts_pg_operator];
|
|
bool replaces[Natts_pg_operator];
|
|
List *restrictionName = NIL; /* optional restrict. sel. function */
|
|
bool updateRestriction = false;
|
|
Oid restrictionOid;
|
|
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);
|
|
catalog = table_open(OperatorRelationId, RowExclusiveLock);
|
|
tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId));
|
|
if (!HeapTupleIsValid(tup))
|
|
elog(ERROR, "cache lookup failed for operator %u", oprId);
|
|
oprForm = (Form_pg_operator) GETSTRUCT(tup);
|
|
|
|
/* Process options */
|
|
foreach(pl, stmt->options)
|
|
{
|
|
DefElem *defel = (DefElem *) lfirst(pl);
|
|
List *param;
|
|
|
|
if (defel->arg == NULL)
|
|
param = NIL; /* NONE, removes the function */
|
|
else
|
|
param = defGetQualifiedName(defel);
|
|
|
|
if (strcmp(defel->defname, "restrict") == 0)
|
|
{
|
|
restrictionName = param;
|
|
updateRestriction = true;
|
|
}
|
|
else if (strcmp(defel->defname, "join") == 0)
|
|
{
|
|
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.
|
|
* Check for them so that we can give a meaningful error message.
|
|
*/
|
|
else if (strcmp(defel->defname, "leftarg") == 0 ||
|
|
strcmp(defel->defname, "rightarg") == 0 ||
|
|
strcmp(defel->defname, "function") == 0 ||
|
|
strcmp(defel->defname, "procedure") == 0)
|
|
{
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("operator attribute \"%s\" cannot be changed",
|
|
defel->defname)));
|
|
}
|
|
else
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("operator attribute \"%s\" not recognized",
|
|
defel->defname)));
|
|
}
|
|
|
|
/* Check permissions. Must be owner. */
|
|
if (!object_ownercheck(OperatorRelationId, oprId, GetUserId()))
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
|
|
NameStr(oprForm->oprname));
|
|
|
|
/*
|
|
* Look up OIDs for any parameters specified
|
|
*/
|
|
if (restrictionName)
|
|
restrictionOid = ValidateRestrictionEstimator(restrictionName);
|
|
else
|
|
restrictionOid = InvalidOid;
|
|
if (joinName)
|
|
joinOid = ValidateJoinEstimator(joinName);
|
|
else
|
|
joinOid = InvalidOid;
|
|
|
|
if (commutatorName)
|
|
{
|
|
/* 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 (negatorName)
|
|
{
|
|
negatorOid = ValidateOperatorReference(negatorName,
|
|
oprForm->oprleft,
|
|
oprForm->oprright);
|
|
|
|
/* Must reject self-negation */
|
|
if (negatorOid == oprForm->oid)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
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)
|
|
{
|
|
values[i] = (Datum) 0;
|
|
replaces[i] = false;
|
|
nulls[i] = false;
|
|
}
|
|
if (updateRestriction)
|
|
{
|
|
replaces[Anum_pg_operator_oprrest - 1] = true;
|
|
values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid);
|
|
}
|
|
if (updateJoin)
|
|
{
|
|
replaces[Anum_pg_operator_oprjoin - 1] = true;
|
|
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),
|
|
values, nulls, replaces);
|
|
|
|
CatalogTupleUpdate(catalog, &tup->t_self, tup);
|
|
|
|
address = makeOperatorDependencies(tup, false, true);
|
|
|
|
if (OidIsValid(commutatorOid) || OidIsValid(negatorOid))
|
|
OperatorUpd(oprId, commutatorOid, negatorOid, false);
|
|
|
|
InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
|
|
|
|
table_close(catalog, NoLock);
|
|
|
|
return address;
|
|
}
|