1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* pg_operator.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* routines to support manipulation of the pg_operator relation
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2004-12-31 23:04:05 +01:00
|
|
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2005-03-29 05:01:32 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.89 2005/03/29 03:01:30 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* NOTES
|
1997-09-07 07:04:48 +02:00
|
|
|
* these routines moved here from commands/define.c and somewhat cleaned up.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/catname.h"
|
2002-07-17 00:12:20 +02:00
|
|
|
#include "catalog/dependency.h"
|
1999-11-22 18:56:41 +01:00
|
|
|
#include "catalog/indexing.h"
|
2002-04-17 01:08:12 +02:00
|
|
|
#include "catalog/namespace.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "catalog/pg_operator.h"
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "miscadmin.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "parser/parse_func.h"
|
2002-04-17 01:08:12 +02:00
|
|
|
#include "parser/parse_oper.h"
|
2002-04-27 05:45:03 +02:00
|
|
|
#include "utils/acl.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "utils/builtins.h"
|
2002-04-09 22:35:55 +02:00
|
|
|
#include "utils/lsyscache.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "utils/syscache.h"
|
|
|
|
|
1996-11-04 00:27:08 +01:00
|
|
|
|
2002-03-29 20:06:29 +01:00
|
|
|
static Oid OperatorGet(const char *operatorName,
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid operatorNamespace,
|
|
|
|
Oid leftObjectId,
|
|
|
|
Oid rightObjectId,
|
|
|
|
bool *defined);
|
1999-04-11 04:30:59 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
static Oid OperatorLookup(List *operatorName,
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid leftObjectId,
|
|
|
|
Oid rightObjectId,
|
|
|
|
bool *defined);
|
2002-04-17 01:08:12 +02:00
|
|
|
|
2002-03-29 20:06:29 +01:00
|
|
|
static Oid OperatorShellMake(const char *operatorName,
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid operatorNamespace,
|
|
|
|
Oid leftTypeId,
|
|
|
|
Oid rightTypeId);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static void OperatorUpd(Oid baseId, Oid commId, Oid negId);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
static Oid get_other_operator(List *otherOp,
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid otherLeftTypeId, Oid otherRightTypeId,
|
|
|
|
const char *operatorName, Oid operatorNamespace,
|
|
|
|
Oid leftTypeId, Oid rightTypeId,
|
|
|
|
bool isCommutator);
|
2002-04-17 01:08:12 +02:00
|
|
|
|
2002-07-17 00:12:20 +02:00
|
|
|
static void makeOperatorDependencies(HeapTuple tuple, Oid pg_operator_relid);
|
|
|
|
|
2001-10-22 21:34:13 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
size_t len = strlen(name);
|
2001-10-22 21:34:13 +02:00
|
|
|
|
|
|
|
/* 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 */
|
2003-07-21 03:59:11 +02:00
|
|
|
if (strspn(name, "~!@#^&|`?+-*/%<>=") != len)
|
2001-10-22 21:34:13 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Can't contain slash-star or dash-dash (comment starts) */
|
|
|
|
if (strstr(name, "/*") || strstr(name, "--"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* For SQL92 compatibility, '+' and '-' cannot be the last char of a
|
|
|
|
* multi-char operator unless the operator contains chars that are not
|
|
|
|
* in SQL92 operators. The idea is to lex '=-' as two operators, but
|
|
|
|
* not to forbid operator names like '?-' that could not be sequences
|
|
|
|
* of SQL92 operators.
|
2001-10-22 21:34:13 +02:00
|
|
|
*/
|
|
|
|
if (len > 1 &&
|
2001-10-25 07:50:21 +02:00
|
|
|
(name[len - 1] == '+' ||
|
|
|
|
name[len - 1] == '-'))
|
2001-10-22 21:34:13 +02:00
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
int ic;
|
2001-10-22 21:34:13 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
for (ic = len - 2; ic >= 0; ic--)
|
2001-10-22 21:34:13 +02:00
|
|
|
{
|
2003-07-21 03:59:11 +02:00
|
|
|
if (strchr("~!@#^&|`?%", name[ic]))
|
2001-10-22 21:34:13 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
/*
|
|
|
|
* OperatorGet
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2002-04-17 01:08:12 +02:00
|
|
|
* finds an operator given an exact specification (name, namespace,
|
|
|
|
* left and right type IDs).
|
2001-06-01 04:41:36 +02:00
|
|
|
*
|
2002-04-17 01:08:12 +02:00
|
|
|
* *defined is set TRUE if defined (not a shell)
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static Oid
|
2002-03-29 20:06:29 +01:00
|
|
|
OperatorGet(const char *operatorName,
|
2002-04-17 01:08:12 +02:00
|
|
|
Oid operatorNamespace,
|
2002-03-29 20:06:29 +01:00
|
|
|
Oid leftObjectId,
|
|
|
|
Oid rightObjectId,
|
|
|
|
bool *defined)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple tup;
|
2002-03-29 20:06:29 +01:00
|
|
|
Oid operatorObjectId;
|
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
tup = SearchSysCache(OPERNAMENSP,
|
|
|
|
PointerGetDatum(operatorName),
|
|
|
|
ObjectIdGetDatum(leftObjectId),
|
|
|
|
ObjectIdGetDatum(rightObjectId),
|
|
|
|
ObjectIdGetDatum(operatorNamespace));
|
1999-04-11 04:30:59 +02:00
|
|
|
if (HeapTupleIsValid(tup))
|
|
|
|
{
|
2002-04-25 04:56:56 +02:00
|
|
|
RegProcedure oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode;
|
1999-05-25 18:15:34 +02:00
|
|
|
|
2002-07-20 07:16:59 +02:00
|
|
|
operatorObjectId = HeapTupleGetOid(tup);
|
1999-04-11 04:30:59 +02:00
|
|
|
*defined = RegProcedureIsValid(oprcode);
|
2002-04-17 01:08:12 +02:00
|
|
|
ReleaseSysCache(tup);
|
1999-04-11 04:30:59 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
operatorObjectId = InvalidOid;
|
|
|
|
*defined = false;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
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;
|
2002-04-25 04:56:56 +02:00
|
|
|
RegProcedure oprcode;
|
2002-04-17 01:08:12 +02:00
|
|
|
|
|
|
|
operatorObjectId = LookupOperName(operatorName, leftObjectId,
|
2003-07-04 04:51:34 +02:00
|
|
|
rightObjectId, true);
|
2002-04-17 01:08:12 +02:00
|
|
|
if (!OidIsValid(operatorObjectId))
|
|
|
|
{
|
|
|
|
*defined = false;
|
|
|
|
return InvalidOid;
|
|
|
|
}
|
|
|
|
|
|
|
|
oprcode = get_opcode(operatorObjectId);
|
|
|
|
*defined = RegProcedureIsValid(oprcode);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-03 22:18:02 +01:00
|
|
|
return operatorObjectId;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
|
2002-03-29 20:06:29 +01:00
|
|
|
/*
|
|
|
|
* OperatorShellMake
|
|
|
|
* Make a "shell" entry for a not-yet-existing operator.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static Oid
|
2002-03-29 20:06:29 +01:00
|
|
|
OperatorShellMake(const char *operatorName,
|
2002-04-17 01:08:12 +02:00
|
|
|
Oid operatorNamespace,
|
2002-03-29 20:06:29 +01:00
|
|
|
Oid leftTypeId,
|
|
|
|
Oid rightTypeId)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2001-10-22 21:34:13 +02:00
|
|
|
Relation pg_operator_desc;
|
|
|
|
Oid operatorObjectId;
|
1998-02-26 05:46:47 +01:00
|
|
|
int i;
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple tup;
|
|
|
|
Datum values[Natts_pg_operator];
|
|
|
|
char nulls[Natts_pg_operator];
|
1998-04-01 17:35:33 +02:00
|
|
|
NameData oname;
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc tupDesc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-10-22 21:34:13 +02:00
|
|
|
/*
|
|
|
|
* validate operator name
|
|
|
|
*/
|
|
|
|
if (!validOperatorName(operatorName))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_NAME),
|
|
|
|
errmsg("\"%s\" is not a valid operator name",
|
|
|
|
operatorName)));
|
2001-10-22 21:34:13 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* initialize our *nulls and *values arrays
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
for (i = 0; i < Natts_pg_operator; ++i)
|
|
|
|
{
|
|
|
|
nulls[i] = ' ';
|
|
|
|
values[i] = (Datum) NULL; /* redundant, but safe */
|
|
|
|
}
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-10-22 21:34:13 +02:00
|
|
|
* initialize values[] with the operator name and input data types.
|
2001-03-22 07:16:21 +01:00
|
|
|
* Note that oprcode is set to InvalidOid, indicating it's a shell.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
i = 0;
|
1998-04-01 17:35:33 +02:00
|
|
|
namestrcpy(&oname, operatorName);
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = NameGetDatum(&oname); /* oprname */
|
2002-04-17 01:08:12 +02:00
|
|
|
values[i++] = ObjectIdGetDatum(operatorNamespace); /* oprnamespace */
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = Int32GetDatum(GetUserId()); /* oprowner */
|
2002-04-17 01:08:12 +02:00
|
|
|
values[i++] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); /* oprkind */
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = BoolGetDatum(false); /* oprcanhash */
|
|
|
|
values[i++] = ObjectIdGetDatum(leftTypeId); /* oprleft */
|
|
|
|
values[i++] = ObjectIdGetDatum(rightTypeId); /* oprright */
|
|
|
|
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprresult */
|
|
|
|
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprcom */
|
|
|
|
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprnegate */
|
|
|
|
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprlsortop */
|
|
|
|
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprrsortop */
|
|
|
|
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprltcmpop */
|
|
|
|
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprgtcmpop */
|
|
|
|
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprcode */
|
|
|
|
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprrest */
|
|
|
|
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprjoin */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2002-04-17 01:08:12 +02:00
|
|
|
* open pg_operator
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-04-17 01:08:12 +02:00
|
|
|
pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
tupDesc = pg_operator_desc->rd_att;
|
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
/*
|
|
|
|
* create a new operator tuple
|
|
|
|
*/
|
2001-10-22 21:34:13 +02:00
|
|
|
tup = heap_formtuple(tupDesc, values, nulls);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-10-22 21:34:13 +02:00
|
|
|
* insert our "shell" operator tuple
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-05-22 00:05:55 +02:00
|
|
|
operatorObjectId = simple_heap_insert(pg_operator_desc, tup);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogUpdateIndexes(pg_operator_desc, tup);
|
1999-11-22 18:56:41 +01:00
|
|
|
|
2002-07-17 00:12:20 +02:00
|
|
|
/* Add dependencies for the entry */
|
|
|
|
makeOperatorDependencies(tup, RelationGetRelid(pg_operator_desc));
|
|
|
|
|
1999-12-16 23:20:03 +01:00
|
|
|
heap_freetuple(tup);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* close the operator relation and return the oid.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1999-09-18 21:08:25 +02:00
|
|
|
heap_close(pg_operator_desc, RowExclusiveLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-03 22:18:02 +01:00
|
|
|
return operatorObjectId;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
* procedureName procedure for operator
|
|
|
|
* commutatorName X commutator operator
|
|
|
|
* negatorName X negator operator
|
|
|
|
* 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)
|
|
|
|
* ltCompareName X L<R compare operator (for merge join)
|
|
|
|
* gtCompareName X L>R compare operator (for merge join)
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* 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
|
1997-09-07 07:04:48 +02:00
|
|
|
* any information other than their names. Since in order to
|
1996-07-09 08:22:35 +02:00
|
|
|
* 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:
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
|
|
|
* check if operator already defined
|
1999-04-11 04:30:59 +02:00
|
|
|
* if so, but oprcode is null, save the Oid -- we are filling in a shell
|
|
|
|
* otherwise error
|
1996-07-09 08:22:35 +02:00
|
|
|
* get the attribute types from relation descriptor for pg_operator
|
|
|
|
* assign values to the fields of the operator:
|
1997-09-07 07:04:48 +02:00
|
|
|
* operatorName
|
|
|
|
* owner id (simply the user id of the caller)
|
|
|
|
* operator "kind" either "b" for binary or "l" for left unary
|
|
|
|
* 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
|
2000-01-05 19:23:54 +01:00
|
|
|
* else if this already exists, enter its ObjectId
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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
|
1999-04-11 04:30:59 +02:00
|
|
|
* 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.
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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
|
Restructure index AM interface for index building and index tuple deletion,
per previous discussion on pghackers. Most of the duplicate code in
different AMs' ambuild routines has been moved out to a common routine
in index.c; this means that all index types now do the right things about
inserting recently-dead tuples, etc. (I also removed support for EXTEND
INDEX in the ambuild routines, since that's about to go away anyway, and
it cluttered the code a lot.) The retail indextuple deletion routines have
been replaced by a "bulk delete" routine in which the indexscan is inside
the access method. I haven't pushed this change as far as it should go yet,
but it should allow considerable simplification of the internal bookkeeping
for deletions. Also, add flag columns to pg_am to eliminate various
hardcoded tests on AM OIDs, and remove unused pg_am columns.
Fix rtree and gist index types to not attempt to store NULLs; before this,
gist usually crashed, while rtree managed not to crash but computed wacko
bounding boxes for NULL entries (which might have had something to do with
the performance problems we've heard about occasionally).
Add AtEOXact routines to hash, rtree, and gist, all of which have static
state that needs to be reset after an error. We discovered this need long
ago for btree, but missed the other guys.
Oh, one more thing: concurrent VACUUM is now the default.
2001-07-16 00:48:19 +02:00
|
|
|
* actions this is required. Do a lookup to find out the
|
1997-09-07 07:04:48 +02:00
|
|
|
* return type of the procedure
|
|
|
|
* restrictionProcedure -- must access the pg_procedure catalog to get
|
|
|
|
* the ObjectId but this is optional
|
|
|
|
* joinProcedure -- same as restrictionProcedure
|
1996-07-09 08:22:35 +02:00
|
|
|
* now either insert or replace the operator into the pg_operator catalog
|
|
|
|
* if the operator shell is being filled in
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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
|
1996-07-09 08:22:35 +02:00
|
|
|
* else if a new operator is being created
|
1997-09-07 07:04:48 +02:00
|
|
|
* create a tuple using heap_formtuple
|
2002-05-22 00:05:55 +02:00
|
|
|
* call simple_heap_insert
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2002-04-17 01:08:12 +02:00
|
|
|
void
|
|
|
|
OperatorCreate(const char *operatorName,
|
|
|
|
Oid operatorNamespace,
|
|
|
|
Oid leftTypeId,
|
|
|
|
Oid rightTypeId,
|
|
|
|
List *procedureName,
|
|
|
|
List *commutatorName,
|
|
|
|
List *negatorName,
|
|
|
|
List *restrictionName,
|
|
|
|
List *joinName,
|
|
|
|
bool canHash,
|
|
|
|
List *leftSortName,
|
|
|
|
List *rightSortName,
|
|
|
|
List *ltCompareName,
|
|
|
|
List *gtCompareName)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Relation pg_operator_desc;
|
|
|
|
HeapTuple tup;
|
|
|
|
char nulls[Natts_pg_operator];
|
|
|
|
char replaces[Natts_pg_operator];
|
|
|
|
Datum values[Natts_pg_operator];
|
|
|
|
Oid operatorObjectId;
|
1999-04-11 04:30:59 +02:00
|
|
|
bool operatorAlreadyDefined;
|
2002-04-17 01:08:12 +02:00
|
|
|
Oid procOid;
|
|
|
|
Oid operResultType;
|
|
|
|
Oid commutatorId,
|
|
|
|
negatorId,
|
|
|
|
leftSortId,
|
|
|
|
rightSortId,
|
|
|
|
ltCompareId,
|
|
|
|
gtCompareId,
|
|
|
|
restOid,
|
|
|
|
joinOid;
|
1999-04-11 04:30:59 +02:00
|
|
|
bool selfCommutator = false;
|
2005-03-29 05:01:32 +02:00
|
|
|
Oid typeId[4]; /* only need up to 4 args here */
|
1997-09-08 04:41:22 +02:00
|
|
|
int nargs;
|
1998-09-01 06:40:42 +02:00
|
|
|
NameData oname;
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc tupDesc;
|
2002-04-17 01:08:12 +02:00
|
|
|
int i;
|
2002-03-29 20:06:29 +01:00
|
|
|
|
|
|
|
/*
|
2002-04-17 01:08:12 +02:00
|
|
|
* Sanity checks
|
2002-03-29 20:06:29 +01:00
|
|
|
*/
|
|
|
|
if (!validOperatorName(operatorName))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_NAME),
|
|
|
|
errmsg("\"%s\" is not a valid operator name",
|
|
|
|
operatorName)));
|
2002-03-29 20:06:29 +01:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
if (!OidIsValid(leftTypeId) && !OidIsValid(rightTypeId))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
2003-08-04 02:43:34 +02:00
|
|
|
errmsg("at least one of leftarg or rightarg must be specified")));
|
2002-04-17 01:08:12 +02:00
|
|
|
|
|
|
|
if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId)))
|
|
|
|
{
|
|
|
|
/* If it's not a binary op, these things mustn't be set: */
|
|
|
|
if (commutatorName)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
2003-08-04 02:43:34 +02:00
|
|
|
errmsg("only binary operators can have commutators")));
|
2002-04-17 01:08:12 +02:00
|
|
|
if (joinName)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
2003-08-04 02:43:34 +02:00
|
|
|
errmsg("only binary operators can have join selectivity")));
|
2002-04-17 01:08:12 +02:00
|
|
|
if (canHash)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
|
|
errmsg("only binary operators can hash")));
|
2002-04-17 01:08:12 +02:00
|
|
|
if (leftSortName || rightSortName || ltCompareName || gtCompareName)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("only binary operators can merge join")));
|
2002-04-17 01:08:12 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
operatorObjectId = OperatorGet(operatorName,
|
2002-04-17 01:08:12 +02:00
|
|
|
operatorNamespace,
|
2002-03-29 20:06:29 +01:00
|
|
|
leftTypeId,
|
|
|
|
rightTypeId,
|
1999-04-11 04:30:59 +02:00
|
|
|
&operatorAlreadyDefined);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-04-11 04:30:59 +02:00
|
|
|
if (operatorAlreadyDefined)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_FUNCTION),
|
|
|
|
errmsg("operator %s already exists",
|
|
|
|
operatorName)));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
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.
|
1999-04-11 04:30:59 +02:00
|
|
|
*/
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
|
|
|
* 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-07 07:04:48 +02:00
|
|
|
*/
|
2002-03-29 20:06:29 +01:00
|
|
|
if (!OidIsValid(leftTypeId))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
typeId[0] = rightTypeId;
|
|
|
|
nargs = 1;
|
|
|
|
}
|
2002-03-29 20:06:29 +01:00
|
|
|
else if (!OidIsValid(rightTypeId))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
typeId[0] = leftTypeId;
|
|
|
|
nargs = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
typeId[0] = leftTypeId;
|
|
|
|
typeId[1] = rightTypeId;
|
|
|
|
nargs = 2;
|
|
|
|
}
|
2003-07-04 04:51:34 +02:00
|
|
|
procOid = LookupFuncName(procedureName, nargs, typeId, false);
|
2002-04-17 01:08:12 +02:00
|
|
|
operResultType = get_func_rettype(procOid);
|
2000-11-16 23:30:52 +01:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-05-20 22:28:20 +02:00
|
|
|
* find restriction estimator
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
if (restrictionName)
|
2002-04-17 01:08:12 +02:00
|
|
|
{
|
2002-08-22 02:01:51 +02:00
|
|
|
typeId[0] = INTERNALOID; /* Query */
|
2001-05-20 22:28:20 +02:00
|
|
|
typeId[1] = OIDOID; /* operator OID */
|
2002-08-22 02:01:51 +02:00
|
|
|
typeId[2] = INTERNALOID; /* args list */
|
2001-05-20 22:28:20 +02:00
|
|
|
typeId[3] = INT4OID; /* varRelid */
|
2000-11-16 23:30:52 +01:00
|
|
|
|
2003-07-04 04:51:34 +02:00
|
|
|
restOid = LookupFuncName(restrictionName, 4, typeId, false);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
2002-04-17 01:08:12 +02:00
|
|
|
restOid = InvalidOid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2001-05-20 22:28:20 +02:00
|
|
|
* find join estimator
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
if (joinName)
|
2002-04-17 01:08:12 +02:00
|
|
|
{
|
2002-08-22 02:01:51 +02:00
|
|
|
typeId[0] = INTERNALOID; /* Query */
|
2001-05-20 22:28:20 +02:00
|
|
|
typeId[1] = OIDOID; /* operator OID */
|
2002-08-22 02:01:51 +02:00
|
|
|
typeId[2] = INTERNALOID; /* args list */
|
2003-01-28 23:13:41 +01:00
|
|
|
typeId[3] = INT2OID; /* jointype */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-07-04 04:51:34 +02:00
|
|
|
joinOid = LookupFuncName(joinName, 4, typeId, false);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
2002-04-17 01:08:12 +02:00
|
|
|
joinOid = InvalidOid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* set up values in the operator tuple
|
|
|
|
*/
|
2002-04-17 01:08:12 +02:00
|
|
|
|
|
|
|
for (i = 0; i < Natts_pg_operator; ++i)
|
|
|
|
{
|
|
|
|
values[i] = (Datum) NULL;
|
|
|
|
replaces[i] = 'r';
|
|
|
|
nulls[i] = ' ';
|
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
i = 0;
|
1998-04-01 17:35:33 +02:00
|
|
|
namestrcpy(&oname, operatorName);
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = NameGetDatum(&oname); /* oprname */
|
2002-04-17 01:08:12 +02:00
|
|
|
values[i++] = ObjectIdGetDatum(operatorNamespace); /* oprnamespace */
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = Int32GetDatum(GetUserId()); /* oprowner */
|
2002-04-17 01:08:12 +02:00
|
|
|
values[i++] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); /* oprkind */
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = BoolGetDatum(canHash); /* oprcanhash */
|
|
|
|
values[i++] = ObjectIdGetDatum(leftTypeId); /* oprleft */
|
|
|
|
values[i++] = ObjectIdGetDatum(rightTypeId); /* oprright */
|
|
|
|
values[i++] = ObjectIdGetDatum(operResultType); /* oprresult */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
1999-04-11 04:30:59 +02:00
|
|
|
* Set up the other operators. If they do not currently exist, create
|
|
|
|
* shells in order to get ObjectId's.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
if (commutatorName)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-04-17 01:08:12 +02:00
|
|
|
/* commutator has reversed arg types */
|
|
|
|
commutatorId = get_other_operator(commutatorName,
|
|
|
|
rightTypeId, leftTypeId,
|
|
|
|
operatorName, operatorNamespace,
|
|
|
|
leftTypeId, rightTypeId,
|
|
|
|
true);
|
2002-09-04 22:31:48 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* self-linkage to this operator; will fix below. Note that only
|
|
|
|
* self-linkage for commutation makes sense.
|
2002-04-17 01:08:12 +02:00
|
|
|
*/
|
|
|
|
if (!OidIsValid(commutatorId))
|
|
|
|
selfCommutator = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
commutatorId = InvalidOid;
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = ObjectIdGetDatum(commutatorId); /* oprcom */
|
1999-04-11 04:30:59 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
if (negatorName)
|
|
|
|
{
|
|
|
|
/* negator has same arg types */
|
|
|
|
negatorId = get_other_operator(negatorName,
|
|
|
|
leftTypeId, rightTypeId,
|
|
|
|
operatorName, operatorNamespace,
|
|
|
|
leftTypeId, rightTypeId,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
negatorId = InvalidOid;
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = ObjectIdGetDatum(negatorId); /* oprnegate */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
if (leftSortName)
|
|
|
|
{
|
|
|
|
/* left sort op takes left-side data type */
|
|
|
|
leftSortId = get_other_operator(leftSortName,
|
2002-09-04 22:31:48 +02:00
|
|
|
leftTypeId, leftTypeId,
|
|
|
|
operatorName, operatorNamespace,
|
|
|
|
leftTypeId, rightTypeId,
|
|
|
|
false);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2002-04-17 01:08:12 +02:00
|
|
|
else
|
|
|
|
leftSortId = InvalidOid;
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = ObjectIdGetDatum(leftSortId); /* oprlsortop */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
if (rightSortName)
|
|
|
|
{
|
|
|
|
/* right sort op takes right-side data type */
|
|
|
|
rightSortId = get_other_operator(rightSortName,
|
|
|
|
rightTypeId, rightTypeId,
|
|
|
|
operatorName, operatorNamespace,
|
|
|
|
leftTypeId, rightTypeId,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
rightSortId = InvalidOid;
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = ObjectIdGetDatum(rightSortId); /* oprrsortop */
|
2002-04-17 01:08:12 +02:00
|
|
|
|
|
|
|
if (ltCompareName)
|
|
|
|
{
|
|
|
|
/* comparator has same arg types */
|
|
|
|
ltCompareId = get_other_operator(ltCompareName,
|
|
|
|
leftTypeId, rightTypeId,
|
|
|
|
operatorName, operatorNamespace,
|
|
|
|
leftTypeId, rightTypeId,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ltCompareId = InvalidOid;
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = ObjectIdGetDatum(ltCompareId); /* oprltcmpop */
|
2002-04-17 01:08:12 +02:00
|
|
|
|
|
|
|
if (gtCompareName)
|
|
|
|
{
|
|
|
|
/* comparator has same arg types */
|
|
|
|
gtCompareId = get_other_operator(gtCompareName,
|
|
|
|
leftTypeId, rightTypeId,
|
|
|
|
operatorName, operatorNamespace,
|
|
|
|
leftTypeId, rightTypeId,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
gtCompareId = InvalidOid;
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = ObjectIdGetDatum(gtCompareId); /* oprgtcmpop */
|
2002-04-17 01:08:12 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
values[i++] = ObjectIdGetDatum(procOid); /* oprcode */
|
|
|
|
values[i++] = ObjectIdGetDatum(restOid); /* oprrest */
|
|
|
|
values[i++] = ObjectIdGetDatum(joinOid); /* oprjoin */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2002-04-17 01:08:12 +02:00
|
|
|
* If we are adding to an operator shell, update; else insert
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
if (operatorObjectId)
|
|
|
|
{
|
2002-04-17 01:08:12 +02:00
|
|
|
tup = SearchSysCacheCopy(OPEROID,
|
|
|
|
ObjectIdGetDatum(operatorObjectId),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(tup))
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "cache lookup failed for operator %u",
|
2002-04-17 01:08:12 +02:00
|
|
|
operatorObjectId);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
tup = heap_modifytuple(tup,
|
2005-01-28 00:24:11 +01:00
|
|
|
RelationGetDescr(pg_operator_desc),
|
2002-04-17 01:08:12 +02:00
|
|
|
values,
|
|
|
|
nulls,
|
|
|
|
replaces);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
simple_heap_update(pg_operator_desc, &tup->t_self, tup);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tupDesc = pg_operator_desc->rd_att;
|
|
|
|
tup = heap_formtuple(tupDesc, values, nulls);
|
|
|
|
|
2002-05-22 00:05:55 +02:00
|
|
|
operatorObjectId = simple_heap_insert(pg_operator_desc, tup);
|
1999-11-22 18:56:41 +01:00
|
|
|
}
|
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
/* Must update the indexes in either case */
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogUpdateIndexes(pg_operator_desc, tup);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-07-17 00:12:20 +02:00
|
|
|
/* Add dependencies for the entry */
|
|
|
|
makeOperatorDependencies(tup, RelationGetRelid(pg_operator_desc));
|
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
heap_close(pg_operator_desc, RowExclusiveLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
1999-04-11 04:30:59 +02:00
|
|
|
* 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.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1999-04-11 04:30:59 +02:00
|
|
|
if (selfCommutator)
|
|
|
|
commutatorId = operatorObjectId;
|
|
|
|
|
|
|
|
if (OidIsValid(commutatorId) || OidIsValid(negatorId))
|
1997-09-07 07:04:48 +02:00
|
|
|
OperatorUpd(operatorObjectId, commutatorId, negatorId);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
2002-04-27 05:45:03 +02:00
|
|
|
AclResult aclresult;
|
2002-04-17 01:08:12 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* self-linkage to this operator; caller will fix later. Note that
|
|
|
|
* only self-linkage for commutation makes sense.
|
2002-04-17 01:08:12 +02:00
|
|
|
*/
|
|
|
|
if (!isCommutator)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
|
|
|
errmsg("operator cannot be its own negator or sort operator")));
|
2002-04-17 01:08:12 +02:00
|
|
|
return InvalidOid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* not in catalogs, different from operator, so make shell */
|
2002-04-27 05:45:03 +02:00
|
|
|
|
|
|
|
aclresult = pg_namespace_aclcheck(otherNamespace, GetUserId(),
|
|
|
|
ACL_CREATE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
2003-08-01 02:15:26 +02:00
|
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
|
|
get_namespace_name(otherNamespace));
|
2002-04-27 05:45:03 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
other_oid = OperatorShellMake(otherName,
|
|
|
|
otherNamespace,
|
|
|
|
otherLeftTypeId,
|
|
|
|
otherRightTypeId);
|
|
|
|
return other_oid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1996-07-09 08:22:35 +02:00
|
|
|
* OperatorUpd
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* For a given operator, look up its negator and commutator operators.
|
1999-04-11 04:30:59 +02:00
|
|
|
* If they are defined, but their negator and commutator fields
|
|
|
|
* (respectively) are empty, then use the new operator for neg or comm.
|
1997-09-07 07:04:48 +02:00
|
|
|
* This solves a problem for users who need to insert two new operators
|
|
|
|
* which are the negator or commutator of each other.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
OperatorUpd(Oid baseId, Oid commId, Oid negId)
|
|
|
|
{
|
1998-02-11 20:14:04 +01:00
|
|
|
int i;
|
1997-09-08 04:41:22 +02:00
|
|
|
Relation pg_operator_desc;
|
|
|
|
HeapTuple tup;
|
|
|
|
char nulls[Natts_pg_operator];
|
|
|
|
char replaces[Natts_pg_operator];
|
|
|
|
Datum values[Natts_pg_operator];
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
for (i = 0; i < Natts_pg_operator; ++i)
|
|
|
|
{
|
2001-06-01 04:41:36 +02:00
|
|
|
values[i] = (Datum) 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
replaces[i] = ' ';
|
|
|
|
nulls[i] = ' ';
|
|
|
|
}
|
|
|
|
|
2000-01-18 00:57:48 +01:00
|
|
|
/*
|
|
|
|
* check and update the commutator & negator, if necessary
|
|
|
|
*
|
|
|
|
* First make sure we can see them...
|
|
|
|
*/
|
|
|
|
CommandCounterIncrement();
|
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
tup = SearchSysCacheCopy(OPEROID,
|
|
|
|
ObjectIdGetDatum(commId),
|
|
|
|
0, 0, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
/*
|
|
|
|
* if the commutator and negator are the same operator, do one update.
|
1999-04-11 04:30:59 +02:00
|
|
|
* XXX this is probably useless code --- I doubt it ever makes sense
|
|
|
|
* for commutator and negator to be the same thing...
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
if (commId == negId)
|
|
|
|
{
|
|
|
|
if (HeapTupleIsValid(tup))
|
|
|
|
{
|
2002-04-17 01:08:12 +02:00
|
|
|
Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
if (!OidIsValid(t->oprcom) || !OidIsValid(t->oprnegate))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
if (!OidIsValid(t->oprnegate))
|
|
|
|
{
|
1999-02-03 22:18:02 +01:00
|
|
|
values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId);
|
1997-09-07 07:04:48 +02:00
|
|
|
replaces[Anum_pg_operator_oprnegate - 1] = 'r';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!OidIsValid(t->oprcom))
|
|
|
|
{
|
1999-02-03 22:18:02 +01:00
|
|
|
values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId);
|
1997-09-07 07:04:48 +02:00
|
|
|
replaces[Anum_pg_operator_oprcom - 1] = 'r';
|
|
|
|
}
|
|
|
|
|
|
|
|
tup = heap_modifytuple(tup,
|
2005-01-28 00:24:11 +01:00
|
|
|
RelationGetDescr(pg_operator_desc),
|
1997-09-07 07:04:48 +02:00
|
|
|
values,
|
|
|
|
nulls,
|
|
|
|
replaces);
|
|
|
|
|
2001-01-23 05:32:23 +01:00
|
|
|
simple_heap_update(pg_operator_desc, &tup->t_self, tup);
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogUpdateIndexes(pg_operator_desc, tup);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
heap_close(pg_operator_desc, RowExclusiveLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if commutator and negator are different, do two updates */
|
1999-04-23 02:50:57 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (HeapTupleIsValid(tup) &&
|
1998-09-01 05:29:17 +02:00
|
|
|
!(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprcom)))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId);
|
|
|
|
replaces[Anum_pg_operator_oprcom - 1] = 'r';
|
2002-04-17 01:08:12 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
tup = heap_modifytuple(tup,
|
2005-01-28 00:24:11 +01:00
|
|
|
RelationGetDescr(pg_operator_desc),
|
1997-09-07 07:04:48 +02:00
|
|
|
values,
|
|
|
|
nulls,
|
|
|
|
replaces);
|
|
|
|
|
2001-01-23 05:32:23 +01:00
|
|
|
simple_heap_update(pg_operator_desc, &tup->t_self, tup);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogUpdateIndexes(pg_operator_desc, tup);
|
1999-11-22 18:56:41 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
values[Anum_pg_operator_oprcom - 1] = (Datum) NULL;
|
|
|
|
replaces[Anum_pg_operator_oprcom - 1] = ' ';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check and update the negator, if necessary */
|
|
|
|
|
2002-04-17 01:08:12 +02:00
|
|
|
tup = SearchSysCacheCopy(OPEROID,
|
|
|
|
ObjectIdGetDatum(negId),
|
|
|
|
0, 0, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (HeapTupleIsValid(tup) &&
|
1998-09-01 05:29:17 +02:00
|
|
|
!(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprnegate)))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId);
|
|
|
|
replaces[Anum_pg_operator_oprnegate - 1] = 'r';
|
2002-04-17 01:08:12 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
tup = heap_modifytuple(tup,
|
2005-01-28 00:24:11 +01:00
|
|
|
RelationGetDescr(pg_operator_desc),
|
1997-09-07 07:04:48 +02:00
|
|
|
values,
|
|
|
|
nulls,
|
|
|
|
replaces);
|
|
|
|
|
2001-01-23 05:32:23 +01:00
|
|
|
simple_heap_update(pg_operator_desc, &tup->t_self, tup);
|
1999-11-22 18:56:41 +01:00
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogUpdateIndexes(pg_operator_desc, tup);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1999-09-18 21:08:25 +02:00
|
|
|
heap_close(pg_operator_desc, RowExclusiveLock);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2002-07-17 00:12:20 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create dependencies for a new operator (either a freshly inserted
|
|
|
|
* complete operator, a new shell operator, or a just-updated shell).
|
|
|
|
*
|
2002-07-18 18:47:26 +02:00
|
|
|
* NB: the OidIsValid tests in this routine are necessary, in case
|
2002-07-17 00:12:20 +02:00
|
|
|
* the given operator is a shell.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
makeOperatorDependencies(HeapTuple tuple, Oid pg_operator_relid)
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple);
|
|
|
|
ObjectAddress myself,
|
|
|
|
referenced;
|
2002-07-17 00:12:20 +02:00
|
|
|
|
|
|
|
myself.classId = pg_operator_relid;
|
2002-07-20 07:16:59 +02:00
|
|
|
myself.objectId = HeapTupleGetOid(tuple);
|
2002-07-17 00:12:20 +02:00
|
|
|
myself.objectSubId = 0;
|
|
|
|
|
|
|
|
/* In case we are updating a shell, delete any existing entries */
|
|
|
|
deleteDependencyRecordsFor(myself.classId, myself.objectId);
|
|
|
|
|
2002-07-18 18:47:26 +02:00
|
|
|
/* Dependency on namespace */
|
|
|
|
if (OidIsValid(oper->oprnamespace))
|
|
|
|
{
|
|
|
|
referenced.classId = get_system_catalog_relid(NamespaceRelationName);
|
|
|
|
referenced.objectId = oper->oprnamespace;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
|
2002-07-17 00:12:20 +02:00
|
|
|
/* Dependency on left type */
|
|
|
|
if (OidIsValid(oper->oprleft))
|
|
|
|
{
|
|
|
|
referenced.classId = RelOid_pg_type;
|
|
|
|
referenced.objectId = oper->oprleft;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dependency on right type */
|
|
|
|
if (OidIsValid(oper->oprright))
|
|
|
|
{
|
|
|
|
referenced.classId = RelOid_pg_type;
|
|
|
|
referenced.objectId = oper->oprright;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dependency on result type */
|
|
|
|
if (OidIsValid(oper->oprresult))
|
|
|
|
{
|
|
|
|
referenced.classId = RelOid_pg_type;
|
|
|
|
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, oprnegate, oprlsortop, oprrsortop, oprltcmpop,
|
2002-09-04 22:31:48 +02:00
|
|
|
* oprgtcmpop. 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.
|
2002-07-17 00:12:20 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* Dependency on implementation function */
|
|
|
|
if (OidIsValid(oper->oprcode))
|
|
|
|
{
|
|
|
|
referenced.classId = RelOid_pg_proc;
|
|
|
|
referenced.objectId = oper->oprcode;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dependency on restriction selectivity function */
|
|
|
|
if (OidIsValid(oper->oprrest))
|
|
|
|
{
|
|
|
|
referenced.classId = RelOid_pg_proc;
|
|
|
|
referenced.objectId = oper->oprrest;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Dependency on join selectivity function */
|
|
|
|
if (OidIsValid(oper->oprjoin))
|
|
|
|
{
|
|
|
|
referenced.classId = RelOid_pg_proc;
|
|
|
|
referenced.objectId = oper->oprjoin;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
}
|