X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Fcommands%2Foperatorcmds.c;h=6edbe85ef5328ad10bcf8afc9c37e6bee5399ad4;hb=59a2111b23f6ceec4c777d68e20c1027d3c57c6f;hp=36bedf4f48e580723076b0a9d32555a438782acc;hpb=9f2e211386931f7aee48ffbc2fcaef1632d8329f;p=postgresql diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index 36bedf4f48..6edbe85ef5 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -4,7 +4,7 @@ * * Routines for operator manipulation commands * - * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -35,23 +35,26 @@ #include "postgres.h" #include "access/heapam.h" +#include "access/htup_details.h" #include "catalog/dependency.h" #include "catalog/indexing.h" -#include "catalog/namespace.h" +#include "catalog/objectaccess.h" #include "catalog/pg_operator.h" +#include "catalog/pg_operator_fn.h" #include "catalog/pg_type.h" +#include "commands/alter.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/builtins.h" #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/syscache.h" - -static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId); +static Oid ValidateRestrictionEstimator(List *restrictionName); +static Oid ValidateJoinEstimator(List *joinName); /* * DefineOperator @@ -61,7 +64,7 @@ static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerI * * 'parameters' is a list of DefElem */ -void +ObjectAddress DefineOperator(List *names, List *parameters) { char *oprName; @@ -74,6 +77,7 @@ DefineOperator(List *names, List *parameters) 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. procedure */ @@ -81,23 +85,13 @@ DefineOperator(List *names, List *parameters) Oid functionOid; /* functions converted to OID */ Oid restrictionOid; Oid joinOid; - Oid typeId[5]; /* only need up to 5 args here */ + 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); - /* - * The SQL standard committee has decided that => should be used for named - * parameters; therefore, a future release of PostgreSQL may disallow it - * as the name of a user-defined operator. - */ - if (strcmp(oprName, "=>") == 0) - ereport(WARNING, - (errmsg("=> is deprecated as an operator name"), - errdetail("This name may be disallowed altogether in future versions of PostgreSQL."))); - /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(oprNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) @@ -151,10 +145,13 @@ DefineOperator(List *names, List *parameters) else if (pg_strcasecmp(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))); + } } /* @@ -167,15 +164,29 @@ DefineOperator(List *names, List *parameters) /* Transform type names to type OIDs */ if (typeName1) - typeId1 = typenameTypeId(NULL, typeName1, NULL); + typeId1 = typenameTypeId(NULL, typeName1); if (typeName2) - typeId2 = typenameTypeId(NULL, typeName2, NULL); + typeId2 = typenameTypeId(NULL, typeName2); if (!OidIsValid(typeId1) && !OidIsValid(typeId2)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("at least one of leftarg or rightarg must be specified"))); + if (typeName1) + { + aclresult = pg_type_aclcheck(typeId1, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error_type(aclresult, typeId1); + } + + if (typeName2) + { + aclresult = pg_type_aclcheck(typeId2, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error_type(aclresult, typeId2); + } + /* * Look up the operator's underlying function. */ @@ -198,7 +209,7 @@ DefineOperator(List *names, List *parameters) functionOid = LookupFuncName(functionName, nargs, typeId, false); /* - * We require EXECUTE rights for the function. This isn't strictly + * 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. */ @@ -207,138 +218,119 @@ DefineOperator(List *names, List *parameters) aclcheck_error(aclresult, ACL_KIND_PROC, NameListToString(functionName)); + rettype = get_func_rettype(functionOid); + aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error_type(aclresult, rettype); + /* - * Look up restriction estimator if specified + * Look up restriction and join estimators if specified */ if (restrictionName) - { - 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 \"float8\"", - NameListToString(restrictionName)))); - - /* Require EXECUTE rights for the estimator */ - aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - NameListToString(restrictionName)); - } + restrictionOid = ValidateRestrictionEstimator(restrictionName); else restrictionOid = InvalidOid; - - /* - * Look up join estimator if specified - */ if (joinName) - { - 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. Try the - * preferred form first. - */ - joinOid = LookupFuncName(joinName, 5, typeId, true); - if (!OidIsValid(joinOid)) - joinOid = LookupFuncName(joinName, 4, typeId, true); - /* 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 \"float8\"", - NameListToString(joinName)))); - - /* Require EXECUTE rights for the estimator */ - aclresult = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - NameListToString(joinName)); - } + joinOid = ValidateJoinEstimator(joinName); else joinOid = InvalidOid; /* * now have OperatorCreate do all the work.. */ - 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. procedure */ - joinOid, /* optional join sel. procedure name */ - canMerge, /* operator merges */ - canHash); /* operator hashes */ + 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. procedure */ + joinOid, /* optional join sel. procedure name */ + canMerge, /* operator merges */ + canHash); /* operator hashes */ } - /* - * RemoveOperator - * Deletes an operator. + * Look up a restriction estimator function ny name, and verify that it has + * the correct signature and we have the permissions to attach it to an + * operator. */ -void -RemoveOperator(RemoveFuncStmt *stmt) +static Oid +ValidateRestrictionEstimator(List *restrictionName) { - List *operatorName = stmt->name; - TypeName *typeName1 = (TypeName *) linitial(stmt->args); - TypeName *typeName2 = (TypeName *) lsecond(stmt->args); - Oid operOid; - HeapTuple tup; - ObjectAddress object; + Oid typeId[4]; + Oid restrictionOid; + AclResult aclresult; - Assert(list_length(stmt->args) == 2); - operOid = LookupOperNameTypeNames(NULL, operatorName, - typeName1, typeName2, - stmt->missing_ok, -1); + typeId[0] = INTERNALOID; /* PlannerInfo */ + typeId[1] = OIDOID; /* operator OID */ + typeId[2] = INTERNALOID; /* args list */ + typeId[3] = INT4OID; /* varRelid */ - if (stmt->missing_ok && !OidIsValid(operOid)) - { - ereport(NOTICE, - (errmsg("operator %s does not exist, skipping", - NameListToString(operatorName)))); - return; - } + restrictionOid = LookupFuncName(restrictionName, 4, typeId, false); - tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid)); - if (!HeapTupleIsValid(tup)) /* should not happen */ - elog(ERROR, "cache lookup failed for operator %u", operOid); + /* 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"))); - /* Permission check: must own operator or its namespace */ - if (!pg_oper_ownercheck(operOid, GetUserId()) && - !pg_namespace_ownercheck(((Form_pg_operator) GETSTRUCT(tup))->oprnamespace, - GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, - NameListToString(operatorName)); + /* Require EXECUTE rights for the estimator */ + aclresult = pg_proc_aclcheck(restrictionOid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + NameListToString(restrictionName)); - ReleaseSysCache(tup); + return restrictionOid; +} + +/* + * Look up a join estimator function ny 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; + AclResult aclresult; + + typeId[0] = INTERNALOID; /* PlannerInfo */ + typeId[1] = OIDOID; /* operator OID */ + typeId[2] = INTERNALOID; /* args list */ + typeId[3] = INT2OID; /* jointype */ + typeId[4] = INTERNALOID; /* SpecialJoinInfo */ /* - * Do the deletion + * As of Postgres 8.4, the preferred signature for join estimators has 5 + * arguments, but we still allow the old 4-argument form. Try the + * preferred form first. */ - object.classId = OperatorRelationId; - object.objectId = operOid; - object.objectSubId = 0; + joinOid = LookupFuncName(joinName, 5, typeId, true); + if (!OidIsValid(joinOid)) + joinOid = LookupFuncName(joinName, 4, typeId, true); + /* 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 = pg_proc_aclcheck(joinOid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + NameListToString(joinName)); - performDeletion(&object, stmt->behavior); + return joinOid; } /* @@ -349,12 +341,32 @@ RemoveOperatorById(Oid operOid) { Relation relation; HeapTuple tup; + Form_pg_operator op; relation = heap_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); + } + } simple_heap_delete(relation, &tup->t_self); @@ -363,92 +375,157 @@ RemoveOperatorById(Oid operOid) heap_close(relation, RowExclusiveLock); } -void -AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId) -{ - Relation rel; - - rel = heap_open(OperatorRelationId, RowExclusiveLock); - - AlterOperatorOwner_internal(rel, operOid, newOwnerId); - - heap_close(rel, NoLock); -} - /* - * change operator owner + * AlterOperator + * routine implementing ALTER OPERATOR SET (option = ...). + * + * Currently, only RESTRICT and JOIN estimator functions can be changed. */ -void -AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2, - Oid newOwnerId) -{ - Oid operOid; - Relation rel; - - rel = heap_open(OperatorRelationId, RowExclusiveLock); - - operOid = LookupOperNameTypeNames(NULL, name, - typeName1, typeName2, - false, -1); - - AlterOperatorOwner_internal(rel, operOid, newOwnerId); - - heap_close(rel, NoLock); -} - -static void -AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) +ObjectAddress +AlterOperator(AlterOperatorStmt *stmt) { + ObjectAddress address; + Oid oprId; + Relation catalog; HeapTuple tup; - AclResult aclresult; 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. procedure */ + bool updateRestriction = false; + Oid restrictionOid; + List *joinName = NIL; /* optional join sel. procedure */ + bool updateJoin = false; + Oid joinOid; - Assert(RelationGetRelid(rel) == OperatorRelationId); - - tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(operOid)); - if (!HeapTupleIsValid(tup)) /* should not happen */ - elog(ERROR, "cache lookup failed for operator %u", operOid); - + /* Look up the operator */ + oprId = LookupOperNameTypeNames(NULL, stmt->opername, + (TypeName *) linitial(stmt->operargs), + (TypeName *) lsecond(stmt->operargs), + false, -1); + catalog = heap_open(OperatorRelationId, RowExclusiveLock); + tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(oprId)); + if (tup == NULL) + elog(ERROR, "cache lookup failed for operator %u", oprId); oprForm = (Form_pg_operator) GETSTRUCT(tup); - /* - * If the new owner is the same as the existing owner, consider the - * command to have succeeded. This is for dump restoration purposes. - */ - if (oprForm->oprowner != newOwnerId) + /* Process options */ + foreach(pl, stmt->options) { - /* Superusers can always do it */ - if (!superuser()) + DefElem *defel = (DefElem *) lfirst(pl); + List *param; + + if (defel->arg == NULL) + param = NIL; /* NONE, removes the function */ + else + param = defGetQualifiedName(defel); + + if (pg_strcasecmp(defel->defname, "restrict") == 0) { - /* Otherwise, must be owner of the existing object */ - if (!pg_oper_ownercheck(operOid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, - NameStr(oprForm->oprname)); - - /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), newOwnerId); - - /* New owner must have CREATE privilege on namespace */ - aclresult = pg_namespace_aclcheck(oprForm->oprnamespace, - newOwnerId, - ACL_CREATE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(oprForm->oprnamespace)); + restrictionName = param; + updateRestriction = true; + } + else if (pg_strcasecmp(defel->defname, "join") == 0) + { + joinName = param; + updateJoin = true; } /* - * Modify the owner --- okay to scribble on tup because it's a copy + * The rest of the options that CREATE accepts cannot be changed. + * Check for them so that we can give a meaningful error message. */ - oprForm->oprowner = newOwnerId; + else if (pg_strcasecmp(defel->defname, "leftarg") == 0 || + pg_strcasecmp(defel->defname, "rightarg") == 0 || + pg_strcasecmp(defel->defname, "procedure") == 0 || + pg_strcasecmp(defel->defname, "commutator") == 0 || + pg_strcasecmp(defel->defname, "negator") == 0 || + pg_strcasecmp(defel->defname, "hashes") == 0 || + pg_strcasecmp(defel->defname, "merges") == 0) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("operator attribute \"%s\" can not 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 (!pg_oper_ownercheck(oprId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, + NameStr(oprForm->oprname)); + + /* + * Look up restriction and join estimators if specified + */ + if (restrictionName) + restrictionOid = ValidateRestrictionEstimator(restrictionName); + else + restrictionOid = InvalidOid; + if (joinName) + joinOid = ValidateJoinEstimator(joinName); + else + joinOid = InvalidOid; - simple_heap_update(rel, &tup->t_self, tup); + /* Perform additional checks, like OperatorCreate does */ + if (!(OidIsValid(oprForm->oprleft) && OidIsValid(oprForm->oprright))) + { + /* 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"))); + } - CatalogUpdateIndexes(rel, tup); + if (oprForm->oprresult != BOOLOID) + { + if (OidIsValid(restrictionOid)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can have restriction selectivity"))); + if (OidIsValid(joinOid)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("only boolean operators can have join selectivity"))); + } - /* Update owner dependency reference */ - changeDependencyOnOwner(OperatorRelationId, operOid, newOwnerId); + /* 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] = restrictionOid; + } + if (updateJoin) + { + replaces[Anum_pg_operator_oprjoin - 1] = true; + values[Anum_pg_operator_oprjoin - 1] = joinOid; + } + + tup = heap_modify_tuple(tup, RelationGetDescr(catalog), + values, nulls, replaces); + + simple_heap_update(catalog, &tup->t_self, tup); + CatalogUpdateIndexes(catalog, tup); + + address = makeOperatorDependencies(tup, true); + + InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0); + + heap_close(catalog, NoLock); - heap_freetuple(tup); + return address; }