responsibility to define a valid operator class.
+ it doesn't already exist).
+ The name of the existing operator family to add this operator class to.
+ used (creating it, if it doesn't already exist).
as the operator class's data type.
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.52 2007/01/05 22:19:26 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.53 2007/01/23 05:07:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
} OpFamilyMember;
+static void AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+ int maxOpNumber, int maxProcNumber,
+ List *items);
+static void AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+ int maxOpNumber, int maxProcNumber,
+ List *items);
+static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
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 storeOperators(List *opfamilyname, Oid amoid,
+ Oid opfamilyoid, Oid opclassoid,
+ List *operators, bool isAdd);
+static void storeProcedures(List *opfamilyname, Oid amoid,
+ Oid opfamilyoid, Oid opclassoid,
+ List *procedures, bool isAdd);
+static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+ List *operators);
+static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+ List *procedures);
static void AlterOpClassOwner_internal(Relation rel, HeapTuple tuple,
Oid newOwnerId);
+static void AlterOpFamilyOwner_internal(Relation rel, HeapTuple tuple,
+ Oid newOwnerId);
/*
member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
member->object = funcOid;
member->number = item->number;
+
+ /* allow overriding of the function's actual arg types */
+ if (item->class_args)
+ processTypesSpec(item->class_args,
+ &member->lefttype, &member->righttype);
+
assignProcTypes(member, amoid, typeoid);
addFamilyMember(&procedures, member, true);
break;
* Now add tuples to pg_amop and pg_amproc tying in the operators and
* functions. Dependencies on them are inserted, too.
*/
- storeOperators(amoid, opfamilyoid, opclassoid, operators);
- storeProcedures(amoid, opfamilyoid, opclassoid, procedures);
+ storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
+ opclassoid, operators, false);
+ storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
+ opclassoid, procedures, false);
/*
* Create dependencies for the opclass proper. Note: we do not create a
heap_close(rel, RowExclusiveLock);
}
+
/*
- * Determine the lefttype/righttype to assign to an operator,
- * and do any validity checking we can manage.
+ * DefineOpFamily
+ * Define a new index operator family.
*/
-static void
-assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
+void
+DefineOpFamily(CreateOpFamilyStmt *stmt)
{
- Operator optup;
- Form_pg_operator opform;
+ char *opfname; /* name of opfamily we're creating */
+ Oid amoid, /* our AM's oid */
+ namespaceoid, /* namespace to create opfamily in */
+ opfamilyoid; /* oid of opfamily we create */
+ Relation rel;
+ HeapTuple tup;
+ Datum values[Natts_pg_opfamily];
+ char nulls[Natts_pg_opfamily];
+ AclResult aclresult;
+ NameData opfName;
+ ObjectAddress myself,
+ referenced;
- /* Fetch the operator definition */
- optup = SearchSysCache(OPEROID,
- ObjectIdGetDatum(member->object),
- 0, 0, 0);
- if (optup == NULL)
- elog(ERROR, "cache lookup failed for operator %u", member->object);
- opform = (Form_pg_operator) GETSTRUCT(optup);
+ /* Convert list of names to a name and namespace */
+ namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
+ &opfname);
+
+ /* Check we have creation rights in target namespace */
+ aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(namespaceoid));
+
+ /* Get necessary info about access method */
+ tup = SearchSysCache(AMNAME,
+ CStringGetDatum(stmt->amname),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("access method \"%s\" does not exist",
+ stmt->amname)));
+
+ amoid = HeapTupleGetOid(tup);
+
+ /* XXX Should we make any privilege check against the AM? */
+
+ ReleaseSysCache(tup);
/*
- * Opfamily operators must be binary ops returning boolean.
+ * Currently, we require superuser privileges to create an opfamily.
+ * See comments in DefineOpClass.
+ *
+ * XXX re-enable NOT_USED code sections below if you remove this test.
*/
- if (opform->oprkind != 'b')
+ if (!superuser())
ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("index operators must be binary")));
- if (opform->oprresult != BOOLOID)
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to create an operator family")));
+
+ 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_INVALID_OBJECT_DEFINITION),
- errmsg("index operators must return boolean")));
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("operator family \"%s\" for access method \"%s\" already exists",
+ opfname, stmt->amname)));
/*
- * If lefttype/righttype isn't specified, use the operator's input types
+ * Okay, let's create the pg_opfamily entry.
*/
- if (!OidIsValid(member->lefttype))
- member->lefttype = opform->oprleft;
- if (!OidIsValid(member->righttype))
- member->righttype = opform->oprright;
+ memset(values, 0, sizeof(values));
+ memset(nulls, ' ', sizeof(nulls));
- ReleaseSysCache(optup);
+ 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);
}
+
/*
- * Determine the lefttype/righttype to assign to a support procedure,
- * and do any validity checking we can manage.
+ * AlterOpFamily
+ * Add or remove operators/procedures within an existing operator family.
+ *
+ * Note: this implements only ALTER OPERATOR FAMILY ... ADD/DROP. Some
+ * other commands called ALTER OPERATOR FAMILY exist, but go through
+ * different code paths.
*/
-static void
-assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
+void
+AlterOpFamily(AlterOpFamilyStmt *stmt)
{
- HeapTuple proctup;
- Form_pg_proc procform;
+ Oid amoid, /* our AM's oid */
+ opfamilyoid; /* oid of opfamily */
+ int maxOpNumber, /* amstrategies value */
+ maxProcNumber; /* amsupport value */
+ HeapTuple tup;
+ Form_pg_am pg_am;
- /* Fetch the procedure definition */
- proctup = SearchSysCache(PROCOID,
- ObjectIdGetDatum(member->object),
- 0, 0, 0);
- if (proctup == NULL)
- elog(ERROR, "cache lookup failed for function %u", member->object);
- procform = (Form_pg_proc) GETSTRUCT(proctup);
+ /* Get necessary info about access method */
+ tup = SearchSysCache(AMNAME,
+ CStringGetDatum(stmt->amname),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("access method \"%s\" does not exist",
+ stmt->amname)));
+
+ amoid = HeapTupleGetOid(tup);
+ pg_am = (Form_pg_am) GETSTRUCT(tup);
+ maxOpNumber = pg_am->amstrategies;
+ /* if amstrategies is zero, just enforce that op numbers fit in int16 */
+ if (maxOpNumber <= 0)
+ maxOpNumber = SHRT_MAX;
+ maxProcNumber = pg_am->amsupport;
+
+ /* XXX Should we make any privilege check against the AM? */
+
+ ReleaseSysCache(tup);
+
+ /* Look up the opfamily */
+ 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);
+ ReleaseSysCache(tup);
/*
- * 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.
+ * Currently, we require superuser privileges to alter an opfamily.
+ *
+ * XXX re-enable NOT_USED code sections below if you remove this test.
*/
- 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")));
-
- /*
- * 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 (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to alter an operator family")));
- /*
- * 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];
- }
+ /*
+ * ADD and DROP cases need separate code from here on down.
+ */
+ if (stmt->isDrop)
+ AlterOpFamilyDrop(stmt->opfamilyname, amoid, opfamilyoid,
+ maxOpNumber, maxProcNumber,
+ stmt->items);
else
- {
- /*
- * 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);
+ AlterOpFamilyAdd(stmt->opfamilyname, amoid, opfamilyoid,
+ maxOpNumber, maxProcNumber,
+ stmt->items);
}
/*
- * Add a new family member to the appropriate list, after checking for
- * duplicated strategy or proc number.
+ * ADD part of ALTER OP FAMILY
*/
static void
-addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
+AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+ int maxOpNumber, int maxProcNumber,
+ List *items)
{
+ List *operators; /* OpFamilyMember list for operators */
+ List *procedures; /* OpFamilyMember list for support procs */
ListCell *l;
- foreach(l, *list)
+ operators = NIL;
+ procedures = NIL;
+
+ /*
+ * Scan the "items" list to obtain additional info.
+ */
+ foreach(l, items)
{
- OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
+ CreateOpClassItem *item = lfirst(l);
+ Oid operOid;
+ Oid funcOid;
+ OpFamilyMember *member;
- if (old->number == member->number &&
- old->lefttype == member->lefttype &&
- old->righttype == member->righttype)
+ Assert(IsA(item, CreateOpClassItem));
+ switch (item->itemtype)
{
- if (isProc)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("procedure number %d for (%s,%s) appears more than once",
- member->number,
- format_type_be(member->lefttype),
+ case OPCLASS_ITEM_OPERATOR:
+ if (item->number <= 0 || item->number > maxOpNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("invalid operator number %d,"
+ " must be between 1 and %d",
+ item->number, maxOpNumber)));
+ if (item->args != NIL)
+ {
+ TypeName *typeName1 = (TypeName *) linitial(item->args);
+ TypeName *typeName2 = (TypeName *) lsecond(item->args);
+
+ operOid = LookupOperNameTypeNames(NULL, item->name,
+ typeName1, typeName2,
+ false, -1);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("operator argument types must be specified in ALTER OPERATOR FAMILY")));
+ operOid = InvalidOid; /* keep compiler quiet */
+ }
+
+#ifdef NOT_USED
+ /* XXX this is unnecessary given the superuser check above */
+ /* Caller must own operator and its underlying function */
+ if (!pg_oper_ownercheck(operOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
+ get_opname(operOid));
+ funcOid = get_opcode(operOid);
+ if (!pg_proc_ownercheck(funcOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+ get_func_name(funcOid));
+#endif
+
+ /* Save the info */
+ member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
+ member->object = operOid;
+ member->number = item->number;
+ member->recheck = item->recheck;
+ assignOperTypes(member, amoid, InvalidOid);
+ addFamilyMember(&operators, member, false);
+ break;
+ case OPCLASS_ITEM_FUNCTION:
+ if (item->number <= 0 || item->number > maxProcNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("invalid procedure number %d,"
+ " must be between 1 and %d",
+ item->number, maxProcNumber)));
+ funcOid = LookupFuncNameTypeNames(item->name, item->args,
+ false);
+#ifdef NOT_USED
+ /* XXX this is unnecessary given the superuser check above */
+ /* Caller must own function */
+ if (!pg_proc_ownercheck(funcOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+ get_func_name(funcOid));
+#endif
+
+ /* Save the info */
+ member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
+ member->object = funcOid;
+ member->number = item->number;
+
+ /* allow overriding of the function's actual arg types */
+ if (item->class_args)
+ processTypesSpec(item->class_args,
+ &member->lefttype, &member->righttype);
+
+ assignProcTypes(member, amoid, InvalidOid);
+ addFamilyMember(&procedures, member, true);
+ break;
+ case OPCLASS_ITEM_STORAGETYPE:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("STORAGE may not be specified in ALTER OPERATOR FAMILY")));
+ break;
+ default:
+ elog(ERROR, "unrecognized item type: %d", item->itemtype);
+ break;
+ }
+ }
+
+ /*
+ * Add tuples to pg_amop and pg_amproc tying in the operators and
+ * functions. Dependencies on them are inserted, too.
+ */
+ storeOperators(opfamilyname, amoid, opfamilyoid,
+ InvalidOid, operators, true);
+ storeProcedures(opfamilyname, amoid, opfamilyoid,
+ InvalidOid, procedures, true);
+}
+
+/*
+ * DROP part of ALTER OP FAMILY
+ */
+static void
+AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+ int maxOpNumber, int maxProcNumber,
+ List *items)
+{
+ List *operators; /* OpFamilyMember list for operators */
+ List *procedures; /* OpFamilyMember list for support procs */
+ ListCell *l;
+
+ operators = NIL;
+ procedures = NIL;
+
+ /*
+ * Scan the "items" list to obtain additional info.
+ */
+ foreach(l, items)
+ {
+ CreateOpClassItem *item = lfirst(l);
+ Oid lefttype,
+ righttype;
+ OpFamilyMember *member;
+
+ Assert(IsA(item, CreateOpClassItem));
+ switch (item->itemtype)
+ {
+ case OPCLASS_ITEM_OPERATOR:
+ if (item->number <= 0 || item->number > maxOpNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("invalid operator number %d,"
+ " must be between 1 and %d",
+ item->number, maxOpNumber)));
+ processTypesSpec(item->args, &lefttype, &righttype);
+ /* Save the info */
+ member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
+ member->number = item->number;
+ member->lefttype = lefttype;
+ member->righttype = righttype;
+ addFamilyMember(&operators, member, false);
+ break;
+ case OPCLASS_ITEM_FUNCTION:
+ if (item->number <= 0 || item->number > maxProcNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("invalid procedure number %d,"
+ " must be between 1 and %d",
+ item->number, maxProcNumber)));
+ processTypesSpec(item->args, &lefttype, &righttype);
+ /* Save the info */
+ member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
+ member->number = item->number;
+ member->lefttype = lefttype;
+ member->righttype = righttype;
+ addFamilyMember(&procedures, member, true);
+ break;
+ case OPCLASS_ITEM_STORAGETYPE:
+ /* grammar prevents this from appearing */
+ default:
+ elog(ERROR, "unrecognized item type: %d", item->itemtype);
+ break;
+ }
+ }
+
+ /*
+ * Remove tuples from pg_amop and pg_amproc.
+ */
+ dropOperators(opfamilyname, amoid, opfamilyoid, operators);
+ dropProcedures(opfamilyname, amoid, opfamilyoid, procedures);
+}
+
+
+/*
+ * Deal with explicit arg types used in ALTER ADD/DROP
+ */
+static void
+processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
+{
+ TypeName *typeName;
+
+ Assert(args != NIL);
+
+ typeName = (TypeName *) linitial(args);
+ *lefttype = typenameTypeId(NULL, typeName);
+
+ if (list_length(args) > 1)
+ {
+ typeName = (TypeName *) lsecond(args);
+ *righttype = typenameTypeId(NULL, typeName);
+ }
+ else
+ *righttype = *lefttype;
+
+ if (list_length(args) > 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("one or two argument types must be specified")));
+}
+
+
+/*
+ * Determine the lefttype/righttype to assign to an operator,
+ * and do any validity checking we can manage.
+ */
+static void
+assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
+{
+ Operator optup;
+ Form_pg_operator opform;
+
+ /* Fetch the operator definition */
+ optup = SearchSysCache(OPEROID,
+ ObjectIdGetDatum(member->object),
+ 0, 0, 0);
+ if (optup == NULL)
+ elog(ERROR, "cache lookup failed for operator %u", member->object);
+ opform = (Form_pg_operator) GETSTRUCT(optup);
+
+ /*
+ * Opfamily operators must be binary ops returning boolean.
+ */
+ if (opform->oprkind != 'b')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("index operators must be binary")));
+ if (opform->oprresult != BOOLOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("index operators must return boolean")));
+
+ /*
+ * If lefttype/righttype isn't specified, use the operator's input types
+ */
+ if (!OidIsValid(member->lefttype))
+ member->lefttype = opform->oprleft;
+ if (!OidIsValid(member->righttype))
+ member->righttype = opform->oprright;
+
+ ReleaseSysCache(optup);
+}
+
+/*
+ * Determine the lefttype/righttype to assign to a support procedure,
+ * and do any validity checking we can manage.
+ */
+static void
+assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
+{
+ HeapTuple proctup;
+ Form_pg_proc procform;
+
+ /* Fetch the procedure definition */
+ proctup = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(member->object),
+ 0, 0, 0);
+ if (proctup == NULL)
+ 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; hash support
+ * procs must be 1-arg procs returning int4; otherwise we don't know.
+ */
+ 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")));
+
+ /*
+ * 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
+ {
+ /*
+ * 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);
+}
+
+/*
+ * Add a new family member to the appropriate list, after checking for
+ * duplicated strategy or proc number.
+ */
+static void
+addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
+{
+ ListCell *l;
+
+ foreach(l, *list)
+ {
+ OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
+
+ if (old->number == member->number &&
+ old->lefttype == member->lefttype &&
+ old->righttype == member->righttype)
+ {
+ if (isProc)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 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,
* else make an AUTO dependency on the opfamily.
*/
static void
-storeOperators(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *operators)
+storeOperators(List *opfamilyname, Oid amoid,
+ Oid opfamilyoid, Oid opclassoid,
+ List *operators, bool isAdd)
{
Relation rel;
Datum values[Natts_pg_amop];
{
OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
+ /*
+ * If adding to an existing family, check for conflict with an
+ * existing pg_amop entry (just to give a nicer error message)
+ */
+ if (isAdd &&
+ SearchSysCacheExists(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamilyoid),
+ ObjectIdGetDatum(op->lefttype),
+ ObjectIdGetDatum(op->righttype),
+ Int16GetDatum(op->number)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("operator %d(%s,%s) already exists in operator family \"%s\"",
+ op->number,
+ format_type_be(op->lefttype),
+ format_type_be(op->righttype),
+ NameListToString(opfamilyname))));
+
/* Create the pg_amop entry */
memset(values, 0, sizeof(values));
memset(nulls, ' ', sizeof(nulls));
* else make an AUTO dependency on the opfamily.
*/
static void
-storeProcedures(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *procedures)
+storeProcedures(List *opfamilyname, Oid amoid,
+ Oid opfamilyoid, Oid opclassoid,
+ List *procedures, bool isAdd)
{
Relation rel;
Datum values[Natts_pg_amproc];
{
OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
+ /*
+ * If adding to an existing family, check for conflict with an
+ * existing pg_amproc entry (just to give a nicer error message)
+ */
+ if (isAdd &&
+ SearchSysCacheExists(AMPROCNUM,
+ ObjectIdGetDatum(opfamilyoid),
+ ObjectIdGetDatum(proc->lefttype),
+ ObjectIdGetDatum(proc->righttype),
+ Int16GetDatum(proc->number)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("function %d(%s,%s) already exists in operator family \"%s\"",
+ proc->number,
+ format_type_be(proc->lefttype),
+ format_type_be(proc->righttype),
+ NameListToString(opfamilyname))));
+
/* Create the pg_amproc entry */
memset(values, 0, sizeof(values));
memset(nulls, ' ', sizeof(nulls));
}
+/*
+ * Remove operator entries from an opfamily.
+ *
+ * Note: this is only allowed for "loose" members of an opfamily, hence
+ * behavior is always RESTRICT.
+ */
+static void
+dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+ List *operators)
+{
+ ListCell *l;
+
+ foreach(l, operators)
+ {
+ OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
+ Oid amopid;
+ ObjectAddress object;
+
+ amopid = GetSysCacheOid(AMOPSTRATEGY,
+ ObjectIdGetDatum(opfamilyoid),
+ ObjectIdGetDatum(op->lefttype),
+ ObjectIdGetDatum(op->righttype),
+ Int16GetDatum(op->number));
+ if (!OidIsValid(amopid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"",
+ op->number,
+ format_type_be(op->lefttype),
+ format_type_be(op->righttype),
+ NameListToString(opfamilyname))));
+
+ object.classId = AccessMethodOperatorRelationId;
+ object.objectId = amopid;
+ object.objectSubId = 0;
+
+ performDeletion(&object, DROP_RESTRICT);
+ }
+}
+
+/*
+ * Remove procedure entries from an opfamily.
+ *
+ * Note: this is only allowed for "loose" members of an opfamily, hence
+ * behavior is always RESTRICT.
+ */
+static void
+dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+ List *procedures)
+{
+ ListCell *l;
+
+ foreach(l, procedures)
+ {
+ OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
+ Oid amprocid;
+ ObjectAddress object;
+
+ amprocid = GetSysCacheOid(AMPROCNUM,
+ ObjectIdGetDatum(opfamilyoid),
+ ObjectIdGetDatum(op->lefttype),
+ ObjectIdGetDatum(op->righttype),
+ Int16GetDatum(op->number));
+ if (!OidIsValid(amprocid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("function %d(%s,%s) does not exist in operator family \"%s\"",
+ op->number,
+ format_type_be(op->lefttype),
+ format_type_be(op->righttype),
+ NameListToString(opfamilyname))));
+
+ object.classId = AccessMethodProcedureRelationId;
+ object.objectId = amprocid;
+ object.objectSubId = 0;
+
+ performDeletion(&object, DROP_RESTRICT);
+ }
+}
+
+
/*
* RemoveOpClass
* Deletes an opclass.
performDeletion(&object, stmt->behavior);
}
+/*
+ * RemoveOpFamily
+ * Deletes an opfamily.
+ */
+void
+RemoveOpFamily(RemoveOpFamilyStmt *stmt)
+{
+ Oid amID,
+ opfID;
+ HeapTuple tuple;
+ ObjectAddress object;
+
+ /*
+ * Get the access method's OID.
+ */
+ amID = GetSysCacheOid(AMNAME,
+ CStringGetDatum(stmt->amname),
+ 0, 0, 0);
+ if (!OidIsValid(amID))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("access method \"%s\" does not exist",
+ stmt->amname)));
+
+ /*
+ * Look up the opfamily.
+ */
+ tuple = OpFamilyCacheLookup(amID, stmt->opfamilyname);
+ if (!HeapTupleIsValid(tuple))
+ {
+ if (!stmt->missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+ NameListToString(stmt->opfamilyname), stmt->amname)));
+ else
+ ereport(NOTICE,
+ (errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+ NameListToString(stmt->opfamilyname), stmt->amname)));
+ return;
+ }
+
+ opfID = HeapTupleGetOid(tuple);
+
+ /* Permission check: must own opfamily or its namespace */
+ if (!pg_opfamily_ownercheck(opfID, GetUserId()) &&
+ !pg_namespace_ownercheck(((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace,
+ GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
+ NameListToString(stmt->opfamilyname));
+
+ ReleaseSysCache(tuple);
+
+ /*
+ * Do the deletion
+ */
+ object.classId = OperatorFamilyRelationId;
+ object.objectId = opfID;
+ object.objectSubId = 0;
+
+ performDeletion(&object, stmt->behavior);
+}
+
+
/*
* Deletion subroutines for use by dependency.c.
*/
}
/*
- * Change opclass owner by oid
+ * Rename opfamily
*/
-#ifdef NOT_USED
void
-AlterOpClassOwner_oid(Oid opcOid, Oid newOwnerId)
+RenameOpFamily(List *name, const char *access_method, const char *newname)
{
- Relation rel;
+ Oid opfOid;
+ Oid amOid;
+ Oid namespaceOid;
+ char *schemaname;
+ char *opfname;
HeapTuple tup;
+ Relation rel;
+ AclResult aclresult;
- rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
+ amOid = GetSysCacheOid(AMNAME,
+ CStringGetDatum(access_method),
+ 0, 0, 0);
+ if (!OidIsValid(amOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("access method \"%s\" does not exist",
+ access_method)));
- tup = SearchSysCacheCopy(CLAOID,
- ObjectIdGetDatum(opcOid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tup)) /* shouldn't happen */
- elog(ERROR, "cache lookup failed for opclass %u", opcOid);
+ rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
- AlterOpClassOwner_internal(rel, tup, newOwnerId);
+ /*
+ * Look up the opfamily
+ */
+ DeconstructQualifiedName(name, &schemaname, &opfname);
+
+ if (schemaname)
+ {
+ namespaceOid = LookupExplicitNamespace(schemaname);
+
+ tup = SearchSysCacheCopy(OPFAMILYAMNAMENSP,
+ ObjectIdGetDatum(amOid),
+ PointerGetDatum(opfname),
+ ObjectIdGetDatum(namespaceOid),
+ 0);
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+ opfname, access_method)));
+
+ opfOid = HeapTupleGetOid(tup);
+ }
+ else
+ {
+ opfOid = OpfamilynameGetOpfid(amOid, opfname);
+ if (!OidIsValid(opfOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+ opfname, access_method)));
+
+ tup = SearchSysCacheCopy(OPFAMILYOID,
+ ObjectIdGetDatum(opfOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "cache lookup failed for opfamily %u", opfOid);
+
+ namespaceOid = ((Form_pg_opfamily) GETSTRUCT(tup))->opfnamespace;
+ }
+
+ /* make sure the new name doesn't exist */
+ if (SearchSysCacheExists(OPFAMILYAMNAMENSP,
+ ObjectIdGetDatum(amOid),
+ CStringGetDatum(newname),
+ ObjectIdGetDatum(namespaceOid),
+ 0))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
+ newname, access_method,
+ get_namespace_name(namespaceOid))));
+ }
+
+ /* must be owner */
+ if (!pg_opfamily_ownercheck(opfOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
+ NameListToString(name));
+
+ /* must have CREATE privilege on namespace */
+ aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(namespaceOid));
+
+ /* rename */
+ namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname);
+ simple_heap_update(rel, &tup->t_self, tup);
+ CatalogUpdateIndexes(rel, tup);
- heap_freetuple(tup);
heap_close(rel, NoLock);
+ heap_freetuple(tup);
}
-#endif
/*
* Change opclass owner by name
newOwnerId);
}
}
+
+/*
+ * Change opfamily owner by name
+ */
+void
+AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId)
+{
+ Oid amOid;
+ Relation rel;
+ HeapTuple tup;
+ char *opfname;
+ char *schemaname;
+
+ amOid = GetSysCacheOid(AMNAME,
+ CStringGetDatum(access_method),
+ 0, 0, 0);
+ if (!OidIsValid(amOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("access method \"%s\" does not exist",
+ access_method)));
+
+ rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
+
+ /*
+ * Look up the opfamily
+ */
+ DeconstructQualifiedName(name, &schemaname, &opfname);
+
+ if (schemaname)
+ {
+ Oid namespaceOid;
+
+ namespaceOid = LookupExplicitNamespace(schemaname);
+
+ tup = SearchSysCacheCopy(OPFAMILYAMNAMENSP,
+ ObjectIdGetDatum(amOid),
+ PointerGetDatum(opfname),
+ ObjectIdGetDatum(namespaceOid),
+ 0);
+ if (!HeapTupleIsValid(tup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+ opfname, access_method)));
+ }
+ else
+ {
+ Oid opfOid;
+
+ opfOid = OpfamilynameGetOpfid(amOid, opfname);
+ if (!OidIsValid(opfOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+ opfname, access_method)));
+
+ tup = SearchSysCacheCopy(OPFAMILYOID,
+ ObjectIdGetDatum(opfOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "cache lookup failed for opfamily %u", opfOid);
+ }
+
+ AlterOpFamilyOwner_internal(rel, tup, newOwnerId);
+
+ heap_freetuple(tup);
+ heap_close(rel, NoLock);
+}
+
+/*
+ * The first parameter is pg_opfamily, opened and suitably locked. The second
+ * parameter is a copy of the tuple from pg_opfamily we want to modify.
+ */
+static void
+AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
+{
+ Oid namespaceOid;
+ AclResult aclresult;
+ Form_pg_opfamily opfForm;
+
+ Assert(tup->t_tableOid == OperatorFamilyRelationId);
+ Assert(RelationGetRelid(rel) == OperatorFamilyRelationId);
+
+ opfForm = (Form_pg_opfamily) GETSTRUCT(tup);
+
+ namespaceOid = opfForm->opfnamespace;
+
+ /*
+ * If the new owner is the same as the existing owner, consider the
+ * command to have succeeded. This is for dump restoration purposes.
+ */
+ if (opfForm->opfowner != newOwnerId)
+ {
+ /* Superusers can always do it */
+ if (!superuser())
+ {
+ /* Otherwise, must be owner of the existing object */
+ if (!pg_opfamily_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
+ NameStr(opfForm->opfname));
+
+ /* Must be able to become new owner */
+ check_is_member_of_role(GetUserId(), newOwnerId);
+
+ /* New owner must have CREATE privilege on namespace */
+ aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(namespaceOid));
+ }
+
+ /*
+ * Modify the owner --- okay to scribble on tup because it's a copy
+ */
+ opfForm->opfowner = newOwnerId;
+
+ simple_heap_update(rel, &tup->t_self, tup);
+
+ CatalogUpdateIndexes(rel, tup);
+
+ /* Update owner dependency reference */
+ changeDependencyOnOwner(OperatorFamilyRelationId, HeapTupleGetOid(tup),
+ newOwnerId);
+ }
+}