2002-07-30 00:14:11 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* opclasscmds.c
|
|
|
|
*
|
2006-12-23 01:43:13 +01:00
|
|
|
* Routines for opclass (and opfamily) manipulation commands
|
2002-07-30 00:14:11 +02:00
|
|
|
*
|
2013-01-01 23:15:01 +01:00
|
|
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
2002-07-30 00:14:11 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/commands/opclasscmds.c
|
2002-07-30 00:14:11 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2006-12-18 19:56:29 +01:00
|
|
|
#include <limits.h>
|
|
|
|
|
2002-07-30 00:14:11 +02:00
|
|
|
#include "access/genam.h"
|
|
|
|
#include "access/heapam.h"
|
2011-12-07 06:18:38 +01:00
|
|
|
#include "access/nbtree.h"
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2008-05-12 02:00:54 +02:00
|
|
|
#include "access/sysattr.h"
|
2002-07-30 00:14:11 +02:00
|
|
|
#include "catalog/dependency.h"
|
|
|
|
#include "catalog/indexing.h"
|
2010-11-25 17:48:49 +01:00
|
|
|
#include "catalog/objectaccess.h"
|
2002-07-30 00:14:11 +02:00
|
|
|
#include "catalog/pg_amop.h"
|
|
|
|
#include "catalog/pg_amproc.h"
|
2005-04-14 22:03:27 +02:00
|
|
|
#include "catalog/pg_namespace.h"
|
2002-07-30 00:14:11 +02:00
|
|
|
#include "catalog/pg_opclass.h"
|
2003-11-12 22:15:59 +01:00
|
|
|
#include "catalog/pg_operator.h"
|
2006-12-23 01:43:13 +01:00
|
|
|
#include "catalog/pg_opfamily.h"
|
2003-11-12 22:15:59 +01:00
|
|
|
#include "catalog/pg_proc.h"
|
2003-11-09 22:30:38 +01:00
|
|
|
#include "catalog/pg_type.h"
|
2010-11-26 23:27:23 +01:00
|
|
|
#include "commands/alter.h"
|
2002-07-30 00:14:11 +02:00
|
|
|
#include "commands/defrem.h"
|
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "parser/parse_func.h"
|
|
|
|
#include "parser/parse_oper.h"
|
|
|
|
#include "parser/parse_type.h"
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/fmgroids.h"
|
|
|
|
#include "utils/lsyscache.h"
|
2008-06-19 02:46:06 +02:00
|
|
|
#include "utils/rel.h"
|
2002-07-30 00:14:11 +02:00
|
|
|
#include "utils/syscache.h"
|
2008-03-26 22:10:39 +01:00
|
|
|
#include "utils/tqual.h"
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
/*
|
|
|
|
* We use lists of this struct type to keep track of both operators and
|
2006-12-23 01:43:13 +01:00
|
|
|
* procedures while building or adding to an opfamily.
|
2003-11-12 22:15:59 +01:00
|
|
|
*/
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
Oid object; /* operator or support proc's OID */
|
|
|
|
int number; /* strategy or support proc number */
|
2006-12-23 01:43:13 +01:00
|
|
|
Oid lefttype; /* lefttype */
|
|
|
|
Oid righttype; /* righttype */
|
2010-11-24 20:20:39 +01:00
|
|
|
Oid sortfamily; /* ordering operator's sort opfamily, or 0 */
|
2007-11-15 23:25:18 +01:00
|
|
|
} OpFamilyMember;
|
2003-11-12 22:15:59 +01:00
|
|
|
|
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
static void AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
|
|
|
|
int maxOpNumber, int maxProcNumber,
|
|
|
|
List *items);
|
|
|
|
static void AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
|
2007-11-15 22:14:46 +01:00
|
|
|
int maxOpNumber, int maxProcNumber,
|
|
|
|
List *items);
|
2007-01-23 06:07:18 +01:00
|
|
|
static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
|
2007-11-15 23:25:18 +01:00
|
|
|
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);
|
2007-01-23 06:07:18 +01:00
|
|
|
static void storeOperators(List *opfamilyname, Oid amoid,
|
2007-11-15 22:14:46 +01:00
|
|
|
Oid opfamilyoid, Oid opclassoid,
|
|
|
|
List *operators, bool isAdd);
|
2007-01-23 06:07:18 +01:00
|
|
|
static void storeProcedures(List *opfamilyname, Oid amoid,
|
2007-11-15 22:14:46 +01:00
|
|
|
Oid opfamilyoid, Oid opclassoid,
|
|
|
|
List *procedures, bool isAdd);
|
2007-01-23 06:07:18 +01:00
|
|
|
static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
|
2007-11-15 22:14:46 +01:00
|
|
|
List *operators);
|
2007-01-23 06:07:18 +01:00
|
|
|
static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
|
2007-11-15 22:14:46 +01:00
|
|
|
List *procedures);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
/*
|
|
|
|
* OpFamilyCacheLookup
|
|
|
|
* Look up an existing opfamily by name.
|
|
|
|
*
|
|
|
|
* Returns a syscache tuple reference, or NULL if not found.
|
|
|
|
*/
|
|
|
|
static HeapTuple
|
2010-08-05 17:25:36 +02:00
|
|
|
OpFamilyCacheLookup(Oid amID, List *opfamilyname, bool missing_ok)
|
2006-12-23 01:43:13 +01:00
|
|
|
{
|
|
|
|
char *schemaname;
|
|
|
|
char *opfname;
|
2010-08-05 17:25:36 +02:00
|
|
|
HeapTuple htup;
|
2006-12-23 01:43:13 +01:00
|
|
|
|
|
|
|
/* deconstruct the name list */
|
|
|
|
DeconstructQualifiedName(opfamilyname, &schemaname, &opfname);
|
|
|
|
|
|
|
|
if (schemaname)
|
|
|
|
{
|
|
|
|
/* Look in specific schema only */
|
|
|
|
Oid namespaceId;
|
|
|
|
|
2013-01-26 19:24:50 +01:00
|
|
|
namespaceId = LookupExplicitNamespace(schemaname, false);
|
2010-08-05 17:25:36 +02:00
|
|
|
htup = SearchSysCache3(OPFAMILYAMNAMENSP,
|
2010-02-14 19:42:19 +01:00
|
|
|
ObjectIdGetDatum(amID),
|
|
|
|
PointerGetDatum(opfname),
|
|
|
|
ObjectIdGetDatum(namespaceId));
|
2006-12-23 01:43:13 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Unqualified opfamily name, so search the search path */
|
2007-11-15 22:14:46 +01:00
|
|
|
Oid opfID = OpfamilynameGetOpfid(amID, opfname);
|
2006-12-23 01:43:13 +01:00
|
|
|
|
|
|
|
if (!OidIsValid(opfID))
|
2010-08-05 17:25:36 +02:00
|
|
|
htup = NULL;
|
|
|
|
else
|
|
|
|
htup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfID));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(htup) && !missing_ok)
|
|
|
|
{
|
2011-04-10 17:42:00 +02:00
|
|
|
HeapTuple amtup;
|
2010-08-05 17:25:36 +02:00
|
|
|
|
|
|
|
amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
|
|
|
|
if (!HeapTupleIsValid(amtup))
|
|
|
|
elog(ERROR, "cache lookup failed for access method %u", amID);
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("operator family \"%s\" does not exist for access method \"%s\"",
|
2011-04-10 17:42:00 +02:00
|
|
|
NameListToString(opfamilyname),
|
|
|
|
NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
|
2006-12-23 01:43:13 +01:00
|
|
|
}
|
2010-08-05 17:25:36 +02:00
|
|
|
|
|
|
|
return htup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get_opfamily_oid
|
2011-04-10 17:42:00 +02:00
|
|
|
* find an opfamily OID by possibly qualified name
|
2010-08-05 17:25:36 +02:00
|
|
|
*
|
|
|
|
* If not found, returns InvalidOid if missing_ok, else throws error.
|
|
|
|
*/
|
|
|
|
Oid
|
|
|
|
get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok)
|
|
|
|
{
|
|
|
|
HeapTuple htup;
|
|
|
|
Oid opfID;
|
|
|
|
|
|
|
|
htup = OpFamilyCacheLookup(amID, opfamilyname, missing_ok);
|
|
|
|
if (!HeapTupleIsValid(htup))
|
|
|
|
return InvalidOid;
|
|
|
|
opfID = HeapTupleGetOid(htup);
|
|
|
|
ReleaseSysCache(htup);
|
|
|
|
|
|
|
|
return opfID;
|
2006-12-23 01:43:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OpClassCacheLookup
|
|
|
|
* Look up an existing opclass by name.
|
|
|
|
*
|
|
|
|
* Returns a syscache tuple reference, or NULL if not found.
|
|
|
|
*/
|
|
|
|
static HeapTuple
|
2010-08-05 17:25:36 +02:00
|
|
|
OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok)
|
2006-12-23 01:43:13 +01:00
|
|
|
{
|
|
|
|
char *schemaname;
|
|
|
|
char *opcname;
|
2010-08-05 17:25:36 +02:00
|
|
|
HeapTuple htup;
|
2006-12-23 01:43:13 +01:00
|
|
|
|
|
|
|
/* deconstruct the name list */
|
|
|
|
DeconstructQualifiedName(opclassname, &schemaname, &opcname);
|
|
|
|
|
|
|
|
if (schemaname)
|
|
|
|
{
|
|
|
|
/* Look in specific schema only */
|
|
|
|
Oid namespaceId;
|
|
|
|
|
2013-01-26 19:24:50 +01:00
|
|
|
namespaceId = LookupExplicitNamespace(schemaname, false);
|
2010-08-05 17:25:36 +02:00
|
|
|
htup = SearchSysCache3(CLAAMNAMENSP,
|
2010-02-14 19:42:19 +01:00
|
|
|
ObjectIdGetDatum(amID),
|
|
|
|
PointerGetDatum(opcname),
|
|
|
|
ObjectIdGetDatum(namespaceId));
|
2006-12-23 01:43:13 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Unqualified opclass name, so search the search path */
|
2007-11-15 22:14:46 +01:00
|
|
|
Oid opcID = OpclassnameGetOpcid(amID, opcname);
|
2006-12-23 01:43:13 +01:00
|
|
|
|
|
|
|
if (!OidIsValid(opcID))
|
2010-08-05 17:25:36 +02:00
|
|
|
htup = NULL;
|
|
|
|
else
|
|
|
|
htup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcID));
|
2006-12-23 01:43:13 +01:00
|
|
|
}
|
2010-08-05 17:25:36 +02:00
|
|
|
|
|
|
|
if (!HeapTupleIsValid(htup) && !missing_ok)
|
|
|
|
{
|
2011-04-10 17:42:00 +02:00
|
|
|
HeapTuple amtup;
|
2010-08-05 17:25:36 +02:00
|
|
|
|
|
|
|
amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
|
|
|
|
if (!HeapTupleIsValid(amtup))
|
|
|
|
elog(ERROR, "cache lookup failed for access method %u", amID);
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("operator class \"%s\" does not exist for access method \"%s\"",
|
|
|
|
NameListToString(opclassname),
|
|
|
|
NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
|
|
|
|
}
|
|
|
|
|
|
|
|
return htup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get_opclass_oid
|
2011-04-10 17:42:00 +02:00
|
|
|
* find an opclass OID by possibly qualified name
|
2010-08-05 17:25:36 +02:00
|
|
|
*
|
|
|
|
* If not found, returns InvalidOid if missing_ok, else throws error.
|
|
|
|
*/
|
|
|
|
Oid
|
|
|
|
get_opclass_oid(Oid amID, List *opclassname, bool missing_ok)
|
|
|
|
{
|
|
|
|
HeapTuple htup;
|
|
|
|
Oid opcID;
|
|
|
|
|
|
|
|
htup = OpClassCacheLookup(amID, opclassname, missing_ok);
|
|
|
|
if (!HeapTupleIsValid(htup))
|
|
|
|
return InvalidOid;
|
|
|
|
opcID = HeapTupleGetOid(htup);
|
|
|
|
ReleaseSysCache(htup);
|
|
|
|
|
|
|
|
return opcID;
|
2006-12-23 01:43:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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];
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_opfamily];
|
2006-12-23 01:43:13 +01:00
|
|
|
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").
|
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
|
|
|
|
ObjectIdGetDatum(amoid),
|
|
|
|
CStringGetDatum(opfname),
|
|
|
|
ObjectIdGetDatum(namespaceoid)))
|
2006-12-23 01:43:13 +01:00
|
|
|
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));
|
2008-11-02 02:45:28 +01:00
|
|
|
memset(nulls, false, sizeof(nulls));
|
2006-12-23 01:43:13 +01:00
|
|
|
|
|
|
|
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());
|
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
tup = heap_form_tuple(rel->rd_att, values, nulls);
|
2006-12-23 01:43:13 +01:00
|
|
|
|
|
|
|
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());
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
/* dependency on extension */
|
2011-07-23 22:59:39 +02:00
|
|
|
recordDependencyOnCurrentExtension(&myself, false);
|
2011-02-08 22:08:41 +01:00
|
|
|
|
2010-11-25 17:48:49 +01:00
|
|
|
/* Post creation hook for new operator family */
|
2013-03-07 02:52:06 +01:00
|
|
|
InvokeObjectPostCreateHook(OperatorFamilyRelationId, opfamilyoid, 0);
|
2010-11-25 17:48:49 +01:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
|
|
|
|
return opfamilyoid;
|
|
|
|
}
|
|
|
|
|
2002-07-30 00:14:11 +02:00
|
|
|
/*
|
|
|
|
* DefineOpClass
|
|
|
|
* Define a new index operator class.
|
|
|
|
*/
|
2012-12-29 13:55:37 +01:00
|
|
|
Oid
|
2002-07-30 00:14:11 +02:00
|
|
|
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 */
|
2006-12-23 01:43:13 +01:00
|
|
|
opfamilyoid, /* oid of containing opfamily */
|
2002-07-30 00:14:11 +02:00
|
|
|
opclassoid; /* oid of opclass we create */
|
2006-12-18 19:56:29 +01:00
|
|
|
int maxOpNumber, /* amstrategies value */
|
|
|
|
maxProcNumber; /* amsupport value */
|
2006-05-03 00:25:10 +02:00
|
|
|
bool amstorage; /* amstorage flag */
|
2006-12-23 01:43:13 +01:00
|
|
|
List *operators; /* OpFamilyMember list for operators */
|
|
|
|
List *procedures; /* OpFamilyMember list for support procs */
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
2002-07-30 00:14:11 +02:00
|
|
|
Relation rel;
|
|
|
|
HeapTuple tup;
|
2006-05-03 00:25:10 +02:00
|
|
|
Form_pg_am pg_am;
|
2002-07-30 00:14:11 +02:00
|
|
|
Datum values[Natts_pg_opclass];
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_opclass];
|
2002-07-30 00:14:11 +02:00
|
|
|
AclResult aclresult;
|
|
|
|
NameData opcName;
|
2002-09-04 22:31:48 +02:00
|
|
|
ObjectAddress myself,
|
|
|
|
referenced;
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
/* 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)
|
2003-08-01 02:15:26 +02:00
|
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
|
|
get_namespace_name(namespaceoid));
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
/* Get necessary info about access method */
|
2010-02-14 19:42:19 +01:00
|
|
|
tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
|
2002-07-30 00:14:11 +02:00
|
|
|
if (!HeapTupleIsValid(tup))
|
2003-07-19 01:20:33 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("access method \"%s\" does not exist",
|
|
|
|
stmt->amname)));
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
amoid = HeapTupleGetOid(tup);
|
2006-05-03 00:25:10 +02:00
|
|
|
pg_am = (Form_pg_am) GETSTRUCT(tup);
|
2006-12-18 19:56:29 +01:00
|
|
|
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;
|
2006-05-03 00:25:10 +02:00
|
|
|
amstorage = pg_am->amstorage;
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
/* XXX Should we make any privilege check against the AM? */
|
|
|
|
|
|
|
|
ReleaseSysCache(tup);
|
|
|
|
|
2002-10-05 00:19:29 +02:00
|
|
|
/*
|
2006-01-13 19:10:25 +01:00
|
|
|
* The question of appropriate permissions for CREATE OPERATOR CLASS is
|
|
|
|
* interesting. Creating an opclass is tantamount to granting public
|
|
|
|
* execute access on the functions involved, since the index machinery
|
|
|
|
* generally does not check access permission before using the functions.
|
|
|
|
* A minimum expectation therefore is that the caller have execute
|
|
|
|
* privilege with grant option. Since we don't have a way to make the
|
|
|
|
* opclass go away if the grant option is revoked, we choose instead to
|
2006-10-04 02:30:14 +02:00
|
|
|
* require ownership of the functions. It's also not entirely clear what
|
2006-01-13 19:10:25 +01:00
|
|
|
* permissions should be required on the datatype, but ownership seems
|
|
|
|
* like a safe choice.
|
|
|
|
*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Currently, we require superuser privileges to create an opclass. This
|
|
|
|
* seems necessary because we have no way to validate that the offered set
|
|
|
|
* of operators and functions are consistent with the AM's expectations.
|
|
|
|
* It would be nice to provide such a check someday, if it can be done
|
|
|
|
* without solving the halting problem :-(
|
2006-01-13 19:10:25 +01:00
|
|
|
*
|
|
|
|
* XXX re-enable NOT_USED code sections below if you remove this test.
|
2002-10-05 00:19:29 +02:00
|
|
|
*/
|
|
|
|
if (!superuser())
|
2003-07-19 01:20:33 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("must be superuser to create an operator class")));
|
2002-10-05 00:19:29 +02:00
|
|
|
|
2002-07-30 00:14:11 +02:00
|
|
|
/* Look up the datatype */
|
2010-10-25 20:40:46 +02:00
|
|
|
typeoid = typenameTypeId(NULL, stmt->datatype);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2002-10-05 00:19:29 +02:00
|
|
|
#ifdef NOT_USED
|
|
|
|
/* XXX this is unnecessary given the superuser check above */
|
2002-07-30 00:14:11 +02:00
|
|
|
/* Check we have ownership of the datatype */
|
|
|
|
if (!pg_type_ownercheck(typeoid, GetUserId()))
|
2012-06-15 21:55:03 +02:00
|
|
|
aclcheck_error_type(ACLCHECK_NOT_OWNER, typeoid);
|
2002-10-05 00:19:29 +02:00
|
|
|
#endif
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
/*
|
|
|
|
* Look up the containing operator family, or create one if FAMILY option
|
|
|
|
* was omitted and there's not a match already.
|
|
|
|
*/
|
|
|
|
if (stmt->opfamilyname)
|
|
|
|
{
|
2010-08-05 17:25:36 +02:00
|
|
|
opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
|
2006-12-23 01:43:13 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Lookup existing family of same name and namespace */
|
2010-02-14 19:42:19 +01:00
|
|
|
tup = SearchSysCache3(OPFAMILYAMNAMENSP,
|
|
|
|
ObjectIdGetDatum(amoid),
|
|
|
|
PointerGetDatum(opcname),
|
|
|
|
ObjectIdGetDatum(namespaceoid));
|
2006-12-23 01:43:13 +01:00
|
|
|
if (HeapTupleIsValid(tup))
|
|
|
|
{
|
|
|
|
opfamilyoid = HeapTupleGetOid(tup);
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
operators = NIL;
|
|
|
|
procedures = NIL;
|
|
|
|
|
2002-07-30 00:14:11 +02:00
|
|
|
/* Storage datatype is optional */
|
|
|
|
storageoid = InvalidOid;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan the "items" list to obtain additional info.
|
|
|
|
*/
|
2003-11-12 22:15:59 +01:00
|
|
|
foreach(l, stmt->items)
|
2002-07-30 00:14:11 +02:00
|
|
|
{
|
2003-11-12 22:15:59 +01:00
|
|
|
CreateOpClassItem *item = lfirst(l);
|
2002-07-30 00:14:11 +02:00
|
|
|
Oid operOid;
|
|
|
|
Oid funcOid;
|
2010-11-24 20:20:39 +01:00
|
|
|
Oid sortfamilyOid;
|
2006-12-23 01:43:13 +01:00
|
|
|
OpFamilyMember *member;
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
Assert(IsA(item, CreateOpClassItem));
|
|
|
|
switch (item->itemtype)
|
|
|
|
{
|
|
|
|
case OPCLASS_ITEM_OPERATOR:
|
2006-12-18 19:56:29 +01:00
|
|
|
if (item->number <= 0 || item->number > maxOpNumber)
|
2003-07-19 01:20:33 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("invalid operator number %d,"
|
|
|
|
" must be between 1 and %d",
|
2006-12-18 19:56:29 +01:00
|
|
|
item->number, maxOpNumber)));
|
2002-07-30 00:14:11 +02:00
|
|
|
if (item->args != NIL)
|
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
TypeName *typeName1 = (TypeName *) linitial(item->args);
|
2002-09-04 22:31:48 +02:00
|
|
|
TypeName *typeName2 = (TypeName *) lsecond(item->args);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-03-14 23:48:25 +01:00
|
|
|
operOid = LookupOperNameTypeNames(NULL, item->name,
|
|
|
|
typeName1, typeName2,
|
|
|
|
false, -1);
|
2002-07-30 00:14:11 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Default to binary op on input datatype */
|
2006-03-14 23:48:25 +01:00
|
|
|
operOid = LookupOperName(NULL, item->name,
|
|
|
|
typeoid, typeoid,
|
|
|
|
false, -1);
|
2002-07-30 00:14:11 +02:00
|
|
|
}
|
2006-01-13 19:10:25 +01:00
|
|
|
|
2010-11-24 20:20:39 +01:00
|
|
|
if (item->order_family)
|
|
|
|
sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
|
|
|
|
item->order_family,
|
|
|
|
false);
|
|
|
|
else
|
|
|
|
sortfamilyOid = InvalidOid;
|
|
|
|
|
2006-01-13 19:10:25 +01:00
|
|
|
#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));
|
2002-07-30 00:14:11 +02:00
|
|
|
funcOid = get_opcode(operOid);
|
2006-01-13 19:10:25 +01:00
|
|
|
if (!pg_proc_ownercheck(funcOid, GetUserId()))
|
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
|
2003-08-01 02:15:26 +02:00
|
|
|
get_func_name(funcOid));
|
2006-01-13 19:10:25 +01:00
|
|
|
#endif
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
/* Save the info */
|
2006-12-23 01:43:13 +01:00
|
|
|
member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
|
2003-11-12 22:15:59 +01:00
|
|
|
member->object = operOid;
|
|
|
|
member->number = item->number;
|
2010-11-24 20:20:39 +01:00
|
|
|
member->sortfamily = sortfamilyOid;
|
2006-12-23 01:43:13 +01:00
|
|
|
assignOperTypes(member, amoid, typeoid);
|
|
|
|
addFamilyMember(&operators, member, false);
|
2002-07-30 00:14:11 +02:00
|
|
|
break;
|
|
|
|
case OPCLASS_ITEM_FUNCTION:
|
2006-12-18 19:56:29 +01:00
|
|
|
if (item->number <= 0 || item->number > maxProcNumber)
|
2003-07-19 01:20:33 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("invalid procedure number %d,"
|
|
|
|
" must be between 1 and %d",
|
2006-12-18 19:56:29 +01:00
|
|
|
item->number, maxProcNumber)));
|
2002-07-30 00:14:11 +02:00
|
|
|
funcOid = LookupFuncNameTypeNames(item->name, item->args,
|
2003-07-04 04:51:34 +02:00
|
|
|
false);
|
2006-01-13 19:10:25 +01:00
|
|
|
#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,
|
2003-08-01 02:15:26 +02:00
|
|
|
get_func_name(funcOid));
|
2006-01-13 19:10:25 +01:00
|
|
|
#endif
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
/* Save the info */
|
2006-12-23 01:43:13 +01:00
|
|
|
member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
|
2003-11-12 22:15:59 +01:00
|
|
|
member->object = funcOid;
|
|
|
|
member->number = item->number;
|
2007-01-23 06:07:18 +01:00
|
|
|
|
|
|
|
/* allow overriding of the function's actual arg types */
|
|
|
|
if (item->class_args)
|
|
|
|
processTypesSpec(item->class_args,
|
|
|
|
&member->lefttype, &member->righttype);
|
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
assignProcTypes(member, amoid, typeoid);
|
|
|
|
addFamilyMember(&procedures, member, true);
|
2002-07-30 00:14:11 +02:00
|
|
|
break;
|
|
|
|
case OPCLASS_ITEM_STORAGETYPE:
|
|
|
|
if (OidIsValid(storageoid))
|
2003-07-19 01:20:33 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("storage type specified more than once")));
|
2010-10-25 20:40:46 +02:00
|
|
|
storageoid = typenameTypeId(NULL, item->storedtype);
|
2006-01-13 19:10:25 +01:00
|
|
|
|
|
|
|
#ifdef NOT_USED
|
|
|
|
/* XXX this is unnecessary given the superuser check above */
|
|
|
|
/* Check we have ownership of the datatype */
|
|
|
|
if (!pg_type_ownercheck(storageoid, GetUserId()))
|
2012-06-15 21:55:03 +02:00
|
|
|
aclcheck_error_type(ACLCHECK_NOT_OWNER, storageoid);
|
2006-01-13 19:10:25 +01:00
|
|
|
#endif
|
2002-07-30 00:14:11 +02:00
|
|
|
break;
|
|
|
|
default:
|
2003-07-19 01:20:33 +02:00
|
|
|
elog(ERROR, "unrecognized item type: %d", item->itemtype);
|
2002-07-30 00:14:11 +02:00
|
|
|
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;
|
2006-05-03 00:25:10 +02:00
|
|
|
else if (!amstorage)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
Wording cleanup for error messages. Also change can't -> cannot.
Standard English uses "may", "can", and "might" in different ways:
may - permission, "You may borrow my rake."
can - ability, "I can lift that log."
might - possibility, "It might rain today."
Unfortunately, in conversational English, their use is often mixed, as
in, "You may use this variable to do X", when in fact, "can" is a better
choice. Similarly, "It may crash" is better stated, "It might crash".
2007-02-01 20:10:30 +01:00
|
|
|
errmsg("storage type cannot be different from data type for access method \"%s\"",
|
2006-05-03 00:25:10 +02:00
|
|
|
stmt->amname)));
|
2002-07-30 00:14:11 +02:00
|
|
|
}
|
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Make sure there is no existing opclass of this name (this is just to
|
|
|
|
* give a more friendly error message than "duplicate key").
|
2002-07-30 00:14:11 +02:00
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
if (SearchSysCacheExists3(CLAAMNAMENSP,
|
|
|
|
ObjectIdGetDatum(amoid),
|
|
|
|
CStringGetDatum(opcname),
|
|
|
|
ObjectIdGetDatum(namespaceoid)))
|
2003-07-19 01:20:33 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
2003-07-20 23:56:35 +02:00
|
|
|
errmsg("operator class \"%s\" for access method \"%s\" already exists",
|
2003-07-19 01:20:33 +02:00
|
|
|
opcname, stmt->amname)));
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If we are creating a default opclass, check there isn't one already.
|
|
|
|
* (Note we do not restrict this test to visible opclasses; this ensures
|
|
|
|
* that typcache.c can find unique solutions to its questions.)
|
2002-07-30 00:14:11 +02:00
|
|
|
*/
|
|
|
|
if (stmt->isDefault)
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
ScanKeyData skey[1];
|
2002-07-30 00:14:11 +02:00
|
|
|
SysScanDesc scan;
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&skey[0],
|
2006-12-23 01:43:13 +01:00
|
|
|
Anum_pg_opclass_opcmethod,
|
2003-11-12 22:15:59 +01:00
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(amoid));
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 1, skey);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
|
|
|
{
|
|
|
|
Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
if (opclass->opcintype == typeoid && opclass->opcdefault)
|
2003-07-19 01:20:33 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
2003-09-26 17:27:37 +02:00
|
|
|
errmsg("could not make operator class \"%s\" be default for type %s",
|
2003-07-19 01:20:33 +02:00
|
|
|
opcname,
|
|
|
|
TypeNameToString(stmt->datatype)),
|
2005-10-15 04:49:52 +02:00
|
|
|
errdetail("Operator class \"%s\" already is the default.",
|
|
|
|
NameStr(opclass->opcname))));
|
2002-07-30 00:14:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Okay, let's create the pg_opclass entry.
|
|
|
|
*/
|
2006-12-23 01:43:13 +01:00
|
|
|
memset(values, 0, sizeof(values));
|
2008-11-02 02:45:28 +01:00
|
|
|
memset(nulls, false, sizeof(nulls));
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
|
2002-07-30 00:14:11 +02:00
|
|
|
namestrcpy(&opcName, opcname);
|
2006-12-23 01:43:13 +01:00
|
|
|
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);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
tup = heap_form_tuple(rel->rd_att, values, nulls);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
opclassoid = simple_heap_insert(rel, tup);
|
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogUpdateIndexes(rel, tup);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
heap_freetuple(tup);
|
|
|
|
|
|
|
|
/*
|
2002-09-04 22:31:48 +02:00
|
|
|
* Now add tuples to pg_amop and pg_amproc tying in the operators and
|
2006-12-23 01:43:13 +01:00
|
|
|
* functions. Dependencies on them are inserted, too.
|
2002-07-30 00:14:11 +02:00
|
|
|
*/
|
2007-01-23 06:07:18 +01:00
|
|
|
storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
|
|
|
|
opclassoid, operators, false);
|
|
|
|
storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
|
|
|
|
opclassoid, procedures, false);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
/*
|
2007-11-15 22:14:46 +01:00
|
|
|
* Create dependencies for the opclass proper. Note: we do not create a
|
2006-12-23 01:43:13 +01:00
|
|
|
* dependency link to the AM, because we don't currently support DROP
|
|
|
|
* ACCESS METHOD.
|
2002-07-30 00:14:11 +02:00
|
|
|
*/
|
2005-04-14 22:03:27 +02:00
|
|
|
myself.classId = OperatorClassRelationId;
|
2002-07-30 00:14:11 +02:00
|
|
|
myself.objectId = opclassoid;
|
|
|
|
myself.objectSubId = 0;
|
|
|
|
|
|
|
|
/* dependency on namespace */
|
2005-04-14 22:03:27 +02:00
|
|
|
referenced.classId = NamespaceRelationId;
|
2002-07-30 00:14:11 +02:00
|
|
|
referenced.objectId = namespaceoid;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
/* dependency on opfamily */
|
|
|
|
referenced.classId = OperatorFamilyRelationId;
|
|
|
|
referenced.objectId = opfamilyoid;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
|
|
|
|
|
2002-07-30 00:14:11 +02:00
|
|
|
/* dependency on indexed datatype */
|
2005-04-14 03:38:22 +02:00
|
|
|
referenced.classId = TypeRelationId;
|
2002-07-30 00:14:11 +02:00
|
|
|
referenced.objectId = typeoid;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
|
|
|
|
/* dependency on storage datatype */
|
|
|
|
if (OidIsValid(storageoid))
|
|
|
|
{
|
2005-04-14 03:38:22 +02:00
|
|
|
referenced.classId = TypeRelationId;
|
2002-07-30 00:14:11 +02:00
|
|
|
referenced.objectId = storageoid;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
|
|
|
|
2005-07-07 22:40:02 +02:00
|
|
|
/* dependency on owner */
|
|
|
|
recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
/* dependency on extension */
|
2011-07-23 22:59:39 +02:00
|
|
|
recordDependencyOnCurrentExtension(&myself, false);
|
2011-02-08 22:08:41 +01:00
|
|
|
|
2010-11-25 17:48:49 +01:00
|
|
|
/* Post creation hook for new operator class */
|
2013-03-07 02:52:06 +01:00
|
|
|
InvokeObjectPostCreateHook(OperatorClassRelationId, opclassoid, 0);
|
2010-11-25 17:48:49 +01:00
|
|
|
|
2002-07-30 00:14:11 +02:00
|
|
|
heap_close(rel, RowExclusiveLock);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
|
|
|
return opclassoid;
|
2002-07-30 00:14:11 +02:00
|
|
|
}
|
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
/*
|
2007-01-23 06:07:18 +01:00
|
|
|
* DefineOpFamily
|
|
|
|
* Define a new index operator family.
|
2003-11-12 22:15:59 +01:00
|
|
|
*/
|
2012-12-29 13:55:37 +01:00
|
|
|
Oid
|
2007-11-15 23:25:18 +01:00
|
|
|
DefineOpFamily(CreateOpFamilyStmt *stmt)
|
2003-11-12 22:15:59 +01:00
|
|
|
{
|
2007-01-23 06:07:18 +01:00
|
|
|
char *opfname; /* name of opfamily we're creating */
|
|
|
|
Oid amoid, /* our AM's oid */
|
2010-07-16 02:13:23 +02:00
|
|
|
namespaceoid; /* namespace to create opfamily in */
|
2007-01-23 06:07:18 +01:00
|
|
|
AclResult aclresult;
|
2003-11-12 22:15:59 +01:00
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
/* 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));
|
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
/* Get access method OID, throwing an error if it doesn't exist. */
|
|
|
|
amoid = get_am_oid(stmt->amname, false);
|
2007-01-23 06:07:18 +01:00
|
|
|
|
|
|
|
/* XXX Should we make any privilege check against the AM? */
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
/*
|
2007-11-15 22:14:46 +01:00
|
|
|
* Currently, we require superuser privileges to create an opfamily. See
|
|
|
|
* comments in DefineOpClass.
|
2003-11-12 22:15:59 +01:00
|
|
|
*/
|
2007-01-23 06:07:18 +01:00
|
|
|
if (!superuser())
|
2003-11-12 22:15:59 +01:00
|
|
|
ereport(ERROR,
|
2007-01-23 06:07:18 +01:00
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg("must be superuser to create an operator family")));
|
|
|
|
|
2010-07-16 02:13:23 +02:00
|
|
|
/* Insert pg_opfamily catalog entry */
|
2012-12-29 13:55:37 +01:00
|
|
|
return CreateOpFamily(stmt->amname, opfname, namespaceoid, amoid);
|
2003-11-12 22:15:59 +01:00
|
|
|
}
|
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
/*
|
2007-01-23 06:07:18 +01:00
|
|
|
* 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.
|
2003-11-12 22:15:59 +01:00
|
|
|
*/
|
2012-12-29 13:55:37 +01:00
|
|
|
Oid
|
2007-11-15 23:25:18 +01:00
|
|
|
AlterOpFamily(AlterOpFamilyStmt *stmt)
|
2003-11-12 22:15:59 +01:00
|
|
|
{
|
2007-01-23 06:07:18 +01:00
|
|
|
Oid amoid, /* our AM's oid */
|
|
|
|
opfamilyoid; /* oid of opfamily */
|
|
|
|
int maxOpNumber, /* amstrategies value */
|
|
|
|
maxProcNumber; /* amsupport value */
|
|
|
|
HeapTuple tup;
|
|
|
|
Form_pg_am pg_am;
|
2003-11-12 22:15:59 +01:00
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
/* Get necessary info about access method */
|
2010-02-14 19:42:19 +01:00
|
|
|
tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
|
2007-01-23 06:07:18 +01:00
|
|
|
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 */
|
2010-08-05 17:25:36 +02:00
|
|
|
opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
/*
|
2007-01-23 06:07:18 +01:00
|
|
|
* Currently, we require superuser privileges to alter an opfamily.
|
|
|
|
*
|
|
|
|
* XXX re-enable NOT_USED code sections below if you remove this test.
|
2003-11-12 22:15:59 +01:00
|
|
|
*/
|
2007-01-23 06:07:18 +01:00
|
|
|
if (!superuser())
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg("must be superuser to alter an operator family")));
|
2006-12-23 01:43:13 +01:00
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
/*
|
|
|
|
* ADD and DROP cases need separate code from here on down.
|
|
|
|
*/
|
|
|
|
if (stmt->isDrop)
|
|
|
|
AlterOpFamilyDrop(stmt->opfamilyname, amoid, opfamilyoid,
|
|
|
|
maxOpNumber, maxProcNumber,
|
|
|
|
stmt->items);
|
2003-11-12 22:15:59 +01:00
|
|
|
else
|
2007-01-23 06:07:18 +01:00
|
|
|
AlterOpFamilyAdd(stmt->opfamilyname, amoid, opfamilyoid,
|
|
|
|
maxOpNumber, maxProcNumber,
|
|
|
|
stmt->items);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
|
|
|
return opfamilyoid;
|
2003-11-12 22:15:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-01-23 06:07:18 +01:00
|
|
|
* ADD part of ALTER OP FAMILY
|
2003-11-12 22:15:59 +01:00
|
|
|
*/
|
|
|
|
static void
|
2007-01-23 06:07:18 +01:00
|
|
|
AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
|
|
|
|
int maxOpNumber, int maxProcNumber,
|
|
|
|
List *items)
|
2003-11-12 22:15:59 +01:00
|
|
|
{
|
2007-01-23 06:07:18 +01:00
|
|
|
List *operators; /* OpFamilyMember list for operators */
|
|
|
|
List *procedures; /* OpFamilyMember list for support procs */
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
2003-11-12 22:15:59 +01:00
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
operators = NIL;
|
|
|
|
procedures = NIL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan the "items" list to obtain additional info.
|
|
|
|
*/
|
|
|
|
foreach(l, items)
|
2003-11-12 22:15:59 +01:00
|
|
|
{
|
2007-01-23 06:07:18 +01:00
|
|
|
CreateOpClassItem *item = lfirst(l);
|
|
|
|
Oid operOid;
|
|
|
|
Oid funcOid;
|
2010-11-24 20:20:39 +01:00
|
|
|
Oid sortfamilyOid;
|
2007-01-23 06:07:18 +01:00
|
|
|
OpFamilyMember *member;
|
2003-11-12 22:15:59 +01:00
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
Assert(IsA(item, CreateOpClassItem));
|
|
|
|
switch (item->itemtype)
|
2003-11-12 22:15:59 +01:00
|
|
|
{
|
2007-01-23 06:07:18 +01:00
|
|
|
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")));
|
2007-11-15 22:14:46 +01:00
|
|
|
operOid = InvalidOid; /* keep compiler quiet */
|
2007-01-23 06:07:18 +01:00
|
|
|
}
|
|
|
|
|
2010-11-24 20:20:39 +01:00
|
|
|
if (item->order_family)
|
|
|
|
sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
|
|
|
|
item->order_family,
|
|
|
|
false);
|
|
|
|
else
|
|
|
|
sortfamilyOid = InvalidOid;
|
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
#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;
|
2010-11-24 20:20:39 +01:00
|
|
|
member->sortfamily = sortfamilyOid;
|
2007-01-23 06:07:18 +01:00
|
|
|
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),
|
2007-11-15 22:14:46 +01:00
|
|
|
errmsg("STORAGE cannot be specified in ALTER OPERATOR FAMILY")));
|
2007-01-23 06:07:18 +01:00
|
|
|
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);
|
2010-10-25 20:40:46 +02:00
|
|
|
*lefttype = typenameTypeId(NULL, typeName);
|
2007-01-23 06:07:18 +01:00
|
|
|
|
|
|
|
if (list_length(args) > 1)
|
|
|
|
{
|
|
|
|
typeName = (TypeName *) lsecond(args);
|
2010-10-25 20:40:46 +02:00
|
|
|
*righttype = typenameTypeId(NULL, typeName);
|
2007-01-23 06:07:18 +01:00
|
|
|
}
|
|
|
|
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
|
2007-11-15 23:25:18 +01:00
|
|
|
assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
|
2007-01-23 06:07:18 +01:00
|
|
|
{
|
|
|
|
Operator optup;
|
|
|
|
Form_pg_operator opform;
|
|
|
|
|
|
|
|
/* Fetch the operator definition */
|
2010-02-14 19:42:19 +01:00
|
|
|
optup = SearchSysCache1(OPEROID, ObjectIdGetDatum(member->object));
|
2007-01-23 06:07:18 +01:00
|
|
|
if (optup == NULL)
|
|
|
|
elog(ERROR, "cache lookup failed for operator %u", member->object);
|
|
|
|
opform = (Form_pg_operator) GETSTRUCT(optup);
|
|
|
|
|
|
|
|
/*
|
2010-11-24 20:20:39 +01:00
|
|
|
* Opfamily operators must be binary.
|
2007-01-23 06:07:18 +01:00
|
|
|
*/
|
|
|
|
if (opform->oprkind != 'b')
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("index operators must be binary")));
|
2010-11-24 20:20:39 +01:00
|
|
|
|
|
|
|
if (OidIsValid(member->sortfamily))
|
|
|
|
{
|
|
|
|
/*
|
2011-04-10 17:42:00 +02:00
|
|
|
* Ordering op, check index supports that. (We could perhaps also
|
2010-11-24 20:20:39 +01:00
|
|
|
* check that the operator returns a type supported by the sortfamily,
|
|
|
|
* but that seems more trouble than it's worth here. If it does not,
|
2011-04-10 17:42:00 +02:00
|
|
|
* the operator will never be matchable to any ORDER BY clause, but no
|
|
|
|
* worse consequences can ensue. Also, trying to check that would
|
2010-11-24 20:20:39 +01:00
|
|
|
* create an ordering hazard during dump/reload: it's possible that
|
|
|
|
* the family has been created but not yet populated with the required
|
|
|
|
* operators.)
|
|
|
|
*/
|
|
|
|
HeapTuple amtup;
|
|
|
|
Form_pg_am pg_am;
|
|
|
|
|
|
|
|
amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
|
|
|
|
if (amtup == NULL)
|
|
|
|
elog(ERROR, "cache lookup failed for access method %u", amoid);
|
|
|
|
pg_am = (Form_pg_am) GETSTRUCT(amtup);
|
|
|
|
|
|
|
|
if (!pg_am->amcanorderbyop)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
2011-04-10 17:42:00 +02:00
|
|
|
errmsg("access method \"%s\" does not support ordering operators",
|
|
|
|
NameStr(pg_am->amname))));
|
2010-11-24 20:20:39 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(amtup);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Search operators must return boolean.
|
|
|
|
*/
|
|
|
|
if (opform->oprresult != BOOLOID)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("index search operators must return boolean")));
|
|
|
|
}
|
2007-01-23 06:07:18 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2007-11-15 23:25:18 +01:00
|
|
|
assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
|
2007-01-23 06:07:18 +01:00
|
|
|
{
|
|
|
|
HeapTuple proctup;
|
|
|
|
Form_pg_proc procform;
|
|
|
|
|
|
|
|
/* Fetch the procedure definition */
|
2010-02-14 19:42:19 +01:00
|
|
|
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(member->object));
|
2007-01-23 06:07:18 +01:00
|
|
|
if (proctup == NULL)
|
|
|
|
elog(ERROR, "cache lookup failed for function %u", member->object);
|
|
|
|
procform = (Form_pg_proc) GETSTRUCT(proctup);
|
|
|
|
|
|
|
|
/*
|
2011-12-07 06:18:38 +01:00
|
|
|
* btree comparison procs must be 2-arg procs returning int4, while btree
|
|
|
|
* sortsupport procs must take internal and return void. hash support
|
|
|
|
* procs must be 1-arg procs returning int4. Otherwise we don't know.
|
2007-01-23 06:07:18 +01:00
|
|
|
*/
|
|
|
|
if (amoid == BTREE_AM_OID)
|
|
|
|
{
|
2011-12-07 06:18:38 +01:00
|
|
|
if (member->number == BTORDER_PROC)
|
|
|
|
{
|
|
|
|
if (procform->pronargs != 2)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("btree comparison procedures must have two arguments")));
|
|
|
|
if (procform->prorettype != INT4OID)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
2012-06-10 21:20:04 +02:00
|
|
|
errmsg("btree comparison procedures must return integer")));
|
2007-01-23 06:07:18 +01:00
|
|
|
|
2011-12-07 06:18:38 +01:00
|
|
|
/*
|
|
|
|
* 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 (member->number == BTSORTSUPPORT_PROC)
|
|
|
|
{
|
|
|
|
if (procform->pronargs != 1 ||
|
|
|
|
procform->proargtypes.values[0] != INTERNALOID)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("btree sort support procedures must accept type \"internal\"")));
|
|
|
|
if (procform->prorettype != VOIDOID)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
2012-06-10 21:20:04 +02:00
|
|
|
errmsg("btree sort support procedures must return void")));
|
2011-12-07 06:18:38 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Can't infer lefttype/righttype from proc, so use default rule
|
|
|
|
*/
|
|
|
|
}
|
2007-01-23 06:07:18 +01:00
|
|
|
}
|
|
|
|
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];
|
|
|
|
}
|
2011-12-07 06:18:38 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The default in CREATE OPERATOR CLASS is to use the class' opcintype as
|
2012-06-10 21:20:04 +02:00
|
|
|
* lefttype and righttype. In CREATE or ALTER OPERATOR FAMILY, opcintype
|
2011-12-07 06:18:38 +01:00
|
|
|
* 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")));
|
2007-01-23 06:07:18 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(proctup);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a new family member to the appropriate list, after checking for
|
|
|
|
* duplicated strategy or proc number.
|
|
|
|
*/
|
|
|
|
static void
|
2007-11-15 23:25:18 +01:00
|
|
|
addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
|
2007-01-23 06:07:18 +01:00
|
|
|
{
|
|
|
|
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),
|
2006-12-23 01:43:13 +01:00
|
|
|
format_type_be(member->righttype))));
|
2003-11-12 22:15:59 +01:00
|
|
|
else
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
2006-12-23 01:43:13 +01:00
|
|
|
errmsg("operator number %d for (%s,%s) appears more than once",
|
|
|
|
member->number,
|
|
|
|
format_type_be(member->lefttype),
|
|
|
|
format_type_be(member->righttype))));
|
2003-11-12 22:15:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*list = lappend(*list, member);
|
|
|
|
}
|
|
|
|
|
2002-07-30 00:14:11 +02:00
|
|
|
/*
|
|
|
|
* Dump the operators to pg_amop
|
2006-12-23 01:43:13 +01:00
|
|
|
*
|
|
|
|
* 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.
|
2002-07-30 00:14:11 +02:00
|
|
|
*/
|
|
|
|
static void
|
2007-01-23 06:07:18 +01:00
|
|
|
storeOperators(List *opfamilyname, Oid amoid,
|
|
|
|
Oid opfamilyoid, Oid opclassoid,
|
|
|
|
List *operators, bool isAdd)
|
2002-07-30 00:14:11 +02:00
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
Relation rel;
|
|
|
|
Datum values[Natts_pg_amop];
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_amop];
|
2002-09-04 22:31:48 +02:00
|
|
|
HeapTuple tup;
|
2006-12-23 01:43:13 +01:00
|
|
|
Oid entryoid;
|
|
|
|
ObjectAddress myself,
|
|
|
|
referenced;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
foreach(l, operators)
|
2002-07-30 00:14:11 +02:00
|
|
|
{
|
2006-12-23 01:43:13 +01:00
|
|
|
OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
|
2011-04-10 17:42:00 +02:00
|
|
|
char oppurpose;
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
/*
|
|
|
|
* If adding to an existing family, check for conflict with an
|
|
|
|
* existing pg_amop entry (just to give a nicer error message)
|
|
|
|
*/
|
|
|
|
if (isAdd &&
|
2010-02-14 19:42:19 +01:00
|
|
|
SearchSysCacheExists4(AMOPSTRATEGY,
|
|
|
|
ObjectIdGetDatum(opfamilyoid),
|
|
|
|
ObjectIdGetDatum(op->lefttype),
|
|
|
|
ObjectIdGetDatum(op->righttype),
|
|
|
|
Int16GetDatum(op->number)))
|
2007-01-23 06:07:18 +01:00
|
|
|
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))));
|
|
|
|
|
2010-11-24 20:20:39 +01:00
|
|
|
oppurpose = OidIsValid(op->sortfamily) ? AMOP_ORDER : AMOP_SEARCH;
|
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
/* Create the pg_amop entry */
|
|
|
|
memset(values, 0, sizeof(values));
|
2008-11-02 02:45:28 +01:00
|
|
|
memset(nulls, false, sizeof(nulls));
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
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);
|
2010-11-24 20:20:39 +01:00
|
|
|
values[Anum_pg_amop_amoppurpose - 1] = CharGetDatum(oppurpose);
|
2006-12-23 01:43:13 +01:00
|
|
|
values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
|
|
|
|
values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
|
2010-11-24 20:20:39 +01:00
|
|
|
values[Anum_pg_amop_amopsortfamily - 1] = ObjectIdGetDatum(op->sortfamily);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
tup = heap_form_tuple(rel->rd_att, values, nulls);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
entryoid = simple_heap_insert(rel, tup);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogUpdateIndexes(rel, tup);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
heap_freetuple(tup);
|
2006-12-23 01:43:13 +01:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
2010-11-24 20:20:39 +01:00
|
|
|
|
|
|
|
/* A search operator also needs a dep on the referenced opfamily */
|
|
|
|
if (OidIsValid(op->sortfamily))
|
|
|
|
{
|
|
|
|
referenced.classId = OperatorFamilyRelationId;
|
|
|
|
referenced.objectId = op->sortfamily;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
}
|
2013-03-18 03:55:14 +01:00
|
|
|
/* Post create hook of this access method operator */
|
|
|
|
InvokeObjectPostCreateHook(AccessMethodOperatorRelationId,
|
|
|
|
entryoid, 0);
|
2002-07-30 00:14:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dump the procedures (support routines) to pg_amproc
|
2006-12-23 01:43:13 +01:00
|
|
|
*
|
|
|
|
* 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.
|
2002-07-30 00:14:11 +02:00
|
|
|
*/
|
|
|
|
static void
|
2007-01-23 06:07:18 +01:00
|
|
|
storeProcedures(List *opfamilyname, Oid amoid,
|
|
|
|
Oid opfamilyoid, Oid opclassoid,
|
|
|
|
List *procedures, bool isAdd)
|
2002-07-30 00:14:11 +02:00
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
Relation rel;
|
|
|
|
Datum values[Natts_pg_amproc];
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_amproc];
|
2002-09-04 22:31:48 +02:00
|
|
|
HeapTuple tup;
|
2006-12-23 01:43:13 +01:00
|
|
|
Oid entryoid;
|
|
|
|
ObjectAddress myself,
|
|
|
|
referenced;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
foreach(l, procedures)
|
2002-07-30 00:14:11 +02:00
|
|
|
{
|
2006-12-23 01:43:13 +01:00
|
|
|
OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
/*
|
|
|
|
* If adding to an existing family, check for conflict with an
|
|
|
|
* existing pg_amproc entry (just to give a nicer error message)
|
|
|
|
*/
|
|
|
|
if (isAdd &&
|
2010-02-14 19:42:19 +01:00
|
|
|
SearchSysCacheExists4(AMPROCNUM,
|
|
|
|
ObjectIdGetDatum(opfamilyoid),
|
|
|
|
ObjectIdGetDatum(proc->lefttype),
|
|
|
|
ObjectIdGetDatum(proc->righttype),
|
|
|
|
Int16GetDatum(proc->number)))
|
2007-01-23 06:07:18 +01:00
|
|
|
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))));
|
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
/* Create the pg_amproc entry */
|
|
|
|
memset(values, 0, sizeof(values));
|
2008-11-02 02:45:28 +01:00
|
|
|
memset(nulls, false, sizeof(nulls));
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
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);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
tup = heap_form_tuple(rel->rd_att, values, nulls);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
entryoid = simple_heap_insert(rel, tup);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogUpdateIndexes(rel, tup);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
heap_freetuple(tup);
|
2006-12-23 01:43:13 +01:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
2013-03-18 03:55:14 +01:00
|
|
|
/* Post create hook of access method procedure */
|
|
|
|
InvokeObjectPostCreateHook(AccessMethodProcedureRelationId,
|
|
|
|
entryoid, 0);
|
2002-07-30 00:14:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
amopid = GetSysCacheOid4(AMOPSTRATEGY,
|
|
|
|
ObjectIdGetDatum(opfamilyoid),
|
|
|
|
ObjectIdGetDatum(op->lefttype),
|
|
|
|
ObjectIdGetDatum(op->righttype),
|
|
|
|
Int16GetDatum(op->number));
|
2007-01-23 06:07:18 +01:00
|
|
|
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;
|
|
|
|
|
2012-01-26 15:24:54 +01:00
|
|
|
performDeletion(&object, DROP_RESTRICT, 0);
|
2007-01-23 06:07:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
amprocid = GetSysCacheOid4(AMPROCNUM,
|
|
|
|
ObjectIdGetDatum(opfamilyoid),
|
|
|
|
ObjectIdGetDatum(op->lefttype),
|
|
|
|
ObjectIdGetDatum(op->righttype),
|
|
|
|
Int16GetDatum(op->number));
|
2007-01-23 06:07:18 +01:00
|
|
|
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;
|
|
|
|
|
2012-01-26 15:24:54 +01:00
|
|
|
performDeletion(&object, DROP_RESTRICT, 0);
|
2007-01-23 06:07:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-07-30 00:14:11 +02:00
|
|
|
/*
|
2006-12-23 01:43:13 +01:00
|
|
|
* Deletion subroutines for use by dependency.c.
|
2002-07-30 00:14:11 +02:00
|
|
|
*/
|
2006-12-23 01:43:13 +01:00
|
|
|
void
|
|
|
|
RemoveOpFamilyById(Oid opfamilyOid)
|
|
|
|
{
|
|
|
|
Relation rel;
|
|
|
|
HeapTuple tup;
|
|
|
|
|
|
|
|
rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyOid));
|
2006-12-23 01:43:13 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2002-07-30 00:14:11 +02:00
|
|
|
void
|
|
|
|
RemoveOpClassById(Oid opclassOid)
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
Relation rel;
|
|
|
|
HeapTuple tup;
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassOid));
|
2002-09-04 22:31:48 +02:00
|
|
|
if (!HeapTupleIsValid(tup)) /* should not happen */
|
2003-07-19 01:20:33 +02:00
|
|
|
elog(ERROR, "cache lookup failed for opclass %u", opclassOid);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
simple_heap_delete(rel, &tup->t_self);
|
|
|
|
|
|
|
|
ReleaseSysCache(tup);
|
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
2006-12-23 01:43:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RemoveAmOpEntryById(Oid entryOid)
|
|
|
|
{
|
|
|
|
Relation rel;
|
|
|
|
HeapTuple tup;
|
|
|
|
ScanKeyData skey[1];
|
|
|
|
SysScanDesc scan;
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&skey[0],
|
2006-12-23 01:43:13 +01:00
|
|
|
ObjectIdAttributeNumber,
|
2003-11-12 22:15:59 +01:00
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
2006-12-23 01:43:13 +01:00
|
|
|
ObjectIdGetDatum(entryOid));
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
scan = systable_beginscan(rel, AccessMethodOperatorOidIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 1, skey);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
/* 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);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
2006-12-23 01:43:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RemoveAmProcEntryById(Oid entryOid)
|
|
|
|
{
|
|
|
|
Relation rel;
|
|
|
|
HeapTuple tup;
|
|
|
|
ScanKeyData skey[1];
|
|
|
|
SysScanDesc scan;
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&skey[0],
|
2006-12-23 01:43:13 +01:00
|
|
|
ObjectIdAttributeNumber,
|
2003-11-12 22:15:59 +01:00
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
2006-12-23 01:43:13 +01:00
|
|
|
ObjectIdGetDatum(entryOid));
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
scan = systable_beginscan(rel, AccessMethodProcedureOidIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 1, skey);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2006-12-23 01:43:13 +01:00
|
|
|
/* 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);
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
2003-06-27 16:45:32 +02:00
|
|
|
|
2013-01-21 16:06:41 +01:00
|
|
|
static char *
|
|
|
|
get_am_name(Oid amOid)
|
2003-06-27 16:45:32 +02:00
|
|
|
{
|
|
|
|
HeapTuple tup;
|
2013-01-21 16:06:41 +01:00
|
|
|
char *result = NULL;
|
2003-06-27 16:45:32 +02:00
|
|
|
|
2013-01-21 16:06:41 +01:00
|
|
|
tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
|
|
|
|
if (HeapTupleIsValid(tup))
|
|
|
|
{
|
|
|
|
result = pstrdup(NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
|
|
|
|
ReleaseSysCache(tup);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2003-06-27 16:45:32 +02:00
|
|
|
|
2013-01-21 16:06:41 +01:00
|
|
|
/*
|
|
|
|
* Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
|
|
|
|
*
|
|
|
|
* Is there an operator class with the given name and signature already
|
2013-05-29 22:58:43 +02:00
|
|
|
* in the given namespace? If so, raise an appropriate error message.
|
2013-01-21 16:06:41 +01:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
|
|
|
|
Oid opcnamespace)
|
|
|
|
{
|
2003-06-27 16:45:32 +02:00
|
|
|
/* make sure the new name doesn't exist */
|
2010-02-14 19:42:19 +01:00
|
|
|
if (SearchSysCacheExists3(CLAAMNAMENSP,
|
2013-01-21 16:06:41 +01:00
|
|
|
ObjectIdGetDatum(opcmethod),
|
|
|
|
CStringGetDatum(opcname),
|
|
|
|
ObjectIdGetDatum(opcnamespace)))
|
2003-06-27 16:45:32 +02:00
|
|
|
ereport(ERROR,
|
2003-07-19 01:20:33 +02:00
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
2003-06-27 16:45:32 +02:00
|
|
|
errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
|
2013-01-21 16:06:41 +01:00
|
|
|
opcname,
|
|
|
|
get_am_name(opcmethod),
|
|
|
|
get_namespace_name(opcnamespace))));
|
2003-06-27 16:45:32 +02:00
|
|
|
}
|
2004-06-25 23:55:59 +02:00
|
|
|
|
|
|
|
/*
|
2013-01-21 16:06:41 +01:00
|
|
|
* Subroutine for ALTER OPERATOR FAMILY SET SCHEMA/RENAME
|
|
|
|
*
|
|
|
|
* Is there an operator family with the given name and signature already
|
2013-05-29 22:58:43 +02:00
|
|
|
* in the given namespace? If so, raise an appropriate error message.
|
2005-11-21 13:49:33 +01:00
|
|
|
*/
|
2013-01-21 16:06:41 +01:00
|
|
|
void
|
|
|
|
IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
|
|
|
|
Oid opfnamespace)
|
2005-11-21 13:49:33 +01:00
|
|
|
{
|
2007-01-23 06:07:18 +01:00
|
|
|
/* make sure the new name doesn't exist */
|
2010-02-14 19:42:19 +01:00
|
|
|
if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
|
2013-01-21 16:06:41 +01:00
|
|
|
ObjectIdGetDatum(opfmethod),
|
|
|
|
CStringGetDatum(opfname),
|
|
|
|
ObjectIdGetDatum(opfnamespace)))
|
2007-01-23 06:07:18 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
|
2013-01-21 16:06:41 +01:00
|
|
|
opfname,
|
|
|
|
get_am_name(opfmethod),
|
|
|
|
get_namespace_name(opfnamespace))));
|
2005-11-21 13:49:33 +01:00
|
|
|
}
|
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
/*
|
|
|
|
* get_am_oid - given an access method name, look up the OID
|
|
|
|
*
|
|
|
|
* If missing_ok is false, throw an error if access method not found. If
|
|
|
|
* true, just return InvalidOid.
|
|
|
|
*/
|
|
|
|
Oid
|
|
|
|
get_am_oid(const char *amname, bool missing_ok)
|
|
|
|
{
|
|
|
|
Oid oid;
|
|
|
|
|
|
|
|
oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
|
|
|
|
if (!OidIsValid(oid) && !missing_ok)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("access method \"%s\" does not exist", amname)));
|
|
|
|
return oid;
|
|
|
|
}
|