postgresql/src/backend/catalog/pg_operator.c

1045 lines
30 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* pg_operator.c
* routines to support manipulation of the pg_operator relation
*
* 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 $
*
* NOTES
* these routines moved here from commands/define.c and somewhat cleaned up.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
1999-07-16 07:00:38 +02:00
#include "parser/parse_func.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
static Oid OperatorGetWithOpenRelation(Relation pg_operator_desc,
1999-05-25 18:15:34 +02:00
const char *operatorName,
Oid leftObjectId,
Oid rightObjectId,
bool *defined);
static Oid OperatorGet(char *operatorName,
1999-05-25 18:15:34 +02:00
char *leftTypeName,
char *rightTypeName,
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
*
* 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)
*/
static Oid
OperatorGetWithOpenRelation(Relation pg_operator_desc,
const char *operatorName,
Oid leftObjectId,
Oid rightObjectId,
bool *defined)
{
HeapScanDesc pg_operator_scan;
Oid operatorObjectId;
HeapTuple tup;
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);
/* ----------------
* 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.
* ----------------
*/
tup = heap_getnext(pg_operator_scan, 0);
if (HeapTupleIsValid(tup))
{
regproc oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode;
1999-05-25 18:15:34 +02:00
operatorObjectId = tup->t_data->t_oid;
*defined = RegProcedureIsValid(oprcode);
}
else
{
operatorObjectId = InvalidOid;
*defined = false;
}
/* ----------------
* close the scan and return the oid.
* ----------------
*/
heap_endscan(pg_operator_scan);
return operatorObjectId;
}
/* ----------------------------------------------------------------
* OperatorGet
*
* finds the operator associated with the specified name
* and left and right type names.
* ----------------------------------------------------------------
*/
static Oid
OperatorGet(char *operatorName,
char *leftTypeName,
char *rightTypeName,
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);
}
if (rightTypeName)
{
rightObjectId = TypeGet(rightTypeName, &rightDefined);
if (!OidIsValid(rightObjectId) || !rightDefined)
1998-01-06 20:42:33 +01:00
elog(ERROR, "OperatorGet: right type '%s' nonexistent",
rightTypeName);
}
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);
return operatorObjectId;
}
/* ----------------------------------------------------------------
* OperatorShellMakeWithOpenRelation
*
* ----------------------------------------------------------------
*/
static Oid
OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
char *operatorName,
Oid leftObjectId,
Oid rightObjectId)
{
int i;
HeapTuple tup;
Datum values[Natts_pg_operator];
char nulls[Natts_pg_operator];
Oid operatorObjectId;
NameData oname;
TupleDesc tupDesc;
/* ----------------
1998-09-01 05:29:17 +02:00
* initialize our *nulls and *values arrays
* ----------------
*/
for (i = 0; i < Natts_pg_operator; ++i)
{
nulls[i] = ' ';
values[i] = (Datum) NULL; /* redundant, but safe */
}
/* ----------------
* initialize *values with the operator name and input data types.
1999-05-25 18:15:34 +02:00
* 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
* ----------------
*/
tupDesc = pg_operator_desc->rd_att;
tup = heap_formtuple(tupDesc,
values,
nulls);
/* ----------------
* insert our "shell" operator tuple and
* close the relation
* ----------------
*/
heap_insert(pg_operator_desc, tup);
1998-11-27 20:52:36 +01:00
operatorObjectId = tup->t_data->t_oid;
/* ----------------
* free the tuple and return the operator oid
* ----------------
*/
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;
Oid leftObjectId = InvalidOid;
Oid rightObjectId = InvalidOid;
bool leftDefined = false;
bool rightDefined = false;
/* ----------------
* get the left and right type oid's for this operator
* ----------------
*/
if (leftTypeName)
leftObjectId = TypeGet(leftTypeName, &leftDefined);
if (rightTypeName)
rightObjectId = TypeGet(rightTypeName, &rightDefined);
if (!((OidIsValid(leftObjectId) && leftDefined) ||
(OidIsValid(rightObjectId) && rightDefined)))
1998-01-06 20:42:33 +01:00
elog(ERROR, "OperatorShellMake: no valid argument types??");
/* ----------------
* open pg_operator
* ----------------
*/
pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
/* ----------------
* add a "shell" operator tuple to the operator relation
* and recover the shell tuple's oid.
* ----------------
*/
operatorObjectId = OperatorShellMakeWithOpenRelation(pg_operator_desc,
1999-05-25 18:15:34 +02:00
operatorName,
leftObjectId,
rightObjectId);
/* ----------------
* close the operator relation and return the oid.
* ----------------
*/
heap_close(pg_operator_desc, RowExclusiveLock);
return operatorObjectId;
}
/* --------------------------------
* OperatorDef
*
* 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.
*
* 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
1998-11-27 20:52:36 +01:00
* 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)
{
int i,
j;
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];
Oid operatorObjectId;
bool operatorAlreadyDefined;
Oid leftTypeId = InvalidOid;
Oid rightTypeId = InvalidOid;
Oid commutatorId = InvalidOid;
Oid negatorId = InvalidOid;
bool leftDefined = false;
bool rightDefined = false;
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)
1998-01-06 20:42:33 +01:00
elog(ERROR, "OperatorDef: operator \"%s\" already defined",
operatorName);
1999-05-25 18:15:34 +02:00
/*
* At this point, if operatorObjectId is not InvalidOid then we are
* filling in a previously-created shell.
*/
/* ----------------
* look up the operator data types.
*
* Note: types must be defined before operators
* ----------------
*/
if (leftTypeName)
{
leftTypeId = TypeGet(leftTypeName, &leftDefined);
if (!OidIsValid(leftTypeId) || !leftDefined)
elog(ERROR, "OperatorDef: left type '%s' nonexistent",
leftTypeName);
}
if (rightTypeName)
{
rightTypeId = TypeGet(rightTypeName, &rightDefined);
if (!OidIsValid(rightTypeId) || !rightDefined)
elog(ERROR, "OperatorDef: right type '%s' nonexistent",
rightTypeName);
}
if (!((OidIsValid(leftTypeId) && leftDefined) ||
(OidIsValid(rightTypeId) && rightDefined)))
elog(ERROR, "OperatorDef: must have at least one argument type");
for (i = 0; i < Natts_pg_operator; ++i)
{
values[i] = (Datum) NULL;
replaces[i] = 'r';
nulls[i] = ' ';
}
/* ----------------
* 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.
* ----------------
*/
1997-09-18 22:22:58 +02:00
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))
1998-05-10 01:43:45 +02:00
func_error("OperatorDef", procedureName, nargs, typeId, NULL);
1998-11-27 20:52:36 +01:00
values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(tup->t_data->t_oid);
values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(((Form_pg_proc)
1999-05-25 18:15:34 +02:00
GETSTRUCT(tup))->prorettype);
/* ----------------
* find restriction
* ----------------
*/
if (restrictionName)
{ /* optional */
1997-09-18 22:22:58 +02:00
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))
1998-05-10 01:43:45 +02:00
func_error("OperatorDef", restrictionName, 5, typeId, NULL);
1998-11-27 20:52:36 +01:00
values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(tup->t_data->t_oid);
}
else
values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
/* ----------------
* find join - only valid for binary operators
* ----------------
*/
if (joinName)
{ /* optional */
1997-09-18 22:22:58 +02:00
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))
1998-05-10 01:43:45 +02:00
func_error("OperatorDef", joinName, 5, typeId, NULL);
1998-11-27 20:52:36 +01:00
values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(tup->t_data->t_oid);
}
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);
1999-05-25 18:15:34 +02:00
++i; /* Skip "oprresult", it was filled in
* above */
/*
* Set up the other operators. If they do not currently exist, create
* shells in order to get ObjectId's.
*/
name[0] = commutatorName;
name[1] = negatorName;
name[2] = leftSortName;
name[3] = rightSortName;
for (j = 0; j < 4; ++j)
{
if (name[j])
{
1999-05-25 18:15:34 +02:00
char *otherLeftTypeName = NULL;
char *otherRightTypeName = NULL;
Oid otherLeftTypeId = InvalidOid;
Oid otherRightTypeId = InvalidOid;
Oid other_oid = InvalidOid;
bool otherDefined = false;
switch (j)
{
1999-05-25 18:15:34 +02:00
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;
1999-05-25 18:15:34 +02:00
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;
1999-05-25 18:15:34 +02:00
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;
1999-05-25 18:15:34 +02:00
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))
1998-01-06 20:42:33 +01:00
elog(ERROR,
"OperatorDef: can't create operator shell '%s'",
name[j]);
values[i++] = ObjectIdGetDatum(other_oid);
}
else
{
1999-05-25 18:15:34 +02:00
/*
* 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);
}
}
/* last three fields were filled in above */
pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
/*
1998-11-27 20:52:36 +01:00
* If we are adding to an operator shell, get its t_self
*/
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);
heap_endscan(pg_operator_scan);
}
else
{
tupDesc = pg_operator_desc->rd_att;
tup = heap_formtuple(tupDesc, values, nulls);
heap_insert(pg_operator_desc, tup);
1998-11-27 20:52:36 +01:00
operatorObjectId = tup->t_data->t_oid;
}
heap_close(pg_operator_desc, RowExclusiveLock);
/*
* If a commutator and/or negator link is provided, update the other
1999-05-25 18:15:34 +02:00
* 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.
*/
if (selfCommutator)
commutatorId = operatorObjectId;
if (OidIsValid(commutatorId) || OidIsValid(negatorId))
OperatorUpd(operatorObjectId, commutatorId, negatorId);
}
/* ----------------------------------------------------------------
* 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;
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},
};
fmgr_info(F_OIDEQ, &opKey[0].sk_func);
opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs;
for (i = 0; i < Natts_pg_operator; ++i)
{
values[i] = (Datum) NULL;
replaces[i] = ' ';
nulls[i] = ' ';
}
pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
/* check and update the commutator, if necessary */
opKey[0].sk_argument = ObjectIdGetDatum(commId);
pg_operator_scan = heap_beginscan(pg_operator_desc,
0,
SnapshotSelf, /* no cache? */
1,
opKey);
tup = heap_getnext(pg_operator_scan, 0);
1999-05-25 18:15:34 +02:00
/*
* 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))
{
1998-09-01 05:29:17 +02:00
Form_pg_operator t;
1998-09-01 05:29:17 +02:00
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);
}
}
heap_endscan(pg_operator_scan);
heap_close(pg_operator_desc, RowExclusiveLock);
return;
}
/* if commutator and negator are different, do two updates */
if (HeapTupleIsValid(tup) &&
1998-09-01 05:29:17 +02:00
!(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->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);
values[Anum_pg_operator_oprcom - 1] = (Datum) NULL;
replaces[Anum_pg_operator_oprcom - 1] = ' ';
}
heap_endscan(pg_operator_scan);
/* check and update the negator, if necessary */
opKey[0].sk_argument = ObjectIdGetDatum(negId);
pg_operator_scan = heap_beginscan(pg_operator_desc,
0,
SnapshotSelf, /* no cache? */
1,
opKey);
tup = heap_getnext(pg_operator_scan, 0);
if (HeapTupleIsValid(tup) &&
1998-09-01 05:29:17 +02:00
!(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprnegate)))
{
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);
}
heap_endscan(pg_operator_scan);
heap_close(pg_operator_desc, RowExclusiveLock);
}
/* ----------------------------------------------------------------
* 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");
1999-05-25 18:15:34 +02:00
if (!(leftTypeName && rightTypeName))
{
/* 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");
}
/* ----------------
* Use OperatorDef() to define the specified operator and
* also create shells for the operator's associated operators
* if they don't already exist.
* ----------------
*/
OperatorDef(operatorName,
leftTypeName,
rightTypeName,
procedureName,
precedence,
isLeftAssociative,
commutatorName,
negatorName,
restrictionName,
joinName,
canHash,
leftSortName,
rightSortName);
}