aboutsummaryrefslogtreecommitdiff
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.c289
1 files changed, 217 insertions, 72 deletions
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 599d2eb8259..e251f8577da 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/opclasscmds.c,v 1.22 2003/11/09 21:30:36 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/opclasscmds.c,v 1.23 2003/11/12 21:15:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,6 +25,8 @@
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "miscadmin.h"
@@ -38,9 +40,24 @@
#include "utils/syscache.h"
-static void storeOperators(Oid opclassoid, int numOperators,
- Oid *operators, bool *recheck);
-static void storeProcedures(Oid opclassoid, int numProcs, Oid *procedures);
+/*
+ * We use lists of this struct type to keep track of both operators and
+ * procedures during DefineOpClass.
+ */
+typedef struct
+{
+ Oid object; /* operator or support proc's OID */
+ int number; /* strategy or support proc number */
+ Oid subtype; /* subtype */
+ bool recheck; /* oper recheck flag (unused for proc) */
+} OpClassMember;
+
+
+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);
/*
@@ -58,10 +75,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
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;
+ List *operators; /* OpClassMember list for operators */
+ List *procedures; /* OpClassMember list for support procs */
+ List *l;
Relation rel;
HeapTuple tup;
Datum values[Natts_pg_opclass];
@@ -123,26 +139,21 @@ DefineOpClass(CreateOpClassStmt *stmt)
format_type_be(typeoid));
#endif
+ operators = NIL;
+ procedures = NIL;
+
/* 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 *) palloc0(sizeof(Oid) * numOperators);
- procedures = (Oid *) palloc0(sizeof(Oid) * numProcs);
- recheck = (bool *) palloc0(sizeof(bool) * numOperators);
-
- /*
* Scan the "items" list to obtain additional info.
*/
- foreach(iteml, stmt->items)
+ foreach(l, stmt->items)
{
- CreateOpClassItem *item = lfirst(iteml);
+ CreateOpClassItem *item = lfirst(l);
Oid operOid;
Oid funcOid;
+ OpClassMember *member;
AclResult aclresult;
Assert(IsA(item, CreateOpClassItem));
@@ -155,11 +166,6 @@ DefineOpClass(CreateOpClassStmt *stmt)
errmsg("invalid operator number %d,"
" must be between 1 and %d",
item->number, numOperators)));
- if (operators[item->number - 1] != InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("operator number %d appears more than once",
- item->number)));
if (item->args != NIL)
{
TypeName *typeName1 = (TypeName *) lfirst(item->args);
@@ -183,8 +189,13 @@ DefineOpClass(CreateOpClassStmt *stmt)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(funcOid));
- operators[item->number - 1] = operOid;
- recheck[item->number - 1] = item->recheck;
+ /* Save the info */
+ member = (OpClassMember *) palloc0(sizeof(OpClassMember));
+ member->object = operOid;
+ member->number = item->number;
+ member->subtype = assignOperSubtype(amoid, typeoid, operOid);
+ member->recheck = item->recheck;
+ addClassMember(&operators, member, false);
break;
case OPCLASS_ITEM_FUNCTION:
if (item->number <= 0 || item->number > numProcs)
@@ -193,11 +204,6 @@ DefineOpClass(CreateOpClassStmt *stmt)
errmsg("invalid procedure number %d,"
" must be between 1 and %d",
item->number, numProcs)));
- if (procedures[item->number - 1] != InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("procedure number %d appears more than once",
- item->number)));
funcOid = LookupFuncNameTypeNames(item->name, item->args,
false);
/* Caller must have execute permission on functions */
@@ -206,7 +212,12 @@ DefineOpClass(CreateOpClassStmt *stmt)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(funcOid));
- procedures[item->number - 1] = funcOid;
+ /* Save the info */
+ member = (OpClassMember *) palloc0(sizeof(OpClassMember));
+ member->object = funcOid;
+ member->number = item->number;
+ member->subtype = assignProcSubtype(amoid, typeoid, funcOid);
+ addClassMember(&procedures, member, true);
break;
case OPCLASS_ITEM_STORAGETYPE:
if (OidIsValid(storageoid))
@@ -271,10 +282,10 @@ DefineOpClass(CreateOpClassStmt *stmt)
ScanKeyData skey[1];
SysScanDesc scan;
- ScanKeyEntryInitialize(&skey[0], 0,
- Anum_pg_opclass_opcamid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(amoid), OIDOID);
+ ScanKeyInit(&skey[0],
+ Anum_pg_opclass_opcamid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(amoid));
scan = systable_beginscan(rel, OpclassAmNameNspIndex, true,
SnapshotNow, 1, skey);
@@ -327,8 +338,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
* Now add tuples to pg_amop and pg_amproc tying in the operators and
* functions.
*/
- storeOperators(opclassoid, numOperators, operators, recheck);
- storeProcedures(opclassoid, numProcs, procedures);
+ storeOperators(opclassoid, operators);
+ storeProcedures(opclassoid, procedures);
/*
* Create dependencies. Note: we do not create a dependency link to
@@ -361,22 +372,22 @@ DefineOpClass(CreateOpClassStmt *stmt)
/* dependencies on operators */
referenced.classId = get_system_catalog_relid(OperatorRelationName);
- for (i = 0; i < numOperators; i++)
+ foreach(l, operators)
{
- if (operators[i] == InvalidOid)
- continue;
- referenced.objectId = operators[i];
+ OpClassMember *op = (OpClassMember *) lfirst(l);
+
+ referenced.objectId = op->object;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/* dependencies on procedures */
- for (i = 0; i < numProcs; i++)
+ foreach(l, procedures)
{
- if (procedures[i] == InvalidOid)
- continue;
+ OpClassMember *proc = (OpClassMember *) lfirst(l);
+
referenced.classId = RelOid_pg_proc;
- referenced.objectId = procedures[i];
+ referenced.objectId = proc->object;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
@@ -385,25 +396,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.
+ */
+static Oid
+assignOperSubtype(Oid amoid, Oid typeoid, Oid operOid)
+{
+ 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;
+
+ optup = SearchSysCache(OPEROID,
+ ObjectIdGetDatum(operOid),
+ 0, 0, 0);
+ if (optup == NULL)
+ elog(ERROR, "cache lookup failed for operator %u", operOid);
+ 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.
+ */
+ if (opform->oprkind != 'b')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree 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")));
+ /*
+ * The subtype is "default" (0) if oprright matches the operator class,
+ * otherwise it is oprright.
+ */
+ if (opform->oprright == typeoid)
+ subtype = InvalidOid;
+ else
+ subtype = 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.
+ */
+static Oid
+assignProcSubtype(Oid amoid, Oid typeoid, Oid procOid)
+{
+ 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;
+
+ proctup = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(procOid),
+ 0, 0, 0);
+ if (proctup == NULL)
+ elog(ERROR, "cache lookup failed for function %u", procOid);
+ 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.
+ */
+ 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[0] != typeoid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree procedures must have index type as first input")));
+ /*
+ * The subtype is "default" (0) if second input type matches the operator
+ * class, otherwise it is the second input type.
+ */
+ if (procform->proargtypes[1] == typeoid)
+ subtype = InvalidOid;
+ else
+ subtype = procform->proargtypes[1];
+ ReleaseSysCache(proctup);
+ return subtype;
+}
+
+/*
+ * Add a new class member to the appropriate list, after checking for
+ * duplicated strategy or proc number.
+ */
+static void
+addClassMember(List **list, OpClassMember *member, bool isProc)
+{
+ List *l;
+
+ foreach(l, *list)
+ {
+ OpClassMember *old = (OpClassMember *) lfirst(l);
+
+ if (old->number == member->number &&
+ old->subtype == member->subtype)
+ {
+ if (isProc)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("procedure number %d appears more than once",
+ member->number)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("operator number %d appears more than once",
+ member->number)));
+ }
+ }
+ *list = lappend(*list, member);
+}
+
+/*
* Dump the operators to pg_amop
*/
static void
-storeOperators(Oid opclassoid, int numOperators,
- Oid *operators, bool *recheck)
+storeOperators(Oid opclassoid, List *operators)
{
Relation rel;
Datum values[Natts_pg_amop];
char nulls[Natts_pg_amop];
HeapTuple tup;
- int i,
- j;
+ List *l;
+ int i;
rel = heap_openr(AccessMethodOperatorRelationName, RowExclusiveLock);
- for (j = 0; j < numOperators; j++)
+ foreach(l, operators)
{
- if (operators[j] == InvalidOid)
- continue;
+ OpClassMember *op = (OpClassMember *) lfirst(l);
for (i = 0; i < Natts_pg_amop; ++i)
{
@@ -413,9 +557,10 @@ storeOperators(Oid opclassoid, int numOperators,
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 */
+ values[i++] = ObjectIdGetDatum(op->subtype); /* amopsubtype */
+ values[i++] = Int16GetDatum(op->number); /* amopstrategy */
+ values[i++] = BoolGetDatum(op->recheck); /* amopreqcheck */
+ values[i++] = ObjectIdGetDatum(op->object); /* amopopr */
tup = heap_formtuple(rel->rd_att, values, nulls);
@@ -433,21 +578,20 @@ storeOperators(Oid opclassoid, int numOperators,
* Dump the procedures (support routines) to pg_amproc
*/
static void
-storeProcedures(Oid opclassoid, int numProcs, Oid *procedures)
+storeProcedures(Oid opclassoid, List *procedures)
{
Relation rel;
Datum values[Natts_pg_amproc];
char nulls[Natts_pg_amproc];
HeapTuple tup;
- int i,
- j;
+ List *l;
+ int i;
rel = heap_openr(AccessMethodProcedureRelationName, RowExclusiveLock);
- for (j = 0; j < numProcs; j++)
+ foreach(l, procedures)
{
- if (procedures[j] == InvalidOid)
- continue;
+ OpClassMember *proc = (OpClassMember *) lfirst(l);
for (i = 0; i < Natts_pg_amproc; ++i)
{
@@ -457,8 +601,9 @@ storeProcedures(Oid opclassoid, int numProcs, Oid *procedures)
i = 0;
values[i++] = ObjectIdGetDatum(opclassoid); /* amopclaid */
- values[i++] = Int16GetDatum(j + 1); /* amprocnum */
- values[i++] = ObjectIdGetDatum(procedures[j]); /* amproc */
+ values[i++] = ObjectIdGetDatum(proc->subtype); /* amprocsubtype */
+ values[i++] = Int16GetDatum(proc->number); /* amprocnum */
+ values[i++] = ObjectIdGetDatum(proc->object); /* amproc */
tup = heap_formtuple(rel->rd_att, values, nulls);
@@ -590,10 +735,10 @@ RemoveOpClassById(Oid opclassOid)
/*
* Remove associated entries in pg_amop.
*/
- ScanKeyEntryInitialize(&skey[0], 0,
- Anum_pg_amop_amopclaid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(opclassOid), OIDOID);
+ ScanKeyInit(&skey[0],
+ Anum_pg_amop_amopclaid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(opclassOid));
rel = heap_openr(AccessMethodOperatorRelationName, RowExclusiveLock);
@@ -609,10 +754,10 @@ RemoveOpClassById(Oid opclassOid)
/*
* Remove associated entries in pg_amproc.
*/
- ScanKeyEntryInitialize(&skey[0], 0,
- Anum_pg_amproc_amopclaid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(opclassOid), OIDOID);
+ ScanKeyInit(&skey[0],
+ Anum_pg_amproc_amopclaid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(opclassOid));
rel = heap_openr(AccessMethodProcedureRelationName, RowExclusiveLock);