X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Fcatalog%2Fpg_operator.c;h=370683823fa96c6ff150cfc4ce8f78cdbc5f5e8e;hb=c7b8998ebbf310a156aa38022555a24d98fdbfb4;hp=53b250fe3fabb7c26769a0f935140d274d47cccd;hpb=bd272cace63effa23d68a6b344a07e326b6a41e2;p=postgresql diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 53b250fe3f..370683823f 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -3,11 +3,12 @@ * pg_operator.c * routines to support manipulation of the pg_operator relation * - * Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.42 1999/09/18 19:06:33 tgl Exp $ + * src/backend/catalog/pg_operator.c * * NOTES * these routines moved here from commands/define.c and somewhat cleaned up. @@ -17,119 +18,136 @@ #include "postgres.h" #include "access/heapam.h" -#include "catalog/catname.h" +#include "access/htup_details.h" +#include "access/xact.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/namespace.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_namespace.h" #include "catalog/pg_operator.h" +#include "catalog/pg_operator_fn.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "miscadmin.h" -#include "parser/parse_func.h" +#include "parser/parse_oper.h" +#include "utils/acl.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" #include "utils/syscache.h" -static Oid OperatorGetWithOpenRelation(Relation pg_operator_desc, - const char *operatorName, - Oid leftObjectId, - Oid rightObjectId, - bool *defined); - -static Oid OperatorGet(char *operatorName, - char *leftTypeName, - char *rightTypeName, +static Oid OperatorGet(const char *operatorName, + Oid operatorNamespace, + Oid leftObjectId, + Oid rightObjectId, bool *defined); -static Oid OperatorShellMakeWithOpenRelation(Relation pg_operator_desc, - char *operatorName, - Oid leftObjectId, - Oid rightObjectId); - -static Oid OperatorShellMake(char *operatorName, - char *leftTypeName, - char *rightTypeName); - -static void OperatorDef(char *operatorName, - char *leftTypeName, - char *rightTypeName, - char *procedureName, - uint16 precedence, - bool isLeftAssociative, - char *commutatorName, - char *negatorName, - char *restrictionName, - char *oinName, - bool canHash, - char *leftSortName, - char *rightSortName); - -static void OperatorUpd(Oid baseId, Oid commId, Oid negId); - -/* ---------------------------------------------------------------- - * OperatorGetWithOpenRelation +static Oid OperatorLookup(List *operatorName, + Oid leftObjectId, + Oid rightObjectId, + bool *defined); + +static Oid OperatorShellMake(const char *operatorName, + Oid operatorNamespace, + Oid leftTypeId, + Oid rightTypeId); + +static Oid get_other_operator(List *otherOp, + Oid otherLeftTypeId, Oid otherRightTypeId, + const char *operatorName, Oid operatorNamespace, + Oid leftTypeId, Oid rightTypeId, + bool isCommutator); + + +/* + * Check whether a proposed operator name is legal + * + * This had better match the behavior of parser/scan.l! * - * preforms a scan on pg_operator for an operator tuple - * with given name and left/right type oids. - * ---------------------------------------------------------------- - * pg_operator_desc -- reldesc for pg_operator - * operatorName -- name of operator to fetch - * leftObjectId -- left data type oid of operator to fetch - * rightObjectId -- right data type oid of operator to fetch - * defined -- set TRUE if defined (not a shell) + * We need this because the parser is not smart enough to check that + * the arguments of CREATE OPERATOR's COMMUTATOR, NEGATOR, etc clauses + * are operator names rather than some other lexical entity. */ -static Oid -OperatorGetWithOpenRelation(Relation pg_operator_desc, - const char *operatorName, - Oid leftObjectId, - Oid rightObjectId, - bool *defined) +static bool +validOperatorName(const char *name) { - HeapScanDesc pg_operator_scan; - Oid operatorObjectId; - HeapTuple tup; + size_t len = strlen(name); - static ScanKeyData opKey[3] = { - {0, Anum_pg_operator_oprname, F_NAMEEQ}, - {0, Anum_pg_operator_oprleft, F_OIDEQ}, - {0, Anum_pg_operator_oprright, F_OIDEQ}, - }; - - fmgr_info(F_NAMEEQ, &opKey[0].sk_func); - fmgr_info(F_OIDEQ, &opKey[1].sk_func); - fmgr_info(F_OIDEQ, &opKey[2].sk_func); - opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs; - opKey[1].sk_nargs = opKey[1].sk_func.fn_nargs; - opKey[2].sk_nargs = opKey[2].sk_func.fn_nargs; - - /* ---------------- - * form scan key - * ---------------- - */ - opKey[0].sk_argument = PointerGetDatum(operatorName); - opKey[1].sk_argument = ObjectIdGetDatum(leftObjectId); - opKey[2].sk_argument = ObjectIdGetDatum(rightObjectId); + /* Can't be empty or too long */ + if (len == 0 || len >= NAMEDATALEN) + return false; - /* ---------------- - * begin the scan - * ---------------- - */ - pg_operator_scan = heap_beginscan(pg_operator_desc, - 0, - SnapshotSelf, /* no cache? */ - 3, - opKey); - - /* ---------------- - * fetch the operator tuple, if it exists, and determine - * the proper return oid value. - * ---------------- + /* Can't contain any invalid characters */ + /* Test string here should match op_chars in scan.l */ + if (strspn(name, "~!@#^&|`?+-*/%<>=") != len) + return false; + + /* Can't contain slash-star or dash-dash (comment starts) */ + if (strstr(name, "/*") || strstr(name, "--")) + return false; + + /* + * For SQL standard compatibility, '+' and '-' cannot be the last char of + * a multi-char operator unless the operator contains chars that are not + * in SQL operators. The idea is to lex '=-' as two operators, but not to + * forbid operator names like '?-' that could not be sequences of standard + * SQL operators. */ - tup = heap_getnext(pg_operator_scan, 0); + if (len > 1 && + (name[len - 1] == '+' || + name[len - 1] == '-')) + { + int ic; + for (ic = len - 2; ic >= 0; ic--) + { + if (strchr("~!@#^&|`?%", name[ic])) + break; + } + if (ic < 0) + return false; /* nope, not valid */ + } + + /* != isn't valid either, because parser will convert it to <> */ + if (strcmp(name, "!=") == 0) + return false; + + return true; +} + + +/* + * OperatorGet + * + * finds an operator given an exact specification (name, namespace, + * left and right type IDs). + * + * *defined is set TRUE if defined (not a shell) + */ +static Oid +OperatorGet(const char *operatorName, + Oid operatorNamespace, + Oid leftObjectId, + Oid rightObjectId, + bool *defined) +{ + HeapTuple tup; + Oid operatorObjectId; + + tup = SearchSysCache4(OPERNAMENSP, + PointerGetDatum(operatorName), + ObjectIdGetDatum(leftObjectId), + ObjectIdGetDatum(rightObjectId), + ObjectIdGetDatum(operatorNamespace)); if (HeapTupleIsValid(tup)) { - regproc oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode; + RegProcedure oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode; - operatorObjectId = tup->t_data->t_oid; + operatorObjectId = HeapTupleGetOid(tup); *defined = RegProcedureIsValid(oprcode); + ReleaseSysCache(tup); } else { @@ -137,238 +155,162 @@ OperatorGetWithOpenRelation(Relation pg_operator_desc, *defined = false; } - /* ---------------- - * close the scan and return the oid. - * ---------------- - */ - heap_endscan(pg_operator_scan); - return operatorObjectId; } -/* ---------------------------------------------------------------- - * OperatorGet +/* + * OperatorLookup * - * finds the operator associated with the specified name - * and left and right type names. - * ---------------------------------------------------------------- + * looks up an operator given a possibly-qualified name and + * left and right type IDs. + * + * *defined is set TRUE if defined (not a shell) */ static Oid -OperatorGet(char *operatorName, - char *leftTypeName, - char *rightTypeName, - bool *defined) +OperatorLookup(List *operatorName, + Oid leftObjectId, + Oid rightObjectId, + bool *defined) { - Relation pg_operator_desc; - Oid operatorObjectId; - Oid leftObjectId = InvalidOid; - Oid rightObjectId = InvalidOid; - bool leftDefined = false; - bool rightDefined = false; - - /* ---------------- - * look up the operator data types. - * - * Note: types must be defined before operators - * ---------------- - */ - if (leftTypeName) - { - leftObjectId = TypeGet(leftTypeName, &leftDefined); - - if (!OidIsValid(leftObjectId) || !leftDefined) - elog(ERROR, "OperatorGet: left type '%s' nonexistent", - leftTypeName); - } + RegProcedure oprcode; - if (rightTypeName) + operatorObjectId = LookupOperName(NULL, operatorName, + leftObjectId, rightObjectId, + true, -1); + if (!OidIsValid(operatorObjectId)) { - rightObjectId = TypeGet(rightTypeName, &rightDefined); - - if (!OidIsValid(rightObjectId) || !rightDefined) - elog(ERROR, "OperatorGet: right type '%s' nonexistent", - rightTypeName); + *defined = false; + return InvalidOid; } - if (!((OidIsValid(leftObjectId) && leftDefined) || - (OidIsValid(rightObjectId) && rightDefined))) - elog(ERROR, "OperatorGet: must have at least one argument type"); - - /* ---------------- - * open the pg_operator relation - * ---------------- - */ - pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock); - - /* ---------------- - * get the oid for the operator with the appropriate name - * and left/right types. - * ---------------- - */ - operatorObjectId = OperatorGetWithOpenRelation(pg_operator_desc, - operatorName, - leftObjectId, - rightObjectId, - defined); - - /* ---------------- - * close the relation and return the operator oid. - * ---------------- - */ - heap_close(pg_operator_desc, AccessShareLock); + oprcode = get_opcode(operatorObjectId); + *defined = RegProcedureIsValid(oprcode); return operatorObjectId; } -/* ---------------------------------------------------------------- - * OperatorShellMakeWithOpenRelation - * - * ---------------------------------------------------------------- + +/* + * OperatorShellMake + * Make a "shell" entry for a not-yet-existing operator. */ static Oid -OperatorShellMakeWithOpenRelation(Relation pg_operator_desc, - char *operatorName, - Oid leftObjectId, - Oid rightObjectId) +OperatorShellMake(const char *operatorName, + Oid operatorNamespace, + Oid leftTypeId, + Oid rightTypeId) { + Relation pg_operator_desc; + Oid operatorObjectId; int i; HeapTuple tup; Datum values[Natts_pg_operator]; - char nulls[Natts_pg_operator]; - Oid operatorObjectId; + bool nulls[Natts_pg_operator]; NameData oname; TupleDesc tupDesc; - /* ---------------- - * initialize our *nulls and *values arrays - * ---------------- + /* + * validate operator name + */ + if (!validOperatorName(operatorName)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("\"%s\" is not a valid operator name", + operatorName))); + + /* + * initialize our *nulls and *values arrays */ for (i = 0; i < Natts_pg_operator; ++i) { - nulls[i] = ' '; - values[i] = (Datum) NULL; /* redundant, but safe */ + nulls[i] = false; + values[i] = (Datum) NULL; /* redundant, but safe */ } - /* ---------------- - * initialize *values with the operator name and input data types. - * Note that oprcode is set to InvalidOid, indicating it's a shell. - * ---------------- + /* + * initialize values[] with the operator name and input data types. Note + * that oprcode is set to InvalidOid, indicating it's a shell. */ - i = 0; namestrcpy(&oname, operatorName); - values[i++] = NameGetDatum(&oname); - values[i++] = Int32GetDatum(GetUserId()); - values[i++] = (Datum) (uint16) 0; - values[i++] = (Datum) 'b'; /* assume it's binary */ - values[i++] = (Datum) (bool) 0; - values[i++] = (Datum) (bool) 0; - values[i++] = ObjectIdGetDatum(leftObjectId); /* <-- left oid */ - values[i++] = ObjectIdGetDatum(rightObjectId); /* <-- right oid */ - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - values[i++] = ObjectIdGetDatum(InvalidOid); - - /* ---------------- - * create a new operator tuple - * ---------------- + values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname); + values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace); + values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId()); + values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); + values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(false); + values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false); + values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId); + values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId); + values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid); + + /* + * open pg_operator */ + pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); tupDesc = pg_operator_desc->rd_att; - tup = heap_formtuple(tupDesc, - values, - nulls); - - /* ---------------- - * insert our "shell" operator tuple and - * close the relation - * ---------------- + /* + * create a new operator tuple */ - heap_insert(pg_operator_desc, tup); - operatorObjectId = tup->t_data->t_oid; + tup = heap_form_tuple(tupDesc, values, nulls); - /* ---------------- - * free the tuple and return the operator oid - * ---------------- + /* + * insert our "shell" operator tuple */ - pfree(tup); - - return operatorObjectId; -} - -/* ---------------------------------------------------------------- - * OperatorShellMake - * - * Specify operator name and left and right type names, - * fill an operator struct with this info and NULL's, - * call heap_insert and return the Oid - * to the caller. - * ---------------------------------------------------------------- - */ -static Oid -OperatorShellMake(char *operatorName, - char *leftTypeName, - char *rightTypeName) -{ - Relation pg_operator_desc; - Oid operatorObjectId; + operatorObjectId = CatalogTupleInsert(pg_operator_desc, tup); - Oid leftObjectId = InvalidOid; - Oid rightObjectId = InvalidOid; - bool leftDefined = false; - bool rightDefined = false; + /* Add dependencies for the entry */ + makeOperatorDependencies(tup, false); - /* ---------------- - * get the left and right type oid's for this operator - * ---------------- - */ - if (leftTypeName) - leftObjectId = TypeGet(leftTypeName, &leftDefined); - - if (rightTypeName) - rightObjectId = TypeGet(rightTypeName, &rightDefined); + heap_freetuple(tup); - if (!((OidIsValid(leftObjectId) && leftDefined) || - (OidIsValid(rightObjectId) && rightDefined))) - elog(ERROR, "OperatorShellMake: no valid argument types??"); + /* Post creation hook for new shell operator */ + InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0); - /* ---------------- - * open pg_operator - * ---------------- + /* + * Make sure the tuple is visible for subsequent lookups/updates. */ - pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock); + CommandCounterIncrement(); - /* ---------------- - * add a "shell" operator tuple to the operator relation - * and recover the shell tuple's oid. - * ---------------- - */ - operatorObjectId = OperatorShellMakeWithOpenRelation(pg_operator_desc, - operatorName, - leftObjectId, - rightObjectId); - /* ---------------- - * close the operator relation and return the oid. - * ---------------- + /* + * close the operator relation and return the oid. */ heap_close(pg_operator_desc, RowExclusiveLock); return operatorObjectId; } -/* -------------------------------- - * OperatorDef +/* + * OperatorCreate + * + * "X" indicates an optional argument (i.e. one that can be NULL or 0) + * operatorName name for new operator + * operatorNamespace namespace for new operator + * leftTypeId X left type ID + * rightTypeId X right type ID + * procedureId procedure ID for operator + * commutatorName X commutator operator + * negatorName X negator operator + * restrictionId X restriction selectivity procedure ID + * joinId X join selectivity procedure ID + * canMerge merge join can be used with this operator + * canHash hash join can be used with this operator + * + * The caller should have validated properties and permissions for the + * objects passed as OID references. We must handle the commutator and + * negator operator references specially, however, since those need not + * exist beforehand. * * This routine gets complicated because it allows the user to * specify operators that do not exist. For example, if operator * "op" is being defined, the negator operator "negop" and the * commutator "commop" can also be defined without specifying - * any information other than their names. Since in order to + * any information other than their names. Since in order to * add "op" to the PG_OPERATOR catalog, all the Oid's for these * operators must be placed in the fields of "op", a forward * declaration is done on the commutator and negator operators. @@ -377,668 +319,546 @@ OperatorShellMake(char *operatorName, * information about the operator (just its name and types). * Forward declaration is used only for this purpose, it is * not available to the user as it is for type definition. - * - * Algorithm: - * - * check if operator already defined - * if so, but oprcode is null, save the Oid -- we are filling in a shell - * otherwise error - * get the attribute types from relation descriptor for pg_operator - * assign values to the fields of the operator: - * operatorName - * owner id (simply the user id of the caller) - * precedence - * operator "kind" either "b" for binary or "l" for left unary - * isLeftAssociative boolean - * canHash boolean - * leftTypeObjectId -- type must already be defined - * rightTypeObjectId -- this is optional, enter ObjectId=0 if none specified - * resultType -- defer this, since it must be determined from - * the pg_procedure catalog - * commutatorObjectId -- if this is NULL, enter ObjectId=0 - * else if this already exists, enter it's ObjectId - * else if this does not yet exist, and is not - * the same as the main operatorName, then create - * a shell and enter the new ObjectId - * else if this does not exist but IS the same - * name & types as the main operator, set the ObjectId=0. - * (We are creating a self-commutating operator.) - * The link will be fixed later by OperatorUpd. - * negatorObjectId -- same as for commutatorObjectId - * leftSortObjectId -- same as for commutatorObjectId - * rightSortObjectId -- same as for commutatorObjectId - * operatorProcedure -- must access the pg_procedure catalog to get the - * ObjectId of the procedure that actually does the operator - * actions this is required. Do an amgetattr to find out the - * return type of the procedure - * restrictionProcedure -- must access the pg_procedure catalog to get - * the ObjectId but this is optional - * joinProcedure -- same as restrictionProcedure - * now either insert or replace the operator into the pg_operator catalog - * if the operator shell is being filled in - * access the catalog in order to get a valid buffer - * create a tuple using ModifyHeapTuple - * get the t_self from the modified tuple and call RelationReplaceHeapTuple - * else if a new operator is being created - * create a tuple using heap_formtuple - * call heap_insert - * -------------------------------- - * "X" indicates an optional argument (i.e. one that can be NULL) - * operatorName; -- operator name - * leftTypeName; -- X left type name - * rightTypeName; -- X right type name - * procedureName; -- procedure name for operator code - * precedence; -- operator precedence - * isLeftAssociative; -- operator is left associative? - * commutatorName; -- X commutator operator name - * negatorName; -- X negator operator name - * restrictionName; -- X restriction sel. procedure name - * joinName; -- X join sel. procedure name - * canHash; -- can hash join be used with operator? - * leftSortName; -- X left sort operator (for merge join) - * rightSortName; -- X right sort operator (for merge join) */ -static void -OperatorDef(char *operatorName, - char *leftTypeName, - char *rightTypeName, - char *procedureName, - uint16 precedence, - bool isLeftAssociative, - char *commutatorName, - char *negatorName, - char *restrictionName, - char *joinName, - bool canHash, - char *leftSortName, - char *rightSortName) +ObjectAddress +OperatorCreate(const char *operatorName, + Oid operatorNamespace, + Oid leftTypeId, + Oid rightTypeId, + Oid procedureId, + List *commutatorName, + List *negatorName, + Oid restrictionId, + Oid joinId, + bool canMerge, + bool canHash) { - int i, - j; Relation pg_operator_desc; - - HeapScanDesc pg_operator_scan; HeapTuple tup; - char nulls[Natts_pg_operator]; - char replaces[Natts_pg_operator]; + bool isUpdate; + bool nulls[Natts_pg_operator]; + bool replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; Oid operatorObjectId; bool operatorAlreadyDefined; - Oid leftTypeId = InvalidOid; - Oid rightTypeId = InvalidOid; - Oid commutatorId = InvalidOid; - Oid negatorId = InvalidOid; - bool leftDefined = false; - bool rightDefined = false; + Oid operResultType; + Oid commutatorId, + negatorId; bool selfCommutator = false; - char *name[4]; - Oid typeId[8]; - int nargs; NameData oname; - TupleDesc tupDesc; - - static ScanKeyData opKey[3] = { - {0, Anum_pg_operator_oprname, F_NAMEEQ}, - {0, Anum_pg_operator_oprleft, F_OIDEQ}, - {0, Anum_pg_operator_oprright, F_OIDEQ}, - }; - - fmgr_info(F_NAMEEQ, &opKey[0].sk_func); - fmgr_info(F_OIDEQ, &opKey[1].sk_func); - fmgr_info(F_OIDEQ, &opKey[2].sk_func); - opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs; - opKey[1].sk_nargs = opKey[1].sk_func.fn_nargs; - opKey[2].sk_nargs = opKey[2].sk_func.fn_nargs; - - operatorObjectId = OperatorGet(operatorName, - leftTypeName, - rightTypeName, - &operatorAlreadyDefined); - - if (operatorAlreadyDefined) - elog(ERROR, "OperatorDef: operator \"%s\" already defined", - operatorName); + int i; + ObjectAddress address; /* - * At this point, if operatorObjectId is not InvalidOid then we are - * filling in a previously-created shell. + * Sanity checks */ + if (!validOperatorName(operatorName)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("\"%s\" is not a valid operator name", + operatorName))); - /* ---------------- - * look up the operator data types. - * - * Note: types must be defined before operators - * ---------------- - */ - if (leftTypeName) + if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId))) { - leftTypeId = TypeGet(leftTypeName, &leftDefined); - - if (!OidIsValid(leftTypeId) || !leftDefined) - elog(ERROR, "OperatorDef: left type '%s' nonexistent", - leftTypeName); + /* 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"))); } - if (rightTypeName) - { - rightTypeId = TypeGet(rightTypeName, &rightDefined); + operResultType = get_func_rettype(procedureId); - if (!OidIsValid(rightTypeId) || !rightDefined) - elog(ERROR, "OperatorDef: right type '%s' nonexistent", - rightTypeName); + 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"))); } - if (!((OidIsValid(leftTypeId) && leftDefined) || - (OidIsValid(rightTypeId) && rightDefined))) - elog(ERROR, "OperatorDef: must have at least one argument type"); + operatorObjectId = OperatorGet(operatorName, + operatorNamespace, + leftTypeId, + rightTypeId, + &operatorAlreadyDefined); - for (i = 0; i < Natts_pg_operator; ++i) - { - values[i] = (Datum) NULL; - replaces[i] = 'r'; - nulls[i] = ' '; - } + if (operatorAlreadyDefined) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_FUNCTION), + errmsg("operator %s already exists", + operatorName))); - /* ---------------- - * Look up registered procedures -- find the return type - * of procedureName to place in "result" field. - * Do this before shells are created so we don't - * have to worry about deleting them later. - * ---------------- + /* + * At this point, if operatorObjectId is not InvalidOid then we are + * filling in a previously-created shell. Insist that the user own any + * such shell. */ - MemSet(typeId, 0, 8 * sizeof(Oid)); - if (!leftTypeName) - { - typeId[0] = rightTypeId; - nargs = 1; - } - else if (!rightTypeName) - { - typeId[0] = leftTypeId; - nargs = 1; - } - else - { - typeId[0] = leftTypeId; - typeId[1] = rightTypeId; - nargs = 2; - } - tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(procedureName), - Int32GetDatum(nargs), - PointerGetDatum(typeId), - 0); - - if (!HeapTupleIsValid(tup)) - func_error("OperatorDef", procedureName, nargs, typeId, NULL); - - values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(tup->t_data->t_oid); - values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(((Form_pg_proc) - GETSTRUCT(tup))->prorettype); - - /* ---------------- - * find restriction - * ---------------- + if (OidIsValid(operatorObjectId) && + !pg_oper_ownercheck(operatorObjectId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, + operatorName); + + /* + * Set up the other operators. If they do not currently exist, create + * shells in order to get ObjectId's. */ - if (restrictionName) - { /* optional */ - MemSet(typeId, 0, 8 * sizeof(Oid)); - typeId[0] = OIDOID; /* operator OID */ - typeId[1] = OIDOID; /* relation OID */ - typeId[2] = INT2OID; /* attribute number */ - typeId[3] = 0; /* value - can be any type */ - typeId[4] = INT4OID; /* flags - left or right selectivity */ - tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(restrictionName), - Int32GetDatum(5), - PointerGetDatum(typeId), - 0); - if (!HeapTupleIsValid(tup)) - func_error("OperatorDef", restrictionName, 5, typeId, NULL); - values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(tup->t_data->t_oid); + if (commutatorName) + { + /* commutator has reversed arg types */ + commutatorId = get_other_operator(commutatorName, + rightTypeId, leftTypeId, + operatorName, operatorNamespace, + leftTypeId, rightTypeId, + true); + + /* Permission check: must own other operator */ + if (OidIsValid(commutatorId) && + !pg_oper_ownercheck(commutatorId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, + NameListToString(commutatorName)); + + /* + * self-linkage to this operator; will fix below. Note that only + * self-linkage for commutation makes sense. + */ + if (!OidIsValid(commutatorId)) + selfCommutator = true; } else - values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid); + commutatorId = InvalidOid; - /* ---------------- - * find join - only valid for binary operators - * ---------------- - */ - if (joinName) - { /* optional */ - MemSet(typeId, 0, 8 * sizeof(Oid)); - typeId[0] = OIDOID; /* operator OID */ - typeId[1] = OIDOID; /* relation OID 1 */ - typeId[2] = INT2OID; /* attribute number 1 */ - typeId[3] = OIDOID; /* relation OID 2 */ - typeId[4] = INT2OID; /* attribute number 2 */ - - tup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(joinName), - Int32GetDatum(5), - PointerGetDatum(typeId), - 0); - if (!HeapTupleIsValid(tup)) - func_error("OperatorDef", joinName, 5, typeId, NULL); - - values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(tup->t_data->t_oid); + if (negatorName) + { + /* negator has same arg types */ + negatorId = get_other_operator(negatorName, + leftTypeId, rightTypeId, + operatorName, operatorNamespace, + leftTypeId, rightTypeId, + false); + + /* Permission check: must own other operator */ + if (OidIsValid(negatorId) && + !pg_oper_ownercheck(negatorId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, + NameListToString(negatorName)); } else - values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid); - - /* ---------------- - * set up values in the operator tuple - * ---------------- - */ - i = 0; - namestrcpy(&oname, operatorName); - values[i++] = NameGetDatum(&oname); - values[i++] = Int32GetDatum(GetUserId()); - values[i++] = UInt16GetDatum(precedence); - values[i++] = leftTypeName ? (rightTypeName ? 'b' : 'r') : 'l'; - values[i++] = Int8GetDatum(isLeftAssociative); - values[i++] = Int8GetDatum(canHash); - values[i++] = ObjectIdGetDatum(leftTypeId); - values[i++] = ObjectIdGetDatum(rightTypeId); - - ++i; /* Skip "oprresult", it was filled in - * above */ + negatorId = InvalidOid; /* - * Set up the other operators. If they do not currently exist, create - * shells in order to get ObjectId's. + * set up values in the operator tuple */ - name[0] = commutatorName; - name[1] = negatorName; - name[2] = leftSortName; - name[3] = rightSortName; - for (j = 0; j < 4; ++j) + for (i = 0; i < Natts_pg_operator; ++i) { - if (name[j]) - { - char *otherLeftTypeName = NULL; - char *otherRightTypeName = NULL; - Oid otherLeftTypeId = InvalidOid; - Oid otherRightTypeId = InvalidOid; - Oid other_oid = InvalidOid; - bool otherDefined = false; - - switch (j) - { - case 0: /* commutator has reversed arg types */ - otherLeftTypeName = rightTypeName; - otherRightTypeName = leftTypeName; - otherLeftTypeId = rightTypeId; - otherRightTypeId = leftTypeId; - other_oid = OperatorGet(name[j], - otherLeftTypeName, - otherRightTypeName, - &otherDefined); - commutatorId = other_oid; - break; - case 1: /* negator has same arg types */ - otherLeftTypeName = leftTypeName; - otherRightTypeName = rightTypeName; - otherLeftTypeId = leftTypeId; - otherRightTypeId = rightTypeId; - other_oid = OperatorGet(name[j], - otherLeftTypeName, - otherRightTypeName, - &otherDefined); - negatorId = other_oid; - break; - case 2: /* left sort op takes left-side data type */ - otherLeftTypeName = leftTypeName; - otherRightTypeName = leftTypeName; - otherLeftTypeId = leftTypeId; - otherRightTypeId = leftTypeId; - other_oid = OperatorGet(name[j], - otherLeftTypeName, - otherRightTypeName, - &otherDefined); - break; - case 3: /* right sort op takes right-side data - * type */ - otherLeftTypeName = rightTypeName; - otherRightTypeName = rightTypeName; - otherLeftTypeId = rightTypeId; - otherRightTypeId = rightTypeId; - other_oid = OperatorGet(name[j], - otherLeftTypeName, - otherRightTypeName, - &otherDefined); - break; - } - - if (OidIsValid(other_oid)) - { - /* other op already in catalogs */ - values[i++] = ObjectIdGetDatum(other_oid); - } - else if (strcmp(operatorName, name[j]) != 0 || - otherLeftTypeId != leftTypeId || - otherRightTypeId != rightTypeId) - { - /* not in catalogs, different from operator */ - other_oid = OperatorShellMake(name[j], - otherLeftTypeName, - otherRightTypeName); - if (!OidIsValid(other_oid)) - elog(ERROR, - "OperatorDef: can't create operator shell '%s'", - name[j]); - values[i++] = ObjectIdGetDatum(other_oid); - } - else - { - - /* - * self-linkage to this operator; will fix below. Note - * that only self-linkage for commutation makes sense. - */ - if (j != 0) - elog(ERROR, - "OperatorDef: operator can't be its own negator or sort op"); - selfCommutator = true; - values[i++] = ObjectIdGetDatum(InvalidOid); - } - } - else - { - /* other operator is omitted */ - values[i++] = ObjectIdGetDatum(InvalidOid); - } + values[i] = (Datum) NULL; + replaces[i] = true; + nulls[i] = false; } - /* last three fields were filled in above */ - - pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock); + namestrcpy(&oname, operatorName); + values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname); + values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace); + values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId()); + values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); + values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge); + values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash); + values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId); + values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId); + values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(operResultType); + values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorId); + values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorId); + values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(procedureId); + values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionId); + values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinId); + + pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); /* - * If we are adding to an operator shell, get its t_self + * If we are replacing an operator shell, update; else insert */ - if (operatorObjectId) { - opKey[0].sk_argument = PointerGetDatum(operatorName); - opKey[1].sk_argument = ObjectIdGetDatum(leftTypeId); - opKey[2].sk_argument = ObjectIdGetDatum(rightTypeId); - - pg_operator_scan = heap_beginscan(pg_operator_desc, - 0, - SnapshotSelf, /* no cache? */ - 3, - opKey); - - tup = heap_getnext(pg_operator_scan, 0); - if (HeapTupleIsValid(tup)) - { - tup = heap_modifytuple(tup, - pg_operator_desc, - values, - nulls, - replaces); - - setheapoverride(true); - heap_replace(pg_operator_desc, &tup->t_self, tup, NULL); - setheapoverride(false); - } - else - elog(ERROR, "OperatorDef: no operator %u", operatorObjectId); + isUpdate = true; + + tup = SearchSysCacheCopy1(OPEROID, + ObjectIdGetDatum(operatorObjectId)); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for operator %u", + operatorObjectId); - heap_endscan(pg_operator_scan); + tup = heap_modify_tuple(tup, + RelationGetDescr(pg_operator_desc), + values, + nulls, + replaces); + + CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup); } else { - tupDesc = pg_operator_desc->rd_att; - tup = heap_formtuple(tupDesc, values, nulls); + isUpdate = false; + + tup = heap_form_tuple(RelationGetDescr(pg_operator_desc), + values, nulls); - heap_insert(pg_operator_desc, tup); - operatorObjectId = tup->t_data->t_oid; + operatorObjectId = CatalogTupleInsert(pg_operator_desc, tup); } + /* Add dependencies for the entry */ + address = makeOperatorDependencies(tup, isUpdate); + + /* Post creation hook for new operator */ + InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0); + heap_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. This supports an alternate style of operator definition - * wherein the user first defines one operator without giving negator - * or commutator, then defines the other operator of the pair with the - * proper commutator or negator attribute. That style doesn't require - * creation of a shell, and it's the only style that worked right - * before Postgres version 6.5. This code also takes care of the - * situation where the new operator is its own commutator. + * operator(s) to point at this one, if they don't already have a link. + * This supports an alternative style of operator definition wherein the + * user first defines one operator without giving negator or commutator, + * then defines the other operator of the pair with the proper commutator + * or negator attribute. That style doesn't require creation of a shell, + * and it's the only style that worked right before Postgres version 6.5. + * This code also takes care of the situation where the new operator is + * its own commutator. */ if (selfCommutator) commutatorId = operatorObjectId; if (OidIsValid(commutatorId) || OidIsValid(negatorId)) - OperatorUpd(operatorObjectId, commutatorId, negatorId); + OperatorUpd(operatorObjectId, commutatorId, negatorId, false); + + return address; } -/* ---------------------------------------------------------------- - * OperatorUpd +/* + * Try to lookup another operator (commutator, etc) * - * For a given operator, look up its negator and commutator operators. - * If they are defined, but their negator and commutator fields - * (respectively) are empty, then use the new operator for neg or comm. - * This solves a problem for users who need to insert two new operators - * which are the negator or commutator of each other. - * ---------------------------------------------------------------- + * 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. */ -static void -OperatorUpd(Oid baseId, Oid commId, Oid negId) +static Oid +get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, + const char *operatorName, Oid operatorNamespace, + Oid leftTypeId, Oid rightTypeId, bool isCommutator) { - int i; - Relation pg_operator_desc; - HeapScanDesc pg_operator_scan; - HeapTuple tup; - char nulls[Natts_pg_operator]; - char replaces[Natts_pg_operator]; - Datum values[Natts_pg_operator]; - - static ScanKeyData opKey[1] = { - {0, ObjectIdAttributeNumber, F_OIDEQ}, - }; + Oid other_oid; + bool otherDefined; + char *otherName; + Oid otherNamespace; + AclResult aclresult; + + other_oid = OperatorLookup(otherOp, + otherLeftTypeId, + otherRightTypeId, + &otherDefined); + + if (OidIsValid(other_oid)) + { + /* other op already in catalogs */ + return other_oid; + } - fmgr_info(F_OIDEQ, &opKey[0].sk_func); - opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs; + otherNamespace = QualifiedNameGetCreationNamespace(otherOp, + &otherName); - for (i = 0; i < Natts_pg_operator; ++i) + if (strcmp(otherName, operatorName) == 0 && + otherNamespace == operatorNamespace && + otherLeftTypeId == leftTypeId && + otherRightTypeId == rightTypeId) { - values[i] = (Datum) NULL; - replaces[i] = ' '; - nulls[i] = ' '; + /* + * 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"))); + return InvalidOid; } - pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock); + /* not in catalogs, different from operator, so make shell */ - /* check and update the commutator, if necessary */ - opKey[0].sk_argument = ObjectIdGetDatum(commId); + aclresult = pg_namespace_aclcheck(otherNamespace, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(otherNamespace)); - pg_operator_scan = heap_beginscan(pg_operator_desc, - 0, - SnapshotSelf, /* no cache? */ - 1, - opKey); + other_oid = OperatorShellMake(otherName, + otherNamespace, + otherLeftTypeId, + otherRightTypeId); + return other_oid; +} - tup = heap_getnext(pg_operator_scan, 0); +/* + * OperatorUpd + * + * 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. + * + * 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 + * isDelete case is needed so as not to leave dangling OID links behind + * after dropping an operator. + */ +void +OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete) +{ + Relation pg_operator_desc; + HeapTuple tup; /* - * if the commutator and negator are the same operator, do one update. - * XXX this is probably useless code --- I doubt it ever makes sense - * for commutator and negator to be the same thing... + * If we're making an operator into its own commutator, then we need a + * command-counter increment here, since we've just inserted the tuple + * we're about to update. But when we're dropping an operator, we can + * skip this because we're at the beginning of the command. */ - if (commId == negId) + if (!isDelete) + CommandCounterIncrement(); + + /* Open the relation. */ + pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); + + /* Get a writable copy of the commutator's tuple. */ + if (OidIsValid(commId)) + tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(commId)); + else + tup = NULL; + + /* Update the commutator's tuple if need be. */ + if (HeapTupleIsValid(tup)) { - if (HeapTupleIsValid(tup)) + Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup); + 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. + */ + if (isDelete && t->oprcom == baseId) { - Form_pg_operator t; - - t = (Form_pg_operator) GETSTRUCT(tup); - if (!OidIsValid(t->oprcom) - || !OidIsValid(t->oprnegate)) - { - - if (!OidIsValid(t->oprnegate)) - { - values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId); - replaces[Anum_pg_operator_oprnegate - 1] = 'r'; - } - - if (!OidIsValid(t->oprcom)) - { - values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId); - replaces[Anum_pg_operator_oprcom - 1] = 'r'; - } - - tup = heap_modifytuple(tup, - pg_operator_desc, - values, - nulls, - replaces); - - setheapoverride(true); - heap_replace(pg_operator_desc, &tup->t_self, tup, NULL); - setheapoverride(false); - - } + t->oprcom = InvalidOid; + update_commutator = true; + } + else if (!isDelete && !OidIsValid(t->oprcom)) + { + t->oprcom = baseId; + update_commutator = true; } - heap_endscan(pg_operator_scan); - - heap_close(pg_operator_desc, RowExclusiveLock); - return; + /* If any columns were found to need modification, update tuple. */ + if (update_commutator) + { + CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup); + + /* + * Do CCI to make the updated tuple visible. We must do this in + * case the commutator is also the negator. (Which would be a + * logic error on the operator definer's part, but that's not a + * good reason to fail here.) We would need a CCI anyway in the + * deletion case for a self-commutator with no negator. + */ + CommandCounterIncrement(); + } } - /* if commutator and negator are different, do two updates */ + /* + * Similarly find and update the negator, if any. + */ + if (OidIsValid(negId)) + tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(negId)); + else + tup = NULL; - if (HeapTupleIsValid(tup) && - !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprcom))) + if (HeapTupleIsValid(tup)) { - values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId); - replaces[Anum_pg_operator_oprcom - 1] = 'r'; - tup = heap_modifytuple(tup, - pg_operator_desc, - values, - nulls, - replaces); - - setheapoverride(true); - heap_replace(pg_operator_desc, &tup->t_self, tup, NULL); - setheapoverride(false); - - values[Anum_pg_operator_oprcom - 1] = (Datum) NULL; - replaces[Anum_pg_operator_oprcom - 1] = ' '; + Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup); + 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. + */ + if (isDelete && t->oprnegate == baseId) + { + t->oprnegate = InvalidOid; + update_negator = true; + } + else if (!isDelete && !OidIsValid(t->oprnegate)) + { + t->oprnegate = baseId; + update_negator = true; + } + + /* If any columns were found to need modification, update tuple. */ + if (update_negator) + { + CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup); + + /* + * In the deletion case, do CCI to make the updated tuple visible. + * We must do this in case the operator is its own negator. (Which + * would be a logic error on the operator definer's part, but + * that's not a good reason to fail here.) + */ + if (isDelete) + CommandCounterIncrement(); + } } - heap_endscan(pg_operator_scan); + /* Close relation and release catalog lock. */ + heap_close(pg_operator_desc, RowExclusiveLock); +} - /* check and update the negator, if necessary */ - opKey[0].sk_argument = ObjectIdGetDatum(negId); +/* + * Create dependencies for an operator (either a freshly inserted + * complete operator, a new shell operator, a just-updated shell, + * or an operator that's being modified by ALTER OPERATOR). + * + * NB: the OidIsValid tests in this routine are necessary, in case + * the given operator is a shell. + */ +ObjectAddress +makeOperatorDependencies(HeapTuple tuple, bool isUpdate) +{ + Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple); + ObjectAddress myself, + referenced; - pg_operator_scan = heap_beginscan(pg_operator_desc, - 0, - SnapshotSelf, /* no cache? */ - 1, - opKey); + myself.classId = OperatorRelationId; + myself.objectId = HeapTupleGetOid(tuple); + myself.objectSubId = 0; - tup = heap_getnext(pg_operator_scan, 0); - if (HeapTupleIsValid(tup) && - !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprnegate))) + /* + * If we are updating the operator, delete any existing entries, except + * for extension membership which should remain the same. + */ + if (isUpdate) { - values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId); - replaces[Anum_pg_operator_oprnegate - 1] = 'r'; - tup = heap_modifytuple(tup, - pg_operator_desc, - values, - nulls, - replaces); - - setheapoverride(true); - heap_replace(pg_operator_desc, &tup->t_self, tup, NULL); - setheapoverride(false); + deleteDependencyRecordsFor(myself.classId, myself.objectId, true); + deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); } - heap_endscan(pg_operator_scan); - - heap_close(pg_operator_desc, RowExclusiveLock); -} + /* Dependency on namespace */ + if (OidIsValid(oper->oprnamespace)) + { + referenced.classId = NamespaceRelationId; + referenced.objectId = oper->oprnamespace; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + /* Dependency on left type */ + if (OidIsValid(oper->oprleft)) + { + referenced.classId = TypeRelationId; + referenced.objectId = oper->oprleft; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } -/* ---------------------------------------------------------------- - * OperatorCreate - * - * This is now just an interface procedure for OperatorDef ... - * - * "X" indicates an optional argument (i.e. one that can be NULL) - * operatorName; -- operator name - * leftTypeName; -- X left type name - * rightTypeName; -- X right type name - * procedureName; -- procedure for operator - * precedence; -- operator precedence - * isLeftAssociative; -- operator is left associative - * commutatorName; -- X commutator operator name - * negatorName; -- X negator operator name - * restrictionName; -- X restriction sel. procedure - * joinName; -- X join sel. procedure - * canHash; -- hash join can be used with this operator - * leftSortName; -- X left sort operator (for merge join) - * rightSortName; -- X right sort operator (for merge join) - */ -void -OperatorCreate(char *operatorName, - char *leftTypeName, - char *rightTypeName, - char *procedureName, - uint16 precedence, - bool isLeftAssociative, - char *commutatorName, - char *negatorName, - char *restrictionName, - char *joinName, - bool canHash, - char *leftSortName, - char *rightSortName) -{ - if (!leftTypeName && !rightTypeName) - elog(ERROR, "OperatorCreate: at least one of leftarg or rightarg must be defined"); + /* Dependency on right type */ + if (OidIsValid(oper->oprright)) + { + referenced.classId = TypeRelationId; + referenced.objectId = oper->oprright; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } - if (!(leftTypeName && rightTypeName)) + /* Dependency on result type */ + if (OidIsValid(oper->oprresult)) { - /* If it's not a binary op, these things mustn't be set: */ - if (commutatorName) - elog(ERROR, "OperatorCreate: only binary operators can have commutators"); - if (negatorName) - elog(ERROR, "OperatorCreate: only binary operators can have negators"); - if (restrictionName || joinName) - elog(ERROR, "OperatorCreate: only binary operators can have selectivity"); - if (canHash) - elog(ERROR, "OperatorCreate: only binary operators can hash"); - if (leftSortName || rightSortName) - elog(ERROR, "OperatorCreate: only binary operators can have sort links"); + referenced.classId = TypeRelationId; + referenced.objectId = oper->oprresult; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } - /* ---------------- - * Use OperatorDef() to define the specified operator and - * also create shells for the operator's associated operators - * if they don't already exist. - * ---------------- + /* + * NOTE: we do not consider the operator to depend on the associated + * operators oprcom and oprnegate. We would not want to delete this + * operator if those go away, but only reset the link fields; which is not + * a function that the dependency code can presently handle. (Something + * could perhaps be done with objectSubId though.) For now, it's okay to + * let those links dangle if a referenced operator is removed. */ - OperatorDef(operatorName, - leftTypeName, - rightTypeName, - procedureName, - precedence, - isLeftAssociative, - commutatorName, - negatorName, - restrictionName, - joinName, - canHash, - leftSortName, - rightSortName); + + /* Dependency on implementation function */ + if (OidIsValid(oper->oprcode)) + { + referenced.classId = ProcedureRelationId; + referenced.objectId = oper->oprcode; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + /* Dependency on restriction selectivity function */ + if (OidIsValid(oper->oprrest)) + { + referenced.classId = ProcedureRelationId; + referenced.objectId = oper->oprrest; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + /* Dependency on join selectivity function */ + if (OidIsValid(oper->oprjoin)) + { + referenced.classId = ProcedureRelationId; + referenced.objectId = oper->oprjoin; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + /* Dependency on owner */ + recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple), + oper->oprowner); + + /* Dependency on extension */ + recordDependencyOnCurrentExtension(&myself, true); + + return myself; }