mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-23 12:20:40 +02:00
This includes removing tabs after periods in C comments, which was applied to back branches, so this change should not effect backpatching.
864 lines
25 KiB
C
864 lines
25 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* pg_operator.c
|
|
* routines to support manipulation of the pg_operator relation
|
|
*
|
|
* Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/catalog/pg_operator.c
|
|
*
|
|
* NOTES
|
|
* these routines moved here from commands/define.c and somewhat cleaned up.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/heapam.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_proc.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "miscadmin.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 OperatorGet(const char *operatorName,
|
|
Oid operatorNamespace,
|
|
Oid leftObjectId,
|
|
Oid rightObjectId,
|
|
bool *defined);
|
|
|
|
static Oid OperatorLookup(List *operatorName,
|
|
Oid leftObjectId,
|
|
Oid rightObjectId,
|
|
bool *defined);
|
|
|
|
static Oid OperatorShellMake(const char *operatorName,
|
|
Oid operatorNamespace,
|
|
Oid leftTypeId,
|
|
Oid rightTypeId);
|
|
|
|
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);
|
|
|
|
static void makeOperatorDependencies(HeapTuple tuple);
|
|
|
|
|
|
/*
|
|
* Check whether a proposed operator name is legal
|
|
*
|
|
* This had better match the behavior of parser/scan.l!
|
|
*
|
|
* 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 bool
|
|
validOperatorName(const char *name)
|
|
{
|
|
size_t len = strlen(name);
|
|
|
|
/* Can't be empty or too long */
|
|
if (len == 0 || len >= NAMEDATALEN)
|
|
return false;
|
|
|
|
/* 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.
|
|
*/
|
|
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))
|
|
{
|
|
RegProcedure oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode;
|
|
|
|
operatorObjectId = HeapTupleGetOid(tup);
|
|
*defined = RegProcedureIsValid(oprcode);
|
|
ReleaseSysCache(tup);
|
|
}
|
|
else
|
|
{
|
|
operatorObjectId = InvalidOid;
|
|
*defined = false;
|
|
}
|
|
|
|
return operatorObjectId;
|
|
}
|
|
|
|
/*
|
|
* OperatorLookup
|
|
*
|
|
* 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
|
|
OperatorLookup(List *operatorName,
|
|
Oid leftObjectId,
|
|
Oid rightObjectId,
|
|
bool *defined)
|
|
{
|
|
Oid operatorObjectId;
|
|
RegProcedure oprcode;
|
|
|
|
operatorObjectId = LookupOperName(NULL, operatorName,
|
|
leftObjectId, rightObjectId,
|
|
true, -1);
|
|
if (!OidIsValid(operatorObjectId))
|
|
{
|
|
*defined = false;
|
|
return InvalidOid;
|
|
}
|
|
|
|
oprcode = get_opcode(operatorObjectId);
|
|
*defined = RegProcedureIsValid(oprcode);
|
|
|
|
return operatorObjectId;
|
|
}
|
|
|
|
|
|
/*
|
|
* OperatorShellMake
|
|
* Make a "shell" entry for a not-yet-existing operator.
|
|
*/
|
|
static Oid
|
|
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];
|
|
bool nulls[Natts_pg_operator];
|
|
NameData oname;
|
|
TupleDesc tupDesc;
|
|
|
|
/*
|
|
* 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] = 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.
|
|
*/
|
|
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(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;
|
|
|
|
/*
|
|
* create a new operator tuple
|
|
*/
|
|
tup = heap_form_tuple(tupDesc, values, nulls);
|
|
|
|
/*
|
|
* insert our "shell" operator tuple
|
|
*/
|
|
operatorObjectId = simple_heap_insert(pg_operator_desc, tup);
|
|
|
|
CatalogUpdateIndexes(pg_operator_desc, tup);
|
|
|
|
/* Add dependencies for the entry */
|
|
makeOperatorDependencies(tup);
|
|
|
|
heap_freetuple(tup);
|
|
|
|
/* Post creation hook for new shell operator */
|
|
InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0);
|
|
|
|
/*
|
|
* Make sure the tuple is visible for subsequent lookups/updates.
|
|
*/
|
|
CommandCounterIncrement();
|
|
|
|
/*
|
|
* close the operator relation and return the oid.
|
|
*/
|
|
heap_close(pg_operator_desc, RowExclusiveLock);
|
|
|
|
return operatorObjectId;
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
* 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.
|
|
* This is called creating a shell, and its main effect is to
|
|
* create a tuple in the PG_OPERATOR catalog with minimal
|
|
* 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.
|
|
*/
|
|
Oid
|
|
OperatorCreate(const char *operatorName,
|
|
Oid operatorNamespace,
|
|
Oid leftTypeId,
|
|
Oid rightTypeId,
|
|
Oid procedureId,
|
|
List *commutatorName,
|
|
List *negatorName,
|
|
Oid restrictionId,
|
|
Oid joinId,
|
|
bool canMerge,
|
|
bool canHash)
|
|
{
|
|
Relation pg_operator_desc;
|
|
HeapTuple tup;
|
|
bool nulls[Natts_pg_operator];
|
|
bool replaces[Natts_pg_operator];
|
|
Datum values[Natts_pg_operator];
|
|
Oid operatorObjectId;
|
|
bool operatorAlreadyDefined;
|
|
Oid operResultType;
|
|
Oid commutatorId,
|
|
negatorId;
|
|
bool selfCommutator = false;
|
|
NameData oname;
|
|
TupleDesc tupDesc;
|
|
int i;
|
|
|
|
/*
|
|
* Sanity checks
|
|
*/
|
|
if (!validOperatorName(operatorName))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_NAME),
|
|
errmsg("\"%s\" is not a valid operator name",
|
|
operatorName)));
|
|
|
|
if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId)))
|
|
{
|
|
/* If it's not a binary op, these things mustn't be set: */
|
|
if (commutatorName)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("only binary operators can have commutators")));
|
|
if (OidIsValid(joinId))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("only binary operators can have join selectivity")));
|
|
if (canMerge)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("only binary operators can merge join")));
|
|
if (canHash)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("only binary operators can hash")));
|
|
}
|
|
|
|
operResultType = get_func_rettype(procedureId);
|
|
|
|
if (operResultType != BOOLOID)
|
|
{
|
|
/* If it's not a boolean op, these things mustn't be set: */
|
|
if (negatorName)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("only boolean operators can have negators")));
|
|
if (OidIsValid(restrictionId))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("only boolean operators can have restriction selectivity")));
|
|
if (OidIsValid(joinId))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("only boolean operators can have join selectivity")));
|
|
if (canMerge)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("only boolean operators can merge join")));
|
|
if (canHash)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("only boolean operators can hash")));
|
|
}
|
|
|
|
operatorObjectId = OperatorGet(operatorName,
|
|
operatorNamespace,
|
|
leftTypeId,
|
|
rightTypeId,
|
|
&operatorAlreadyDefined);
|
|
|
|
if (operatorAlreadyDefined)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_DUPLICATE_FUNCTION),
|
|
errmsg("operator %s already exists",
|
|
operatorName)));
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
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 (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
|
|
commutatorId = InvalidOid;
|
|
|
|
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
|
|
negatorId = InvalidOid;
|
|
|
|
/*
|
|
* set up values in the operator tuple
|
|
*/
|
|
|
|
for (i = 0; i < Natts_pg_operator; ++i)
|
|
{
|
|
values[i] = (Datum) NULL;
|
|
replaces[i] = true;
|
|
nulls[i] = false;
|
|
}
|
|
|
|
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 replacing an operator shell, update; else insert
|
|
*/
|
|
if (operatorObjectId)
|
|
{
|
|
tup = SearchSysCacheCopy1(OPEROID,
|
|
ObjectIdGetDatum(operatorObjectId));
|
|
if (!HeapTupleIsValid(tup))
|
|
elog(ERROR, "cache lookup failed for operator %u",
|
|
operatorObjectId);
|
|
|
|
tup = heap_modify_tuple(tup,
|
|
RelationGetDescr(pg_operator_desc),
|
|
values,
|
|
nulls,
|
|
replaces);
|
|
|
|
simple_heap_update(pg_operator_desc, &tup->t_self, tup);
|
|
}
|
|
else
|
|
{
|
|
tupDesc = pg_operator_desc->rd_att;
|
|
tup = heap_form_tuple(tupDesc, values, nulls);
|
|
|
|
operatorObjectId = simple_heap_insert(pg_operator_desc, tup);
|
|
}
|
|
|
|
/* Must update the indexes in either case */
|
|
CatalogUpdateIndexes(pg_operator_desc, tup);
|
|
|
|
/* Add dependencies for the entry */
|
|
makeOperatorDependencies(tup);
|
|
|
|
/* 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 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);
|
|
|
|
return operatorObjectId;
|
|
}
|
|
|
|
/*
|
|
* Try to lookup another operator (commutator, etc)
|
|
*
|
|
* 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 Oid
|
|
get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId,
|
|
const char *operatorName, Oid operatorNamespace,
|
|
Oid leftTypeId, Oid rightTypeId, bool isCommutator)
|
|
{
|
|
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;
|
|
}
|
|
|
|
otherNamespace = QualifiedNameGetCreationNamespace(otherOp,
|
|
&otherName);
|
|
|
|
if (strcmp(otherName, operatorName) == 0 &&
|
|
otherNamespace == operatorNamespace &&
|
|
otherLeftTypeId == leftTypeId &&
|
|
otherRightTypeId == rightTypeId)
|
|
{
|
|
/*
|
|
* self-linkage to this operator; caller will fix later. Note that
|
|
* only self-linkage for commutation makes sense.
|
|
*/
|
|
if (!isCommutator)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
errmsg("operator cannot be its own negator or sort operator")));
|
|
return InvalidOid;
|
|
}
|
|
|
|
/* not in catalogs, different from operator, so make shell */
|
|
|
|
aclresult = pg_namespace_aclcheck(otherNamespace, GetUserId(),
|
|
ACL_CREATE);
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(otherNamespace));
|
|
|
|
other_oid = OperatorShellMake(otherName,
|
|
otherNamespace,
|
|
otherLeftTypeId,
|
|
otherRightTypeId);
|
|
return other_oid;
|
|
}
|
|
|
|
/*
|
|
* OperatorUpd
|
|
*
|
|
* 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.
|
|
*/
|
|
static void
|
|
OperatorUpd(Oid baseId, Oid commId, Oid negId)
|
|
{
|
|
int i;
|
|
Relation pg_operator_desc;
|
|
HeapTuple tup;
|
|
bool nulls[Natts_pg_operator];
|
|
bool replaces[Natts_pg_operator];
|
|
Datum values[Natts_pg_operator];
|
|
|
|
for (i = 0; i < Natts_pg_operator; ++i)
|
|
{
|
|
values[i] = (Datum) 0;
|
|
replaces[i] = false;
|
|
nulls[i] = false;
|
|
}
|
|
|
|
/*
|
|
* check and update the commutator & negator, if necessary
|
|
*
|
|
* We need a CommandCounterIncrement here in case of a self-commutator
|
|
* operator: we'll need to update the tuple that we just inserted.
|
|
*/
|
|
CommandCounterIncrement();
|
|
|
|
pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock);
|
|
|
|
tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(commId));
|
|
|
|
/*
|
|
* 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 (commId == negId)
|
|
{
|
|
if (HeapTupleIsValid(tup))
|
|
{
|
|
Form_pg_operator 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] = true;
|
|
}
|
|
|
|
if (!OidIsValid(t->oprcom))
|
|
{
|
|
values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId);
|
|
replaces[Anum_pg_operator_oprcom - 1] = true;
|
|
}
|
|
|
|
tup = heap_modify_tuple(tup,
|
|
RelationGetDescr(pg_operator_desc),
|
|
values,
|
|
nulls,
|
|
replaces);
|
|
|
|
simple_heap_update(pg_operator_desc, &tup->t_self, tup);
|
|
|
|
CatalogUpdateIndexes(pg_operator_desc, tup);
|
|
}
|
|
}
|
|
|
|
heap_close(pg_operator_desc, RowExclusiveLock);
|
|
|
|
return;
|
|
}
|
|
|
|
/* if commutator and negator are different, do two updates */
|
|
|
|
if (HeapTupleIsValid(tup) &&
|
|
!(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprcom)))
|
|
{
|
|
values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId);
|
|
replaces[Anum_pg_operator_oprcom - 1] = true;
|
|
|
|
tup = heap_modify_tuple(tup,
|
|
RelationGetDescr(pg_operator_desc),
|
|
values,
|
|
nulls,
|
|
replaces);
|
|
|
|
simple_heap_update(pg_operator_desc, &tup->t_self, tup);
|
|
|
|
CatalogUpdateIndexes(pg_operator_desc, tup);
|
|
|
|
values[Anum_pg_operator_oprcom - 1] = (Datum) NULL;
|
|
replaces[Anum_pg_operator_oprcom - 1] = false;
|
|
}
|
|
|
|
/* check and update the negator, if necessary */
|
|
|
|
tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(negId));
|
|
|
|
if (HeapTupleIsValid(tup) &&
|
|
!(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprnegate)))
|
|
{
|
|
values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId);
|
|
replaces[Anum_pg_operator_oprnegate - 1] = true;
|
|
|
|
tup = heap_modify_tuple(tup,
|
|
RelationGetDescr(pg_operator_desc),
|
|
values,
|
|
nulls,
|
|
replaces);
|
|
|
|
simple_heap_update(pg_operator_desc, &tup->t_self, tup);
|
|
|
|
CatalogUpdateIndexes(pg_operator_desc, tup);
|
|
}
|
|
|
|
heap_close(pg_operator_desc, RowExclusiveLock);
|
|
}
|
|
|
|
/*
|
|
* Create dependencies for a new operator (either a freshly inserted
|
|
* complete operator, a new shell operator, or a just-updated shell).
|
|
*
|
|
* NB: the OidIsValid tests in this routine are necessary, in case
|
|
* the given operator is a shell.
|
|
*/
|
|
static void
|
|
makeOperatorDependencies(HeapTuple tuple)
|
|
{
|
|
Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple);
|
|
ObjectAddress myself,
|
|
referenced;
|
|
|
|
myself.classId = OperatorRelationId;
|
|
myself.objectId = HeapTupleGetOid(tuple);
|
|
myself.objectSubId = 0;
|
|
|
|
/*
|
|
* In case we are updating a shell, delete any existing entries, except
|
|
* for extension membership which should remain the same.
|
|
*/
|
|
deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
|
|
deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* Dependency on right type */
|
|
if (OidIsValid(oper->oprright))
|
|
{
|
|
referenced.classId = TypeRelationId;
|
|
referenced.objectId = oper->oprright;
|
|
referenced.objectSubId = 0;
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
}
|
|
|
|
/* Dependency on result type */
|
|
if (OidIsValid(oper->oprresult))
|
|
{
|
|
referenced.classId = TypeRelationId;
|
|
referenced.objectId = oper->oprresult;
|
|
referenced.objectSubId = 0;
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
/* 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);
|
|
}
|