diff options
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/Makefile | 5 | ||||
-rw-r--r-- | src/backend/commands/opclasscmds.c | 639 |
2 files changed, 642 insertions, 2 deletions
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile index 58e8fe20e2d..92961049d77 100644 --- a/src/backend/commands/Makefile +++ b/src/backend/commands/Makefile @@ -4,7 +4,7 @@ # Makefile for backend/commands # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.29 2002/07/11 07:39:27 ishii Exp $ +# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.30 2002/07/29 22:14:10 tgl Exp $ # #------------------------------------------------------------------------- @@ -15,7 +15,8 @@ include $(top_builddir)/src/Makefile.global OBJS = aggregatecmds.o analyze.o async.o cluster.o comment.o \ conversioncmds.o copy.o \ dbcommands.o define.o explain.o functioncmds.o \ - indexcmds.o lockcmds.o operatorcmds.o portalcmds.o proclang.o \ + indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \ + portalcmds.o proclang.o \ schemacmds.o sequence.o tablecmds.o trigger.o typecmds.o user.o \ vacuum.o vacuumlazy.o variable.o view.o diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c new file mode 100644 index 00000000000..fb86c980b98 --- /dev/null +++ b/src/backend/commands/opclasscmds.c @@ -0,0 +1,639 @@ +/*------------------------------------------------------------------------- + * + * opclasscmds.c + * + * Routines for opclass manipulation commands + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/opclasscmds.c,v 1.1 2002/07/29 22:14:10 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/heapam.h" +#include "catalog/catname.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/namespace.h" +#include "catalog/pg_am.h" +#include "catalog/pg_amop.h" +#include "catalog/pg_amproc.h" +#include "catalog/pg_opclass.h" +#include "commands/defrem.h" +#include "miscadmin.h" +#include "parser/parse_func.h" +#include "parser/parse_oper.h" +#include "parser/parse_type.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + + +static void storeOperators(Oid opclassoid, int numOperators, + Oid *operators, bool *recheck); +static void storeProcedures(Oid opclassoid, int numProcs, Oid *procedures); + + +/* + * DefineOpClass + * Define a new index operator class. + */ +void +DefineOpClass(CreateOpClassStmt *stmt) +{ + char *opcname; /* name of opclass we're creating */ + Oid amoid, /* our AM's oid */ + typeoid, /* indexable datatype oid */ + storageoid, /* storage datatype oid, if any */ + namespaceoid, /* namespace to create opclass in */ + opclassoid; /* oid of opclass we create */ + int numOperators, /* amstrategies value */ + numProcs; /* amsupport value */ + Oid *operators, /* oids of operators, by strategy num */ + *procedures; /* oids of support procs */ + bool *recheck; /* do operators need recheck */ + List *iteml; + Relation rel; + HeapTuple tup; + Datum values[Natts_pg_opclass]; + char nulls[Natts_pg_opclass]; + AclResult aclresult; + NameData opcName; + int i; + ObjectAddress myself, + referenced; + + /* Convert list of names to a name and namespace */ + namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname, + &opcname); + + /* Check we have creation rights in target namespace */ + aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_namespace_name(namespaceoid)); + + /* Get necessary info about access method */ + tup = SearchSysCache(AMNAME, + CStringGetDatum(stmt->amname), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "DefineOpClass: access method \"%s\" not found", + stmt->amname); + + amoid = HeapTupleGetOid(tup); + numOperators = ((Form_pg_am) GETSTRUCT(tup))->amstrategies; + numProcs = ((Form_pg_am) GETSTRUCT(tup))->amsupport; + + /* XXX Should we make any privilege check against the AM? */ + + ReleaseSysCache(tup); + + /* Look up the datatype */ + typeoid = typenameTypeId(stmt->datatype); + + /* Check we have ownership of the datatype */ + if (!pg_type_ownercheck(typeoid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, format_type_be(typeoid)); + + /* Storage datatype is optional */ + storageoid = InvalidOid; + + /* + * Create work arrays to hold info about operators and procedures. + * We do this mainly so that we can detect duplicate strategy + * numbers and support-proc numbers. + */ + operators = (Oid *) palloc(sizeof(Oid) * numOperators); + MemSet(operators, 0, sizeof(Oid) * numOperators); + procedures = (Oid *) palloc(sizeof(Oid) * numProcs); + MemSet(procedures, 0, sizeof(Oid) * numProcs); + recheck = (bool *) palloc(sizeof(bool) * numOperators); + MemSet(recheck, 0, sizeof(bool) * numOperators); + + /* + * Scan the "items" list to obtain additional info. + */ + foreach(iteml, stmt->items) + { + CreateOpClassItem *item = lfirst(iteml); + Oid operOid; + Oid funcOid; + AclResult aclresult; + + Assert(IsA(item, CreateOpClassItem)); + switch (item->itemtype) + { + case OPCLASS_ITEM_OPERATOR: + if (item->number <= 0 || item->number > numOperators) + elog(ERROR, "DefineOpClass: invalid operator number %d," + " must be between 1 and %d", + item->number, numOperators); + if (operators[item->number - 1] != InvalidOid) + elog(ERROR, "DefineOpClass: operator number %d appears more than once", + item->number); + if (item->args != NIL) + { + TypeName *typeName1 = (TypeName *) lfirst(item->args); + TypeName *typeName2 = (TypeName *) lsecond(item->args); + + operOid = LookupOperNameTypeNames(item->name, + typeName1, typeName2, + "DefineOpClass"); + /* No need to check for error */ + } + else + { + /* Default to binary op on input datatype */ + operOid = LookupOperName(item->name, typeoid, typeoid); + if (!OidIsValid(operOid)) + elog(ERROR, "DefineOpClass: Operator '%s' for types '%s' and '%s' does not exist", + NameListToString(item->name), + format_type_be(typeoid), + format_type_be(typeoid)); + } + /* Caller must have execute permission on operators */ + funcOid = get_opcode(operOid); + aclresult = pg_proc_aclcheck(funcOid, GetUserId(), + ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_func_name(funcOid)); + operators[item->number - 1] = operOid; + recheck[item->number - 1] = item->recheck; + break; + case OPCLASS_ITEM_FUNCTION: + if (item->number <= 0 || item->number > numProcs) + elog(ERROR, "DefineOpClass: invalid procedure number %d," + " must be between 1 and %d", + item->number, numProcs); + if (procedures[item->number - 1] != InvalidOid) + elog(ERROR, "DefineOpClass: procedure number %d appears more than once", + item->number); + funcOid = LookupFuncNameTypeNames(item->name, item->args, + true, "DefineOpClass"); + /* Caller must have execute permission on functions */ + aclresult = pg_proc_aclcheck(funcOid, GetUserId(), + ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_func_name(funcOid)); + procedures[item->number - 1] = funcOid; + break; + case OPCLASS_ITEM_STORAGETYPE: + if (OidIsValid(storageoid)) + elog(ERROR, "DefineOpClass: storage type specified more than once"); + storageoid = typenameTypeId(item->storedtype); + break; + default: + elog(ERROR, "DefineOpClass: bogus item type %d", + item->itemtype); + break; + } + } + + /* + * If storagetype is specified, make sure it's legal. + */ + if (OidIsValid(storageoid)) + { + /* Just drop the spec if same as column datatype */ + if (storageoid == typeoid) + storageoid = InvalidOid; + else + { + /* + * Currently, only GiST allows storagetype different from + * datatype. This hardcoded test should be eliminated in + * favor of adding another boolean column to pg_am ... + */ + if (amoid != GIST_AM_OID) + elog(ERROR, "Storage type may not be different from datatype for access method %s", + stmt->amname); + } + } + + rel = heap_openr(OperatorClassRelationName, RowExclusiveLock); + + /* + * Make sure there is no existing opclass of this name (this is + * just to give a more friendly error message than "duplicate key"). + */ + if (SearchSysCacheExists(CLAAMNAMENSP, + ObjectIdGetDatum(amoid), + CStringGetDatum(opcname), + ObjectIdGetDatum(namespaceoid), + 0)) + elog(ERROR, "Operator class \"%s\" already exists for access method \"%s\"", + opcname, stmt->amname); + + /* + * If we are creating a default opclass, check there isn't one already. + * (XXX should we restrict this test to visible opclasses?) + */ + if (stmt->isDefault) + { + ScanKeyData skey[1]; + SysScanDesc scan; + + ScanKeyEntryInitialize(&skey[0], 0x0, + Anum_pg_opclass_opcamid, F_OIDEQ, + ObjectIdGetDatum(amoid)); + + scan = systable_beginscan(rel, OpclassAmNameNspIndex, true, + SnapshotNow, 1, skey); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup); + + if (opclass->opcintype == typeoid && opclass->opcdefault) + elog(ERROR, "Can't add class \"%s\" as default for type %s" + "\n\tclass \"%s\" already is the default", + opcname, + TypeNameToString(stmt->datatype), + NameStr(opclass->opcname)); + } + + systable_endscan(scan); + } + + /* + * 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 */ + } + + i = 0; + values[i++] = ObjectIdGetDatum(amoid); /* opcamid */ + namestrcpy(&opcName, opcname); + values[i++] = NameGetDatum(&opcName); /* opcname */ + values[i++] = ObjectIdGetDatum(namespaceoid); /* opcnamespace */ + values[i++] = Int32GetDatum(GetUserId()); /* opcowner */ + values[i++] = ObjectIdGetDatum(typeoid); /* opcintype */ + values[i++] = BoolGetDatum(stmt->isDefault); /* opcdefault */ + values[i++] = ObjectIdGetDatum(storageoid); /* opckeytype */ + + tup = heap_formtuple(rel->rd_att, values, nulls); + + opclassoid = simple_heap_insert(rel, tup); + + if (RelationGetForm(rel)->relhasindex) + { + Relation idescs[Num_pg_opclass_indices]; + + CatalogOpenIndices(Num_pg_opclass_indices, Name_pg_opclass_indices, + idescs); + CatalogIndexInsert(idescs, Num_pg_opclass_indices, rel, tup); + CatalogCloseIndices(Num_pg_opclass_indices, idescs); + } + + heap_freetuple(tup); + + /* + * Now add tuples to pg_amop and pg_amproc tying in the + * operators and functions. + */ + storeOperators(opclassoid, numOperators, operators, recheck); + storeProcedures(opclassoid, numProcs, procedures); + + /* + * Create dependencies. Note: we do not create a dependency link to + * the AM, because we don't currently support DROP ACCESS METHOD. + */ + myself.classId = RelationGetRelid(rel); + myself.objectId = opclassoid; + myself.objectSubId = 0; + + /* dependency on namespace */ + referenced.classId = get_system_catalog_relid(NamespaceRelationName); + referenced.objectId = namespaceoid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* dependency on indexed datatype */ + referenced.classId = RelOid_pg_type; + referenced.objectId = typeoid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* dependency on storage datatype */ + if (OidIsValid(storageoid)) + { + referenced.classId = RelOid_pg_type; + referenced.objectId = storageoid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + /* dependencies on operators */ + referenced.classId = get_system_catalog_relid(OperatorRelationName); + for (i = 0; i < numOperators; i++) + { + if (operators[i] == InvalidOid) + continue; + referenced.objectId = operators[i]; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + /* dependencies on procedures */ + for (i = 0; i < numProcs; i++) + { + if (procedures[i] == InvalidOid) + continue; + referenced.classId = RelOid_pg_proc; + referenced.objectId = procedures[i]; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + heap_close(rel, RowExclusiveLock); +} + +/* + * Dump the operators to pg_amop + */ +static void +storeOperators(Oid opclassoid, int numOperators, + Oid *operators, bool *recheck) +{ + Relation rel; + Datum values[Natts_pg_amop]; + char nulls[Natts_pg_amop]; + HeapTuple tup; + int i, j; + + rel = heap_openr(AccessMethodOperatorRelationName, RowExclusiveLock); + + for (j = 0; j < numOperators; j++) + { + if (operators[j] == InvalidOid) + continue; + + for (i = 0; i < Natts_pg_amop; ++i) + { + nulls[i] = ' '; + values[i] = (Datum) NULL; + } + + i = 0; + values[i++] = ObjectIdGetDatum(opclassoid); /* amopclaid */ + values[i++] = Int16GetDatum(j + 1); /* amopstrategy */ + values[i++] = BoolGetDatum(recheck[j]); /* amopreqcheck */ + values[i++] = ObjectIdGetDatum(operators[j]); /* amopopr */ + + tup = heap_formtuple(rel->rd_att, values, nulls); + + simple_heap_insert(rel, tup); + + if (RelationGetForm(rel)->relhasindex) + { + Relation idescs[Num_pg_amop_indices]; + + CatalogOpenIndices(Num_pg_amop_indices, Name_pg_amop_indices, + idescs); + CatalogIndexInsert(idescs, Num_pg_amop_indices, rel, tup); + CatalogCloseIndices(Num_pg_amop_indices, idescs); + } + heap_freetuple(tup); + } + + heap_close(rel, RowExclusiveLock); +} + +/* + * Dump the procedures (support routines) to pg_amproc + */ +static void +storeProcedures(Oid opclassoid, int numProcs, Oid *procedures) +{ + Relation rel; + Datum values[Natts_pg_amproc]; + char nulls[Natts_pg_amproc]; + HeapTuple tup; + int i, j; + + rel = heap_openr(AccessMethodProcedureRelationName, RowExclusiveLock); + + for (j = 0; j < numProcs; j++) + { + if (procedures[j] == InvalidOid) + continue; + + for (i = 0; i < Natts_pg_amproc; ++i) + { + nulls[i] = ' '; + values[i] = (Datum) NULL; + } + + i = 0; + values[i++] = ObjectIdGetDatum(opclassoid); /* amopclaid */ + values[i++] = Int16GetDatum(j + 1); /* amprocnum */ + values[i++] = ObjectIdGetDatum(procedures[j]); /* amproc */ + + tup = heap_formtuple(rel->rd_att, values, nulls); + + simple_heap_insert(rel, tup); + + if (RelationGetForm(rel)->relhasindex) + { + Relation idescs[Num_pg_amproc_indices]; + + CatalogOpenIndices(Num_pg_amproc_indices, Name_pg_amproc_indices, + idescs); + CatalogIndexInsert(idescs, Num_pg_amproc_indices, rel, tup); + CatalogCloseIndices(Num_pg_amproc_indices, idescs); + } + heap_freetuple(tup); + } + + heap_close(rel, RowExclusiveLock); +} + + +/* + * RemoveOpClass + * Deletes an opclass. + */ +void +RemoveOpClass(RemoveOpClassStmt *stmt) +{ + Oid amID, opcID; + char *catalogname; + char *schemaname = NULL; + char *opcname = NULL; + HeapTuple tuple; + ObjectAddress object; + + /* + * Get the access method's OID. + */ + amID = GetSysCacheOid(AMNAME, + CStringGetDatum(stmt->amname), + 0, 0, 0); + if (!OidIsValid(amID)) + elog(ERROR, "RemoveOpClass: access method \"%s\" not found", + stmt->amname); + + /* + * Look up the opclass. + */ + + /* deconstruct the name list */ + switch (length(stmt->opclassname)) + { + case 1: + opcname = strVal(lfirst(stmt->opclassname)); + break; + case 2: + schemaname = strVal(lfirst(stmt->opclassname)); + opcname = strVal(lsecond(stmt->opclassname)); + break; + case 3: + catalogname = strVal(lfirst(stmt->opclassname)); + schemaname = strVal(lsecond(stmt->opclassname)); + opcname = strVal(lfirst(lnext(lnext(stmt->opclassname)))); + /* + * We check the catalog name and then ignore it. + */ + if (strcmp(catalogname, DatabaseName) != 0) + elog(ERROR, "Cross-database references are not implemented"); + break; + default: + elog(ERROR, "Improper opclass name (too many dotted names): %s", + NameListToString(stmt->opclassname)); + break; + } + + if (schemaname) + { + /* Look in specific schema only */ + Oid namespaceId; + + namespaceId = GetSysCacheOid(NAMESPACENAME, + CStringGetDatum(schemaname), + 0, 0, 0); + if (!OidIsValid(namespaceId)) + elog(ERROR, "Namespace \"%s\" does not exist", + 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)) + elog(ERROR, "RemoveOpClass: operator class \"%s\" not supported by access method \"%s\"", + opcname, stmt->amname); + tuple = SearchSysCache(CLAOID, + ObjectIdGetDatum(opcID), + 0, 0, 0); + } + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "RemoveOpClass: operator class \"%s\" not supported by access method \"%s\"", + NameListToString(stmt->opclassname), stmt->amname); + + opcID = HeapTupleGetOid(tuple); + + /* Permission check: must own opclass or its namespace */ + if (!pg_opclass_ownercheck(opcID, GetUserId()) && + !pg_namespace_ownercheck(((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace, + GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, + NameListToString(stmt->opclassname)); + + ReleaseSysCache(tuple); + + /* + * Do the deletion + */ + object.classId = get_system_catalog_relid(OperatorClassRelationName); + object.objectId = opcID; + object.objectSubId = 0; + + performDeletion(&object, stmt->behavior); +} + +/* + * Guts of opclass deletion. + */ +void +RemoveOpClassById(Oid opclassOid) +{ + Relation rel; + HeapTuple tup; + ScanKeyData skey[1]; + SysScanDesc scan; + + /* + * First remove the pg_opclass entry itself. + */ + rel = heap_openr(OperatorClassRelationName, RowExclusiveLock); + + tup = SearchSysCache(CLAOID, + ObjectIdGetDatum(opclassOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "RemoveOpClassById: couldn't find pg_class entry %u", + opclassOid); + + simple_heap_delete(rel, &tup->t_self); + + ReleaseSysCache(tup); + + heap_close(rel, RowExclusiveLock); + + /* + * Remove associated entries in pg_amop. + */ + ScanKeyEntryInitialize(&skey[0], 0, + Anum_pg_amop_amopclaid, F_OIDEQ, + ObjectIdGetDatum(opclassOid)); + + rel = heap_openr(AccessMethodOperatorRelationName, RowExclusiveLock); + + scan = systable_beginscan(rel, AccessMethodStrategyIndex, true, + SnapshotNow, 1, skey); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + simple_heap_delete(rel, &tup->t_self); + } + + systable_endscan(scan); + heap_close(rel, RowExclusiveLock); + + /* + * Remove associated entries in pg_amproc. + */ + ScanKeyEntryInitialize(&skey[0], 0, + Anum_pg_amproc_amopclaid, F_OIDEQ, + ObjectIdGetDatum(opclassOid)); + + rel = heap_openr(AccessMethodProcedureRelationName, RowExclusiveLock); + + scan = systable_beginscan(rel, AccessMethodProcedureIndex, true, + SnapshotNow, 1, skey); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + simple_heap_delete(rel, &tup->t_self); + } + + systable_endscan(scan); + heap_close(rel, RowExclusiveLock); +} |