summaryrefslogtreecommitdiff
path: root/src/backend/commands/opclasscmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/opclasscmds.c')
-rw-r--r--src/backend/commands/opclasscmds.c733
1 files changed, 504 insertions, 229 deletions
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index d4dec746501..8b1b27ef3e7 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -2,14 +2,14 @@
*
* opclasscmds.c
*
- * Routines for opclass manipulation commands
+ * Routines for opclass (and opfamily) manipulation commands
*
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.50 2006/12/18 18:56:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.51 2006/12/23 00:43:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -26,6 +26,7 @@
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
+#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
@@ -42,27 +43,187 @@
/*
* We use lists of this struct type to keep track of both operators and
- * procedures during DefineOpClass.
+ * procedures while building or adding to an opfamily.
*/
typedef struct
{
Oid object; /* operator or support proc's OID */
int number; /* strategy or support proc number */
- Oid subtype; /* subtype */
+ Oid lefttype; /* lefttype */
+ Oid righttype; /* righttype */
bool recheck; /* oper recheck flag (unused for proc) */
-} OpClassMember;
+} OpFamilyMember;
-static Oid assignOperSubtype(Oid amoid, Oid typeoid, Oid operOid);
-static Oid assignProcSubtype(Oid amoid, Oid typeoid, Oid procOid);
-static void addClassMember(List **list, OpClassMember *member, bool isProc);
-static void storeOperators(Oid opclassoid, List *operators);
-static void storeProcedures(Oid opclassoid, List *procedures);
+static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
+static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
+static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc);
+static void storeOperators(Oid amoid, Oid opfamilyoid, Oid opclassoid,
+ List *operators);
+static void storeProcedures(Oid amoid, Oid opfamilyoid, Oid opclassoid,
+ List *procedures);
static void AlterOpClassOwner_internal(Relation rel, HeapTuple tuple,
Oid newOwnerId);
/*
+ * OpFamilyCacheLookup
+ * Look up an existing opfamily by name.
+ *
+ * Returns a syscache tuple reference, or NULL if not found.
+ */
+static HeapTuple
+OpFamilyCacheLookup(Oid amID, List *opfamilyname)
+{
+ char *schemaname;
+ char *opfname;
+
+ /* deconstruct the name list */
+ DeconstructQualifiedName(opfamilyname, &schemaname, &opfname);
+
+ if (schemaname)
+ {
+ /* Look in specific schema only */
+ Oid namespaceId;
+
+ namespaceId = LookupExplicitNamespace(schemaname);
+ return SearchSysCache(OPFAMILYAMNAMENSP,
+ ObjectIdGetDatum(amID),
+ PointerGetDatum(opfname),
+ ObjectIdGetDatum(namespaceId),
+ 0);
+ }
+ else
+ {
+ /* Unqualified opfamily name, so search the search path */
+ Oid opfID = OpfamilynameGetOpfid(amID, opfname);
+
+ if (!OidIsValid(opfID))
+ return NULL;
+ return SearchSysCache(OPFAMILYOID,
+ ObjectIdGetDatum(opfID),
+ 0, 0, 0);
+ }
+}
+
+/*
+ * OpClassCacheLookup
+ * Look up an existing opclass by name.
+ *
+ * Returns a syscache tuple reference, or NULL if not found.
+ */
+static HeapTuple
+OpClassCacheLookup(Oid amID, List *opclassname)
+{
+ char *schemaname;
+ char *opcname;
+
+ /* deconstruct the name list */
+ DeconstructQualifiedName(opclassname, &schemaname, &opcname);
+
+ if (schemaname)
+ {
+ /* Look in specific schema only */
+ Oid namespaceId;
+
+ namespaceId = LookupExplicitNamespace(schemaname);
+ return SearchSysCache(CLAAMNAMENSP,
+ ObjectIdGetDatum(amID),
+ PointerGetDatum(opcname),
+ ObjectIdGetDatum(namespaceId),
+ 0);
+ }
+ else
+ {
+ /* Unqualified opclass name, so search the search path */
+ Oid opcID = OpclassnameGetOpcid(amID, opcname);
+
+ if (!OidIsValid(opcID))
+ return NULL;
+ return SearchSysCache(CLAOID,
+ ObjectIdGetDatum(opcID),
+ 0, 0, 0);
+ }
+}
+
+/*
+ * CreateOpFamily
+ * Internal routine to make the catalog entry for a new operator family.
+ *
+ * Caller must have done permissions checks etc. already.
+ */
+static Oid
+CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
+{
+ Oid opfamilyoid;
+ Relation rel;
+ HeapTuple tup;
+ Datum values[Natts_pg_opfamily];
+ char nulls[Natts_pg_opfamily];
+ NameData opfName;
+ ObjectAddress myself,
+ referenced;
+
+ rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
+
+ /*
+ * Make sure there is no existing opfamily of this name (this is just to
+ * give a more friendly error message than "duplicate key").
+ */
+ if (SearchSysCacheExists(OPFAMILYAMNAMENSP,
+ ObjectIdGetDatum(amoid),
+ CStringGetDatum(opfname),
+ ObjectIdGetDatum(namespaceoid),
+ 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("operator family \"%s\" for access method \"%s\" already exists",
+ opfname, amname)));
+
+ /*
+ * Okay, let's create the pg_opfamily entry.
+ */
+ memset(values, 0, sizeof(values));
+ memset(nulls, ' ', sizeof(nulls));
+
+ values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
+ namestrcpy(&opfName, opfname);
+ values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
+ values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+ values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
+
+ tup = heap_formtuple(rel->rd_att, values, nulls);
+
+ opfamilyoid = simple_heap_insert(rel, tup);
+
+ CatalogUpdateIndexes(rel, tup);
+
+ heap_freetuple(tup);
+
+ /*
+ * Create dependencies for the opfamily proper. Note: we do not create a
+ * dependency link to the AM, because we don't currently support DROP
+ * ACCESS METHOD.
+ */
+ myself.classId = OperatorFamilyRelationId;
+ myself.objectId = opfamilyoid;
+ myself.objectSubId = 0;
+
+ /* dependency on namespace */
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = namespaceoid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* dependency on owner */
+ recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
+
+ heap_close(rel, RowExclusiveLock);
+
+ return opfamilyoid;
+}
+
+/*
* DefineOpClass
* Define a new index operator class.
*/
@@ -74,12 +235,13 @@ DefineOpClass(CreateOpClassStmt *stmt)
typeoid, /* indexable datatype oid */
storageoid, /* storage datatype oid, if any */
namespaceoid, /* namespace to create opclass in */
+ opfamilyoid, /* oid of containing opfamily */
opclassoid; /* oid of opclass we create */
int maxOpNumber, /* amstrategies value */
maxProcNumber; /* amsupport value */
bool amstorage; /* amstorage flag */
- List *operators; /* OpClassMember list for operators */
- List *procedures; /* OpClassMember list for support procs */
+ List *operators; /* OpFamilyMember list for operators */
+ List *procedures; /* OpFamilyMember list for support procs */
ListCell *l;
Relation rel;
HeapTuple tup;
@@ -88,7 +250,6 @@ DefineOpClass(CreateOpClassStmt *stmt)
char nulls[Natts_pg_opclass];
AclResult aclresult;
NameData opcName;
- int i;
ObjectAddress myself,
referenced;
@@ -161,6 +322,52 @@ DefineOpClass(CreateOpClassStmt *stmt)
format_type_be(typeoid));
#endif
+ /*
+ * Look up the containing operator family, or create one if FAMILY option
+ * was omitted and there's not a match already.
+ */
+ if (stmt->opfamilyname)
+ {
+ tup = OpFamilyCacheLookup(amoid, stmt->opfamilyname);
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+ NameListToString(stmt->opfamilyname), stmt->amname)));
+ opfamilyoid = HeapTupleGetOid(tup);
+ /*
+ * XXX given the superuser check above, there's no need for an
+ * ownership check here
+ */
+ ReleaseSysCache(tup);
+ }
+ else
+ {
+ /* Lookup existing family of same name and namespace */
+ tup = SearchSysCache(OPFAMILYAMNAMENSP,
+ ObjectIdGetDatum(amoid),
+ PointerGetDatum(opcname),
+ ObjectIdGetDatum(namespaceoid),
+ 0);
+ if (HeapTupleIsValid(tup))
+ {
+ opfamilyoid = HeapTupleGetOid(tup);
+ /*
+ * XXX given the superuser check above, there's no need for an
+ * ownership check here
+ */
+ ReleaseSysCache(tup);
+ }
+ else
+ {
+ /*
+ * Create it ... again no need for more permissions ...
+ */
+ opfamilyoid = CreateOpFamily(stmt->amname, opcname,
+ namespaceoid, amoid);
+ }
+ }
+
operators = NIL;
procedures = NIL;
@@ -175,7 +382,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
CreateOpClassItem *item = lfirst(l);
Oid operOid;
Oid funcOid;
- OpClassMember *member;
+ OpFamilyMember *member;
Assert(IsA(item, CreateOpClassItem));
switch (item->itemtype)
@@ -217,12 +424,12 @@ DefineOpClass(CreateOpClassStmt *stmt)
#endif
/* Save the info */
- member = (OpClassMember *) palloc0(sizeof(OpClassMember));
+ member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
member->object = operOid;
member->number = item->number;
- member->subtype = assignOperSubtype(amoid, typeoid, operOid);
member->recheck = item->recheck;
- addClassMember(&operators, member, false);
+ assignOperTypes(member, amoid, typeoid);
+ addFamilyMember(&operators, member, false);
break;
case OPCLASS_ITEM_FUNCTION:
if (item->number <= 0 || item->number > maxProcNumber)
@@ -242,11 +449,11 @@ DefineOpClass(CreateOpClassStmt *stmt)
#endif
/* Save the info */
- member = (OpClassMember *) palloc0(sizeof(OpClassMember));
+ member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
member->object = funcOid;
member->number = item->number;
- member->subtype = assignProcSubtype(amoid, typeoid, funcOid);
- addClassMember(&procedures, member, true);
+ assignProcTypes(member, amoid, typeoid);
+ addFamilyMember(&procedures, member, true);
break;
case OPCLASS_ITEM_STORAGETYPE:
if (OidIsValid(storageoid))
@@ -311,7 +518,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
SysScanDesc scan;
ScanKeyInit(&skey[0],
- Anum_pg_opclass_opcamid,
+ Anum_pg_opclass_opcmethod,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(amoid));
@@ -338,21 +545,18 @@ DefineOpClass(CreateOpClassStmt *stmt)
/*
* Okay, let's create the pg_opclass entry.
*/
- for (i = 0; i < Natts_pg_opclass; ++i)
- {
- nulls[i] = ' ';
- values[i] = (Datum) NULL; /* redundant, but safe */
- }
+ memset(values, 0, sizeof(values));
+ memset(nulls, ' ', sizeof(nulls));
- i = 0;
- values[i++] = ObjectIdGetDatum(amoid); /* opcamid */
+ values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
namestrcpy(&opcName, opcname);
- values[i++] = NameGetDatum(&opcName); /* opcname */
- values[i++] = ObjectIdGetDatum(namespaceoid); /* opcnamespace */
- values[i++] = ObjectIdGetDatum(GetUserId()); /* opcowner */
- values[i++] = ObjectIdGetDatum(typeoid); /* opcintype */
- values[i++] = BoolGetDatum(stmt->isDefault); /* opcdefault */
- values[i++] = ObjectIdGetDatum(storageoid); /* opckeytype */
+ values[Anum_pg_opclass_opcname - 1] = NameGetDatum(&opcName);
+ values[Anum_pg_opclass_opcnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+ values[Anum_pg_opclass_opcowner - 1] = ObjectIdGetDatum(GetUserId());
+ values[Anum_pg_opclass_opcfamily - 1] = ObjectIdGetDatum(opfamilyoid);
+ values[Anum_pg_opclass_opcintype - 1] = ObjectIdGetDatum(typeoid);
+ values[Anum_pg_opclass_opcdefault - 1] = BoolGetDatum(stmt->isDefault);
+ values[Anum_pg_opclass_opckeytype - 1] = ObjectIdGetDatum(storageoid);
tup = heap_formtuple(rel->rd_att, values, nulls);
@@ -364,14 +568,15 @@ DefineOpClass(CreateOpClassStmt *stmt)
/*
* Now add tuples to pg_amop and pg_amproc tying in the operators and
- * functions.
+ * functions. Dependencies on them are inserted, too.
*/
- storeOperators(opclassoid, operators);
- storeProcedures(opclassoid, procedures);
+ storeOperators(amoid, opfamilyoid, opclassoid, operators);
+ storeProcedures(amoid, opfamilyoid, opclassoid, procedures);
/*
- * Create dependencies. Note: we do not create a dependency link to the
- * AM, because we don't currently support DROP ACCESS METHOD.
+ * Create dependencies for the opclass proper. Note: we do not create a
+ * dependency link to the AM, because we don't currently support DROP
+ * ACCESS METHOD.
*/
myself.classId = OperatorClassRelationId;
myself.objectId = opclassoid;
@@ -383,6 +588,12 @@ DefineOpClass(CreateOpClassStmt *stmt)
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ /* dependency on opfamily */
+ referenced.classId = OperatorFamilyRelationId;
+ referenced.objectId = opfamilyoid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+
/* dependency on indexed datatype */
referenced.classId = TypeRelationId;
referenced.objectId = typeoid;
@@ -398,28 +609,6 @@ DefineOpClass(CreateOpClassStmt *stmt)
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
- /* dependencies on operators */
- foreach(l, operators)
- {
- OpClassMember *op = (OpClassMember *) lfirst(l);
-
- referenced.classId = OperatorRelationId;
- referenced.objectId = op->object;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- }
-
- /* dependencies on procedures */
- foreach(l, procedures)
- {
- OpClassMember *proc = (OpClassMember *) lfirst(l);
-
- referenced.classId = ProcedureRelationId;
- referenced.objectId = proc->object;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- }
-
/* dependency on owner */
recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
@@ -427,139 +616,158 @@ DefineOpClass(CreateOpClassStmt *stmt)
}
/*
- * Determine the subtype to assign to an operator, and do any validity
- * checking we can manage
- *
- * Currently this is done using hardwired rules; we don't let the user
- * specify it directly.
+ * Determine the lefttype/righttype to assign to an operator,
+ * and do any validity checking we can manage.
*/
-static Oid
-assignOperSubtype(Oid amoid, Oid typeoid, Oid operOid)
+static void
+assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
{
- Oid subtype;
Operator optup;
Form_pg_operator opform;
- /* Subtypes are currently only supported by btree, others use 0 */
- if (amoid != BTREE_AM_OID)
- return InvalidOid;
-
+ /* Fetch the operator definition */
optup = SearchSysCache(OPEROID,
- ObjectIdGetDatum(operOid),
+ ObjectIdGetDatum(member->object),
0, 0, 0);
if (optup == NULL)
- elog(ERROR, "cache lookup failed for operator %u", operOid);
+ elog(ERROR, "cache lookup failed for operator %u", member->object);
opform = (Form_pg_operator) GETSTRUCT(optup);
/*
- * btree operators must be binary ops returning boolean, and the left-side
- * input type must match the operator class' input type.
+ * Opfamily operators must be binary ops returning boolean.
*/
if (opform->oprkind != 'b')
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("btree operators must be binary")));
+ errmsg("index operators must be binary")));
if (opform->oprresult != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("btree operators must return boolean")));
- if (opform->oprleft != typeoid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("btree operators must have index type as left input")));
+ errmsg("index operators must return boolean")));
/*
- * The subtype is "default" (0) if oprright matches the operator class,
- * otherwise it is oprright.
+ * If lefttype/righttype isn't specified, use the operator's input types
*/
- if (opform->oprright == typeoid)
- subtype = InvalidOid;
- else
- subtype = opform->oprright;
+ if (!OidIsValid(member->lefttype))
+ member->lefttype = opform->oprleft;
+ if (!OidIsValid(member->righttype))
+ member->righttype = opform->oprright;
+
ReleaseSysCache(optup);
- return subtype;
}
/*
- * Determine the subtype to assign to a support procedure, and do any validity
- * checking we can manage
- *
- * Currently this is done using hardwired rules; we don't let the user
- * specify it directly.
+ * Determine the lefttype/righttype to assign to a support procedure,
+ * and do any validity checking we can manage.
*/
-static Oid
-assignProcSubtype(Oid amoid, Oid typeoid, Oid procOid)
+static void
+assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
{
- Oid subtype;
HeapTuple proctup;
Form_pg_proc procform;
- /* Subtypes are currently only supported by btree, others use 0 */
- if (amoid != BTREE_AM_OID)
- return InvalidOid;
-
+ /* Fetch the procedure definition */
proctup = SearchSysCache(PROCOID,
- ObjectIdGetDatum(procOid),
+ ObjectIdGetDatum(member->object),
0, 0, 0);
if (proctup == NULL)
- elog(ERROR, "cache lookup failed for function %u", procOid);
+ elog(ERROR, "cache lookup failed for function %u", member->object);
procform = (Form_pg_proc) GETSTRUCT(proctup);
/*
- * btree support procs must be 2-arg procs returning int4, and the first
- * input type must match the operator class' input type.
+ * btree support procs must be 2-arg procs returning int4; hash support
+ * procs must be 1-arg procs returning int4; otherwise we don't know.
*/
- if (procform->pronargs != 2)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("btree procedures must have two arguments")));
- if (procform->prorettype != INT4OID)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("btree procedures must return integer")));
- if (procform->proargtypes.values[0] != typeoid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("btree procedures must have index type as first input")));
+ if (amoid == BTREE_AM_OID)
+ {
+ if (procform->pronargs != 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree procedures must have two arguments")));
+ if (procform->prorettype != INT4OID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree procedures must return integer")));
- /*
- * The subtype is "default" (0) if second input type matches the operator
- * class, otherwise it is the second input type.
- */
- if (procform->proargtypes.values[1] == typeoid)
- subtype = InvalidOid;
+ /*
+ * If lefttype/righttype isn't specified, use the proc's input types
+ */
+ if (!OidIsValid(member->lefttype))
+ member->lefttype = procform->proargtypes.values[0];
+ if (!OidIsValid(member->righttype))
+ member->righttype = procform->proargtypes.values[1];
+ }
+ else if (amoid == HASH_AM_OID)
+ {
+ if (procform->pronargs != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("hash procedures must have one argument")));
+ if (procform->prorettype != INT4OID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("hash procedures must return integer")));
+
+ /*
+ * If lefttype/righttype isn't specified, use the proc's input type
+ */
+ if (!OidIsValid(member->lefttype))
+ member->lefttype = procform->proargtypes.values[0];
+ if (!OidIsValid(member->righttype))
+ member->righttype = procform->proargtypes.values[0];
+ }
else
- subtype = procform->proargtypes.values[1];
+ {
+ /*
+ * The default for GiST and GIN in CREATE OPERATOR CLASS is to use
+ * the class' opcintype as lefttype and righttype. In CREATE or
+ * ALTER OPERATOR FAMILY, opcintype isn't available, so make the
+ * user specify the types.
+ */
+ if (!OidIsValid(member->lefttype))
+ member->lefttype = typeoid;
+ if (!OidIsValid(member->righttype))
+ member->righttype = typeoid;
+ if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("associated data types must be specified for index support procedure")));
+ }
+
ReleaseSysCache(proctup);
- return subtype;
}
/*
- * Add a new class member to the appropriate list, after checking for
+ * Add a new family member to the appropriate list, after checking for
* duplicated strategy or proc number.
*/
static void
-addClassMember(List **list, OpClassMember *member, bool isProc)
+addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
{
ListCell *l;
foreach(l, *list)
{
- OpClassMember *old = (OpClassMember *) lfirst(l);
+ OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
if (old->number == member->number &&
- old->subtype == member->subtype)
+ old->lefttype == member->lefttype &&
+ old->righttype == member->righttype)
{
if (isProc)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("procedure number %d appears more than once",
- member->number)));
+ errmsg("procedure number %d for (%s,%s) appears more than once",
+ member->number,
+ format_type_be(member->lefttype),
+ format_type_be(member->righttype))));
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("operator number %d appears more than once",
- member->number)));
+ errmsg("operator number %d for (%s,%s) appears more than once",
+ member->number,
+ format_type_be(member->lefttype),
+ format_type_be(member->righttype))));
}
}
*list = lappend(*list, member);
@@ -567,43 +775,80 @@ addClassMember(List **list, OpClassMember *member, bool isProc)
/*
* Dump the operators to pg_amop
+ *
+ * We also make dependency entries in pg_depend for the opfamily entries.
+ * If opclassoid is valid then make an INTERNAL dependency on that opclass,
+ * else make an AUTO dependency on the opfamily.
*/
static void
-storeOperators(Oid opclassoid, List *operators)
+storeOperators(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *operators)
{
Relation rel;
Datum values[Natts_pg_amop];
char nulls[Natts_pg_amop];
HeapTuple tup;
+ Oid entryoid;
+ ObjectAddress myself,
+ referenced;
ListCell *l;
- int i;
rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
foreach(l, operators)
{
- OpClassMember *op = (OpClassMember *) lfirst(l);
+ OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
- for (i = 0; i < Natts_pg_amop; ++i)
- {
- nulls[i] = ' ';
- values[i] = (Datum) NULL;
- }
+ /* Create the pg_amop entry */
+ memset(values, 0, sizeof(values));
+ memset(nulls, ' ', sizeof(nulls));
- i = 0;
- values[i++] = ObjectIdGetDatum(opclassoid); /* amopclaid */
- values[i++] = ObjectIdGetDatum(op->subtype); /* amopsubtype */
- values[i++] = Int16GetDatum(op->number); /* amopstrategy */
- values[i++] = BoolGetDatum(op->recheck); /* amopreqcheck */
- values[i++] = ObjectIdGetDatum(op->object); /* amopopr */
+ values[Anum_pg_amop_amopfamily - 1] = ObjectIdGetDatum(opfamilyoid);
+ values[Anum_pg_amop_amoplefttype - 1] = ObjectIdGetDatum(op->lefttype);
+ values[Anum_pg_amop_amoprighttype - 1] = ObjectIdGetDatum(op->righttype);
+ values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
+ values[Anum_pg_amop_amopreqcheck - 1] = BoolGetDatum(op->recheck);
+ values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
+ values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
tup = heap_formtuple(rel->rd_att, values, nulls);
- simple_heap_insert(rel, tup);
+ entryoid = simple_heap_insert(rel, tup);
CatalogUpdateIndexes(rel, tup);
heap_freetuple(tup);
+
+ /* Make its dependencies */
+ myself.classId = AccessMethodOperatorRelationId;
+ myself.objectId = entryoid;
+ myself.objectSubId = 0;
+
+ referenced.classId = OperatorRelationId;
+ referenced.objectId = op->object;
+ referenced.objectSubId = 0;
+
+ if (OidIsValid(opclassoid))
+ {
+ /* if contained in an opclass, use a NORMAL dep on operator */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* ... and an INTERNAL dep on the opclass */
+ referenced.classId = OperatorClassRelationId;
+ referenced.objectId = opclassoid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+ else
+ {
+ /* if "loose" in the opfamily, use a AUTO dep on operator */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+
+ /* ... and an AUTO dep on the opfamily */
+ referenced.classId = OperatorFamilyRelationId;
+ referenced.objectId = opfamilyoid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+ }
}
heap_close(rel, RowExclusiveLock);
@@ -611,42 +856,78 @@ storeOperators(Oid opclassoid, List *operators)
/*
* Dump the procedures (support routines) to pg_amproc
+ *
+ * We also make dependency entries in pg_depend for the opfamily entries.
+ * If opclassoid is valid then make an INTERNAL dependency on that opclass,
+ * else make an AUTO dependency on the opfamily.
*/
static void
-storeProcedures(Oid opclassoid, List *procedures)
+storeProcedures(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *procedures)
{
Relation rel;
Datum values[Natts_pg_amproc];
char nulls[Natts_pg_amproc];
HeapTuple tup;
+ Oid entryoid;
+ ObjectAddress myself,
+ referenced;
ListCell *l;
- int i;
rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
foreach(l, procedures)
{
- OpClassMember *proc = (OpClassMember *) lfirst(l);
+ OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
- for (i = 0; i < Natts_pg_amproc; ++i)
- {
- nulls[i] = ' ';
- values[i] = (Datum) NULL;
- }
+ /* Create the pg_amproc entry */
+ memset(values, 0, sizeof(values));
+ memset(nulls, ' ', sizeof(nulls));
- i = 0;
- values[i++] = ObjectIdGetDatum(opclassoid); /* amopclaid */
- values[i++] = ObjectIdGetDatum(proc->subtype); /* amprocsubtype */
- values[i++] = Int16GetDatum(proc->number); /* amprocnum */
- values[i++] = ObjectIdGetDatum(proc->object); /* amproc */
+ values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
+ values[Anum_pg_amproc_amproclefttype - 1] = ObjectIdGetDatum(proc->lefttype);
+ values[Anum_pg_amproc_amprocrighttype - 1] = ObjectIdGetDatum(proc->righttype);
+ values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
+ values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);
tup = heap_formtuple(rel->rd_att, values, nulls);
- simple_heap_insert(rel, tup);
+ entryoid = simple_heap_insert(rel, tup);
CatalogUpdateIndexes(rel, tup);
heap_freetuple(tup);
+
+ /* Make its dependencies */
+ myself.classId = AccessMethodProcedureRelationId;
+ myself.objectId = entryoid;
+ myself.objectSubId = 0;
+
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = proc->object;
+ referenced.objectSubId = 0;
+
+ if (OidIsValid(opclassoid))
+ {
+ /* if contained in an opclass, use a NORMAL dep on procedure */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* ... and an INTERNAL dep on the opclass */
+ referenced.classId = OperatorClassRelationId;
+ referenced.objectId = opclassoid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+ else
+ {
+ /* if "loose" in the opfamily, use a AUTO dep on procedure */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+
+ /* ... and an AUTO dep on the opfamily */
+ referenced.classId = OperatorFamilyRelationId;
+ referenced.objectId = opfamilyoid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+ }
}
heap_close(rel, RowExclusiveLock);
@@ -662,8 +943,6 @@ RemoveOpClass(RemoveOpClassStmt *stmt)
{
Oid amID,
opcID;
- char *schemaname;
- char *opcname;
HeapTuple tuple;
ObjectAddress object;
@@ -682,49 +961,9 @@ RemoveOpClass(RemoveOpClassStmt *stmt)
/*
* Look up the opclass.
*/
-
- /* deconstruct the name list */
- DeconstructQualifiedName(stmt->opclassname, &schemaname, &opcname);
-
- if (schemaname)
- {
- /* Look in specific schema only */
- Oid namespaceId;
-
- namespaceId = LookupExplicitNamespace(schemaname);
- tuple = SearchSysCache(CLAAMNAMENSP,
- ObjectIdGetDatum(amID),
- PointerGetDatum(opcname),
- ObjectIdGetDatum(namespaceId),
- 0);
- }
- else
- {
- /* Unqualified opclass name, so search the search path */
- opcID = OpclassnameGetOpcid(amID, opcname);
- if (!OidIsValid(opcID))
- {
- if (!stmt->missing_ok)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("operator class \"%s\" does not exist for access method \"%s\"",
- opcname, stmt->amname)));
- else
- ereport(NOTICE,
- (errmsg("operator class \"%s\" does not exist for access method \"%s\"",
- opcname, stmt->amname)));
-
- return;
- }
-
- tuple = SearchSysCache(CLAOID,
- ObjectIdGetDatum(opcID),
- 0, 0, 0);
- }
-
+ tuple = OpClassCacheLookup(amID, stmt->opclassname);
if (!HeapTupleIsValid(tuple))
{
-
if (!stmt->missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -759,19 +998,35 @@ RemoveOpClass(RemoveOpClassStmt *stmt)
}
/*
- * Guts of opclass deletion.
+ * Deletion subroutines for use by dependency.c.
*/
void
+RemoveOpFamilyById(Oid opfamilyOid)
+{
+ Relation rel;
+ HeapTuple tup;
+
+ rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache(OPFAMILYOID,
+ ObjectIdGetDatum(opfamilyOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);
+
+ simple_heap_delete(rel, &tup->t_self);
+
+ ReleaseSysCache(tup);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+void
RemoveOpClassById(Oid opclassOid)
{
Relation rel;
HeapTuple tup;
- ScanKeyData skey[1];
- SysScanDesc scan;
- /*
- * First remove the pg_opclass entry itself.
- */
rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
tup = SearchSysCache(CLAOID,
@@ -785,41 +1040,61 @@ RemoveOpClassById(Oid opclassOid)
ReleaseSysCache(tup);
heap_close(rel, RowExclusiveLock);
+}
+
+void
+RemoveAmOpEntryById(Oid entryOid)
+{
+ Relation rel;
+ HeapTuple tup;
+ ScanKeyData skey[1];
+ SysScanDesc scan;
- /*
- * Remove associated entries in pg_amop.
- */
ScanKeyInit(&skey[0],
- Anum_pg_amop_amopclaid,
+ ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(opclassOid));
+ ObjectIdGetDatum(entryOid));
rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
- scan = systable_beginscan(rel, AccessMethodStrategyIndexId, true,
+ scan = systable_beginscan(rel, AccessMethodOperatorOidIndexId, true,
SnapshotNow, 1, skey);
- while (HeapTupleIsValid(tup = systable_getnext(scan)))
- simple_heap_delete(rel, &tup->t_self);
+ /* we expect exactly one match */
+ tup = systable_getnext(scan);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "could not find tuple for amop entry %u", entryOid);
+
+ simple_heap_delete(rel, &tup->t_self);
systable_endscan(scan);
heap_close(rel, RowExclusiveLock);
+}
+
+void
+RemoveAmProcEntryById(Oid entryOid)
+{
+ Relation rel;
+ HeapTuple tup;
+ ScanKeyData skey[1];
+ SysScanDesc scan;
- /*
- * Remove associated entries in pg_amproc.
- */
ScanKeyInit(&skey[0],
- Anum_pg_amproc_amopclaid,
+ ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(opclassOid));
+ ObjectIdGetDatum(entryOid));
rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
- scan = systable_beginscan(rel, AccessMethodProcedureIndexId, true,
+ scan = systable_beginscan(rel, AccessMethodProcedureOidIndexId, true,
SnapshotNow, 1, skey);
- while (HeapTupleIsValid(tup = systable_getnext(scan)))
- simple_heap_delete(rel, &tup->t_self);
+ /* we expect exactly one match */
+ tup = systable_getnext(scan);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "could not find tuple for amproc entry %u", entryOid);
+
+ simple_heap_delete(rel, &tup->t_self);
systable_endscan(scan);
heap_close(rel, RowExclusiveLock);
@@ -1022,7 +1297,7 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
/*
* The first parameter is pg_opclass, opened and suitably locked. The second
- * parameter is the tuple from pg_opclass we want to modify.
+ * parameter is a copy of the tuple from pg_opclass we want to modify.
*/
static void
AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)