PostgreSQL Source Code  git master
opclasscmds.c File Reference
#include "postgres.h"
#include <limits.h>
#include "access/genam.h"
#include "access/hash.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "access/table.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_am.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
Include dependency graph for opclasscmds.c:

Go to the source code of this file.

Functions

static void AlterOpFamilyAdd (AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, int maxOpNumber, int maxProcNumber, int optsProcNumber, List *items)
 
static void AlterOpFamilyDrop (AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, int maxOpNumber, int maxProcNumber, List *items)
 
static void processTypesSpec (List *args, Oid *lefttype, Oid *righttype)
 
static void assignOperTypes (OpFamilyMember *member, Oid amoid, Oid typeoid)
 
static void assignProcTypes (OpFamilyMember *member, Oid amoid, Oid typeoid, int opclassOptsProcNum)
 
static void addFamilyMember (List **list, OpFamilyMember *member)
 
static void storeOperators (List *opfamilyname, Oid amoid, Oid opfamilyoid, List *operators, bool isAdd)
 
static void storeProcedures (List *opfamilyname, Oid amoid, Oid opfamilyoid, List *procedures, bool isAdd)
 
static void dropOperators (List *opfamilyname, Oid amoid, Oid opfamilyoid, List *operators)
 
static void dropProcedures (List *opfamilyname, Oid amoid, Oid opfamilyoid, List *procedures)
 
static HeapTuple OpFamilyCacheLookup (Oid amID, List *opfamilyname, bool missing_ok)
 
Oid get_opfamily_oid (Oid amID, List *opfamilyname, bool missing_ok)
 
static HeapTuple OpClassCacheLookup (Oid amID, List *opclassname, bool missing_ok)
 
Oid get_opclass_oid (Oid amID, List *opclassname, bool missing_ok)
 
static ObjectAddress CreateOpFamily (CreateOpFamilyStmt *stmt, const char *opfname, Oid namespaceoid, Oid amoid)
 
ObjectAddress DefineOpClass (CreateOpClassStmt *stmt)
 
ObjectAddress DefineOpFamily (CreateOpFamilyStmt *stmt)
 
Oid AlterOpFamily (AlterOpFamilyStmt *stmt)
 
void IsThereOpClassInNamespace (const char *opcname, Oid opcmethod, Oid opcnamespace)
 
void IsThereOpFamilyInNamespace (const char *opfname, Oid opfmethod, Oid opfnamespace)
 

Function Documentation

◆ addFamilyMember()

static void addFamilyMember ( List **  list,
OpFamilyMember member 
)
static

Definition at line 1391 of file opclasscmds.c.

1392 {
1393  ListCell *l;
1394 
1395  foreach(l, *list)
1396  {
1397  OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
1398 
1399  if (old->number == member->number &&
1400  old->lefttype == member->lefttype &&
1401  old->righttype == member->righttype)
1402  {
1403  if (member->is_func)
1404  ereport(ERROR,
1405  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1406  errmsg("function number %d for (%s,%s) appears more than once",
1407  member->number,
1408  format_type_be(member->lefttype),
1409  format_type_be(member->righttype))));
1410  else
1411  ereport(ERROR,
1412  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1413  errmsg("operator number %d for (%s,%s) appears more than once",
1414  member->number,
1415  format_type_be(member->lefttype),
1416  format_type_be(member->righttype))));
1417  }
1418  }
1419  *list = lappend(*list, member);
1420 }
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
List * lappend(List *list, void *datum)
Definition: list.c:339
#define lfirst(lc)
Definition: pg_list.h:172
Oid lefttype
Definition: amapi.h:85
Oid righttype
Definition: amapi.h:86
int number
Definition: amapi.h:84
bool is_func
Definition: amapi.h:82

References ereport, errcode(), errmsg(), ERROR, format_type_be(), OpFamilyMember::is_func, lappend(), OpFamilyMember::lefttype, lfirst, sort-test::list, OpFamilyMember::number, and OpFamilyMember::righttype.

Referenced by AlterOpFamilyAdd(), AlterOpFamilyDrop(), and DefineOpClass().

◆ AlterOpFamily()

Oid AlterOpFamily ( AlterOpFamilyStmt stmt)

Definition at line 816 of file opclasscmds.c.

817 {
818  Oid amoid, /* our AM's oid */
819  opfamilyoid; /* oid of opfamily */
820  int maxOpNumber, /* amstrategies value */
821  optsProcNumber, /* amoptsprocnum value */
822  maxProcNumber; /* amsupport value */
823  HeapTuple tup;
824  Form_pg_am amform;
825  IndexAmRoutine *amroutine;
826 
827  /* Get necessary info about access method */
828  tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
829  if (!HeapTupleIsValid(tup))
830  ereport(ERROR,
831  (errcode(ERRCODE_UNDEFINED_OBJECT),
832  errmsg("access method \"%s\" does not exist",
833  stmt->amname)));
834 
835  amform = (Form_pg_am) GETSTRUCT(tup);
836  amoid = amform->oid;
837  amroutine = GetIndexAmRoutineByAmId(amoid, false);
838  ReleaseSysCache(tup);
839 
840  maxOpNumber = amroutine->amstrategies;
841  /* if amstrategies is zero, just enforce that op numbers fit in int16 */
842  if (maxOpNumber <= 0)
843  maxOpNumber = SHRT_MAX;
844  maxProcNumber = amroutine->amsupport;
845  optsProcNumber = amroutine->amoptsprocnum;
846 
847  /* XXX Should we make any privilege check against the AM? */
848 
849  /* Look up the opfamily */
850  opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
851 
852  /*
853  * Currently, we require superuser privileges to alter an opfamily.
854  *
855  * XXX re-enable NOT_USED code sections below if you remove this test.
856  */
857  if (!superuser())
858  ereport(ERROR,
859  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
860  errmsg("must be superuser to alter an operator family")));
861 
862  /*
863  * ADD and DROP cases need separate code from here on down.
864  */
865  if (stmt->isDrop)
866  AlterOpFamilyDrop(stmt, amoid, opfamilyoid,
867  maxOpNumber, maxProcNumber, stmt->items);
868  else
869  AlterOpFamilyAdd(stmt, amoid, opfamilyoid,
870  maxOpNumber, maxProcNumber, optsProcNumber,
871  stmt->items);
872 
873  return opfamilyoid;
874 }
IndexAmRoutine * GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
Definition: amapi.c:56
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
#define stmt
Definition: indent_codes.h:59
static void AlterOpFamilyDrop(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, int maxOpNumber, int maxProcNumber, List *items)
Definition: opclasscmds.c:1029
Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok)
Definition: opclasscmds.c:138
static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, int maxOpNumber, int maxProcNumber, int optsProcNumber, List *items)
Definition: opclasscmds.c:880
FormData_pg_am * Form_pg_am
Definition: pg_am.h:48
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
unsigned int Oid
Definition: postgres_ext.h:31
uint16 amoptsprocnum
Definition: amapi.h:221
uint16 amsupport
Definition: amapi.h:219
uint16 amstrategies
Definition: amapi.h:217
bool superuser(void)
Definition: superuser.c:46
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218

References AlterOpFamilyAdd(), AlterOpFamilyDrop(), IndexAmRoutine::amoptsprocnum, IndexAmRoutine::amstrategies, IndexAmRoutine::amsupport, CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, get_opfamily_oid(), GetIndexAmRoutineByAmId(), GETSTRUCT, HeapTupleIsValid, ReleaseSysCache(), SearchSysCache1(), stmt, and superuser().

Referenced by ProcessUtilitySlow().

◆ AlterOpFamilyAdd()

static void AlterOpFamilyAdd ( AlterOpFamilyStmt stmt,
Oid  amoid,
Oid  opfamilyoid,
int  maxOpNumber,
int  maxProcNumber,
int  optsProcNumber,
List items 
)
static

Definition at line 880 of file opclasscmds.c.

883 {
884  IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
885  List *operators; /* OpFamilyMember list for operators */
886  List *procedures; /* OpFamilyMember list for support procs */
887  ListCell *l;
888 
889  operators = NIL;
890  procedures = NIL;
891 
892  /*
893  * Scan the "items" list to obtain additional info.
894  */
895  foreach(l, items)
896  {
898  Oid operOid;
899  Oid funcOid;
900  Oid sortfamilyOid;
901  OpFamilyMember *member;
902 
903  switch (item->itemtype)
904  {
906  if (item->number <= 0 || item->number > maxOpNumber)
907  ereport(ERROR,
908  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
909  errmsg("invalid operator number %d,"
910  " must be between 1 and %d",
911  item->number, maxOpNumber)));
912  if (item->name->objargs != NIL)
913  operOid = LookupOperWithArgs(item->name, false);
914  else
915  {
916  ereport(ERROR,
917  (errcode(ERRCODE_SYNTAX_ERROR),
918  errmsg("operator argument types must be specified in ALTER OPERATOR FAMILY")));
919  operOid = InvalidOid; /* keep compiler quiet */
920  }
921 
922  if (item->order_family)
923  sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
924  item->order_family,
925  false);
926  else
927  sortfamilyOid = InvalidOid;
928 
929 #ifdef NOT_USED
930  /* XXX this is unnecessary given the superuser check above */
931  /* Caller must own operator and its underlying function */
932  if (!object_ownercheck(OperatorRelationId, operOid, GetUserId()))
934  get_opname(operOid));
935  funcOid = get_opcode(operOid);
936  if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
938  get_func_name(funcOid));
939 #endif
940 
941  /* Save the info */
942  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
943  member->is_func = false;
944  member->object = operOid;
945  member->number = item->number;
946  member->sortfamily = sortfamilyOid;
947  /* We can set up dependency fields immediately */
948  /* Historically, ALTER ADD has created soft dependencies */
949  member->ref_is_hard = false;
950  member->ref_is_family = true;
951  member->refobjid = opfamilyoid;
952  assignOperTypes(member, amoid, InvalidOid);
953  addFamilyMember(&operators, member);
954  break;
956  if (item->number <= 0 || item->number > maxProcNumber)
957  ereport(ERROR,
958  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
959  errmsg("invalid function number %d,"
960  " must be between 1 and %d",
961  item->number, maxProcNumber)));
962  funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
963 #ifdef NOT_USED
964  /* XXX this is unnecessary given the superuser check above */
965  /* Caller must own function */
966  if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
968  get_func_name(funcOid));
969 #endif
970 
971  /* Save the info */
972  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
973  member->is_func = true;
974  member->object = funcOid;
975  member->number = item->number;
976  /* We can set up dependency fields immediately */
977  /* Historically, ALTER ADD has created soft dependencies */
978  member->ref_is_hard = false;
979  member->ref_is_family = true;
980  member->refobjid = opfamilyoid;
981 
982  /* allow overriding of the function's actual arg types */
983  if (item->class_args)
985  &member->lefttype, &member->righttype);
986 
987  assignProcTypes(member, amoid, InvalidOid, optsProcNumber);
988  addFamilyMember(&procedures, member);
989  break;
991  ereport(ERROR,
992  (errcode(ERRCODE_SYNTAX_ERROR),
993  errmsg("STORAGE cannot be specified in ALTER OPERATOR FAMILY")));
994  break;
995  default:
996  elog(ERROR, "unrecognized item type: %d", item->itemtype);
997  break;
998  }
999  }
1000 
1001  /*
1002  * Let the index AM editorialize on the dependency choices. It could also
1003  * do further validation on the operators and functions, if it likes.
1004  */
1005  if (amroutine->amadjustmembers)
1006  amroutine->amadjustmembers(opfamilyoid,
1007  InvalidOid, /* no specific opclass */
1008  operators,
1009  procedures);
1010 
1011  /*
1012  * Add tuples to pg_amop and pg_amproc tying in the operators and
1013  * functions. Dependencies on them are inserted, too.
1014  */
1015  storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
1016  operators, true);
1017  storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
1018  procedures, true);
1019 
1020  /* make information available to event triggers */
1021  EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
1022  operators, procedures);
1023 }
@ ACLCHECK_NOT_OWNER
Definition: acl.h:185
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2688
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4130
#define elog(elevel,...)
Definition: elog.h:224
void EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid, List *operators, List *procedures)
char * get_opname(Oid opno)
Definition: lsyscache.c:1288
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1263
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1586
void * palloc0(Size size)
Definition: mcxt.c:1334
Oid GetUserId(void)
Definition: miscinit.c:514
static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
Definition: opclasscmds.c:1136
static void storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, List *procedures, bool isAdd)
Definition: opclasscmds.c:1535
static void storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, List *operators, bool isAdd)
Definition: opclasscmds.c:1428
static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid, int opclassOptsProcNum)
Definition: opclasscmds.c:1202
static void addFamilyMember(List **list, OpFamilyMember *member)
Definition: opclasscmds.c:1391
static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
Definition: opclasscmds.c:1107
Oid LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok)
Definition: parse_func.c:2205
Oid LookupOperWithArgs(ObjectWithArgs *oper, bool noError)
Definition: parse_oper.c:133
#define OPCLASS_ITEM_STORAGETYPE
Definition: parsenodes.h:3107
#define OPCLASS_ITEM_OPERATOR
Definition: parsenodes.h:3105
#define OPCLASS_ITEM_FUNCTION
Definition: parsenodes.h:3106
@ OBJECT_OPERATOR
Definition: parsenodes.h:2204
@ OBJECT_FUNCTION
Definition: parsenodes.h:2198
#define lfirst_node(type, lc)
Definition: pg_list.h:176
#define NIL
Definition: pg_list.h:68
#define InvalidOid
Definition: postgres_ext.h:36
ObjectWithArgs * name
Definition: parsenodes.h:3113
amadjustmembers_function amadjustmembers
Definition: amapi.h:278
Definition: pg_list.h:54
Oid refobjid
Definition: amapi.h:90
bool ref_is_family
Definition: amapi.h:89
Oid object
Definition: amapi.h:83
bool ref_is_hard
Definition: amapi.h:88
Oid sortfamily
Definition: amapi.h:87
static ItemArray items
Definition: test_tidstore.c:49

References aclcheck_error(), ACLCHECK_NOT_OWNER, addFamilyMember(), IndexAmRoutine::amadjustmembers, assignOperTypes(), assignProcTypes(), CreateOpClassItem::class_args, elog, ereport, errcode(), errmsg(), ERROR, EventTriggerCollectAlterOpFam(), get_func_name(), get_opcode(), get_opfamily_oid(), get_opname(), GetIndexAmRoutineByAmId(), GetUserId(), InvalidOid, OpFamilyMember::is_func, items, CreateOpClassItem::itemtype, OpFamilyMember::lefttype, lfirst_node, LookupFuncWithArgs(), LookupOperWithArgs(), CreateOpClassItem::name, NIL, OpFamilyMember::number, CreateOpClassItem::number, ObjectWithArgs::objargs, OpFamilyMember::object, OBJECT_FUNCTION, OBJECT_OPERATOR, object_ownercheck(), OPCLASS_ITEM_FUNCTION, OPCLASS_ITEM_OPERATOR, OPCLASS_ITEM_STORAGETYPE, CreateOpClassItem::order_family, palloc0(), processTypesSpec(), OpFamilyMember::ref_is_family, OpFamilyMember::ref_is_hard, OpFamilyMember::refobjid, OpFamilyMember::righttype, OpFamilyMember::sortfamily, stmt, storeOperators(), and storeProcedures().

Referenced by AlterOpFamily().

◆ AlterOpFamilyDrop()

static void AlterOpFamilyDrop ( AlterOpFamilyStmt stmt,
Oid  amoid,
Oid  opfamilyoid,
int  maxOpNumber,
int  maxProcNumber,
List items 
)
static

Definition at line 1029 of file opclasscmds.c.

1031 {
1032  List *operators; /* OpFamilyMember list for operators */
1033  List *procedures; /* OpFamilyMember list for support procs */
1034  ListCell *l;
1035 
1036  operators = NIL;
1037  procedures = NIL;
1038 
1039  /*
1040  * Scan the "items" list to obtain additional info.
1041  */
1042  foreach(l, items)
1043  {
1045  Oid lefttype,
1046  righttype;
1047  OpFamilyMember *member;
1048 
1049  switch (item->itemtype)
1050  {
1051  case OPCLASS_ITEM_OPERATOR:
1052  if (item->number <= 0 || item->number > maxOpNumber)
1053  ereport(ERROR,
1054  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1055  errmsg("invalid operator number %d,"
1056  " must be between 1 and %d",
1057  item->number, maxOpNumber)));
1058  processTypesSpec(item->class_args, &lefttype, &righttype);
1059  /* Save the info */
1060  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
1061  member->is_func = false;
1062  member->number = item->number;
1063  member->lefttype = lefttype;
1064  member->righttype = righttype;
1065  addFamilyMember(&operators, member);
1066  break;
1067  case OPCLASS_ITEM_FUNCTION:
1068  if (item->number <= 0 || item->number > maxProcNumber)
1069  ereport(ERROR,
1070  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1071  errmsg("invalid function number %d,"
1072  " must be between 1 and %d",
1073  item->number, maxProcNumber)));
1074  processTypesSpec(item->class_args, &lefttype, &righttype);
1075  /* Save the info */
1076  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
1077  member->is_func = true;
1078  member->number = item->number;
1079  member->lefttype = lefttype;
1080  member->righttype = righttype;
1081  addFamilyMember(&procedures, member);
1082  break;
1084  /* grammar prevents this from appearing */
1085  default:
1086  elog(ERROR, "unrecognized item type: %d", item->itemtype);
1087  break;
1088  }
1089  }
1090 
1091  /*
1092  * Remove tuples from pg_amop and pg_amproc.
1093  */
1094  dropOperators(stmt->opfamilyname, amoid, opfamilyoid, operators);
1095  dropProcedures(stmt->opfamilyname, amoid, opfamilyoid, procedures);
1096 
1097  /* make information available to event triggers */
1098  EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
1099  operators, procedures);
1100 }
static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, List *operators)
Definition: opclasscmds.c:1627
static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, List *procedures)
Definition: opclasscmds.c:1667

References addFamilyMember(), CreateOpClassItem::class_args, dropOperators(), dropProcedures(), elog, ereport, errcode(), errmsg(), ERROR, EventTriggerCollectAlterOpFam(), OpFamilyMember::is_func, items, CreateOpClassItem::itemtype, OpFamilyMember::lefttype, lfirst_node, NIL, OpFamilyMember::number, CreateOpClassItem::number, OPCLASS_ITEM_FUNCTION, OPCLASS_ITEM_OPERATOR, OPCLASS_ITEM_STORAGETYPE, palloc0(), processTypesSpec(), OpFamilyMember::righttype, and stmt.

Referenced by AlterOpFamily().

◆ assignOperTypes()

static void assignOperTypes ( OpFamilyMember member,
Oid  amoid,
Oid  typeoid 
)
static

Definition at line 1136 of file opclasscmds.c.

1137 {
1138  Operator optup;
1139  Form_pg_operator opform;
1140 
1141  /* Fetch the operator definition */
1142  optup = SearchSysCache1(OPEROID, ObjectIdGetDatum(member->object));
1143  if (!HeapTupleIsValid(optup))
1144  elog(ERROR, "cache lookup failed for operator %u", member->object);
1145  opform = (Form_pg_operator) GETSTRUCT(optup);
1146 
1147  /*
1148  * Opfamily operators must be binary.
1149  */
1150  if (opform->oprkind != 'b')
1151  ereport(ERROR,
1152  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1153  errmsg("index operators must be binary")));
1154 
1155  if (OidIsValid(member->sortfamily))
1156  {
1157  /*
1158  * Ordering op, check index supports that. (We could perhaps also
1159  * check that the operator returns a type supported by the sortfamily,
1160  * but that seems more trouble than it's worth here. If it does not,
1161  * the operator will never be matchable to any ORDER BY clause, but no
1162  * worse consequences can ensue. Also, trying to check that would
1163  * create an ordering hazard during dump/reload: it's possible that
1164  * the family has been created but not yet populated with the required
1165  * operators.)
1166  */
1167  IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
1168 
1169  if (!amroutine->amcanorderbyop)
1170  ereport(ERROR,
1171  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1172  errmsg("access method \"%s\" does not support ordering operators",
1173  get_am_name(amoid))));
1174  }
1175  else
1176  {
1177  /*
1178  * Search operators must return boolean.
1179  */
1180  if (opform->oprresult != BOOLOID)
1181  ereport(ERROR,
1182  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1183  errmsg("index search operators must return boolean")));
1184  }
1185 
1186  /*
1187  * If lefttype/righttype isn't specified, use the operator's input types
1188  */
1189  if (!OidIsValid(member->lefttype))
1190  member->lefttype = opform->oprleft;
1191  if (!OidIsValid(member->righttype))
1192  member->righttype = opform->oprright;
1193 
1194  ReleaseSysCache(optup);
1195 }
char * get_am_name(Oid amOid)
Definition: amcmds.c:192
#define OidIsValid(objectId)
Definition: c.h:762
FormData_pg_operator * Form_pg_operator
Definition: pg_operator.h:83
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
bool amcanorderbyop
Definition: amapi.h:225

References IndexAmRoutine::amcanorderbyop, elog, ereport, errcode(), errmsg(), ERROR, get_am_name(), GetIndexAmRoutineByAmId(), GETSTRUCT, HeapTupleIsValid, OpFamilyMember::lefttype, OpFamilyMember::object, ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), OpFamilyMember::righttype, SearchSysCache1(), and OpFamilyMember::sortfamily.

Referenced by AlterOpFamilyAdd(), and DefineOpClass().

◆ assignProcTypes()

static void assignProcTypes ( OpFamilyMember member,
Oid  amoid,
Oid  typeoid,
int  opclassOptsProcNum 
)
static

Definition at line 1202 of file opclasscmds.c.

1204 {
1205  HeapTuple proctup;
1206  Form_pg_proc procform;
1207 
1208  /* Fetch the procedure definition */
1209  proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(member->object));
1210  if (!HeapTupleIsValid(proctup))
1211  elog(ERROR, "cache lookup failed for function %u", member->object);
1212  procform = (Form_pg_proc) GETSTRUCT(proctup);
1213 
1214  /* Check the signature of the opclass options parsing function */
1215  if (member->number == opclassOptsProcNum)
1216  {
1217  if (OidIsValid(typeoid))
1218  {
1219  if ((OidIsValid(member->lefttype) && member->lefttype != typeoid) ||
1220  (OidIsValid(member->righttype) && member->righttype != typeoid))
1221  ereport(ERROR,
1222  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1223  errmsg("associated data types for operator class options parsing functions must match opclass input type")));
1224  }
1225  else
1226  {
1227  if (member->lefttype != member->righttype)
1228  ereport(ERROR,
1229  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1230  errmsg("left and right associated data types for operator class options parsing functions must match")));
1231  }
1232 
1233  if (procform->prorettype != VOIDOID ||
1234  procform->pronargs != 1 ||
1235  procform->proargtypes.values[0] != INTERNALOID)
1236  ereport(ERROR,
1237  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1238  errmsg("invalid operator class options parsing function"),
1239  errhint("Valid signature of operator class options parsing function is %s.",
1240  "(internal) RETURNS void")));
1241  }
1242 
1243  /*
1244  * btree comparison procs must be 2-arg procs returning int4. btree
1245  * sortsupport procs must take internal and return void. btree in_range
1246  * procs must be 5-arg procs returning bool. btree equalimage procs must
1247  * take 1 arg and return bool. hash support proc 1 must be a 1-arg proc
1248  * returning int4, while proc 2 must be a 2-arg proc returning int8.
1249  * Otherwise we don't know.
1250  */
1251  else if (amoid == BTREE_AM_OID)
1252  {
1253  if (member->number == BTORDER_PROC)
1254  {
1255  if (procform->pronargs != 2)
1256  ereport(ERROR,
1257  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1258  errmsg("btree comparison functions must have two arguments")));
1259  if (procform->prorettype != INT4OID)
1260  ereport(ERROR,
1261  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1262  errmsg("btree comparison functions must return integer")));
1263 
1264  /*
1265  * If lefttype/righttype isn't specified, use the proc's input
1266  * types
1267  */
1268  if (!OidIsValid(member->lefttype))
1269  member->lefttype = procform->proargtypes.values[0];
1270  if (!OidIsValid(member->righttype))
1271  member->righttype = procform->proargtypes.values[1];
1272  }
1273  else if (member->number == BTSORTSUPPORT_PROC)
1274  {
1275  if (procform->pronargs != 1 ||
1276  procform->proargtypes.values[0] != INTERNALOID)
1277  ereport(ERROR,
1278  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1279  errmsg("btree sort support functions must accept type \"internal\"")));
1280  if (procform->prorettype != VOIDOID)
1281  ereport(ERROR,
1282  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1283  errmsg("btree sort support functions must return void")));
1284 
1285  /*
1286  * Can't infer lefttype/righttype from proc, so use default rule
1287  */
1288  }
1289  else if (member->number == BTINRANGE_PROC)
1290  {
1291  if (procform->pronargs != 5)
1292  ereport(ERROR,
1293  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1294  errmsg("btree in_range functions must have five arguments")));
1295  if (procform->prorettype != BOOLOID)
1296  ereport(ERROR,
1297  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1298  errmsg("btree in_range functions must return boolean")));
1299 
1300  /*
1301  * If lefttype/righttype isn't specified, use the proc's input
1302  * types (we look at the test-value and offset arguments)
1303  */
1304  if (!OidIsValid(member->lefttype))
1305  member->lefttype = procform->proargtypes.values[0];
1306  if (!OidIsValid(member->righttype))
1307  member->righttype = procform->proargtypes.values[2];
1308  }
1309  else if (member->number == BTEQUALIMAGE_PROC)
1310  {
1311  if (procform->pronargs != 1)
1312  ereport(ERROR,
1313  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1314  errmsg("btree equal image functions must have one argument")));
1315  if (procform->prorettype != BOOLOID)
1316  ereport(ERROR,
1317  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1318  errmsg("btree equal image functions must return boolean")));
1319 
1320  /*
1321  * pg_amproc functions are indexed by (lefttype, righttype), but
1322  * an equalimage function can only be called at CREATE INDEX time.
1323  * The same opclass opcintype OID is always used for lefttype and
1324  * righttype. Providing a cross-type routine isn't sensible.
1325  * Reject cross-type ALTER OPERATOR FAMILY ... ADD FUNCTION 4
1326  * statements here.
1327  */
1328  if (member->lefttype != member->righttype)
1329  ereport(ERROR,
1330  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1331  errmsg("btree equal image functions must not be cross-type")));
1332  }
1333  }
1334  else if (amoid == HASH_AM_OID)
1335  {
1336  if (member->number == HASHSTANDARD_PROC)
1337  {
1338  if (procform->pronargs != 1)
1339  ereport(ERROR,
1340  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1341  errmsg("hash function 1 must have one argument")));
1342  if (procform->prorettype != INT4OID)
1343  ereport(ERROR,
1344  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1345  errmsg("hash function 1 must return integer")));
1346  }
1347  else if (member->number == HASHEXTENDED_PROC)
1348  {
1349  if (procform->pronargs != 2)
1350  ereport(ERROR,
1351  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1352  errmsg("hash function 2 must have two arguments")));
1353  if (procform->prorettype != INT8OID)
1354  ereport(ERROR,
1355  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1356  errmsg("hash function 2 must return bigint")));
1357  }
1358 
1359  /*
1360  * If lefttype/righttype isn't specified, use the proc's input type
1361  */
1362  if (!OidIsValid(member->lefttype))
1363  member->lefttype = procform->proargtypes.values[0];
1364  if (!OidIsValid(member->righttype))
1365  member->righttype = procform->proargtypes.values[0];
1366  }
1367 
1368  /*
1369  * The default in CREATE OPERATOR CLASS is to use the class' opcintype as
1370  * lefttype and righttype. In CREATE or ALTER OPERATOR FAMILY, opcintype
1371  * isn't available, so make the user specify the types.
1372  */
1373  if (!OidIsValid(member->lefttype))
1374  member->lefttype = typeoid;
1375  if (!OidIsValid(member->righttype))
1376  member->righttype = typeoid;
1377 
1378  if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
1379  ereport(ERROR,
1380  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1381  errmsg("associated data types must be specified for index support function")));
1382 
1383  ReleaseSysCache(proctup);
1384 }
int errhint(const char *fmt,...)
Definition: elog.c:1319
#define HASHSTANDARD_PROC
Definition: hash.h:355
#define HASHEXTENDED_PROC
Definition: hash.h:356
#define BTEQUALIMAGE_PROC
Definition: nbtree.h:710
#define BTORDER_PROC
Definition: nbtree.h:707
#define BTSORTSUPPORT_PROC
Definition: nbtree.h:708
#define BTINRANGE_PROC
Definition: nbtree.h:709
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:136

References BTEQUALIMAGE_PROC, BTINRANGE_PROC, BTORDER_PROC, BTSORTSUPPORT_PROC, elog, ereport, errcode(), errhint(), errmsg(), ERROR, GETSTRUCT, HASHEXTENDED_PROC, HASHSTANDARD_PROC, HeapTupleIsValid, OpFamilyMember::lefttype, OpFamilyMember::number, OpFamilyMember::object, ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), OpFamilyMember::righttype, and SearchSysCache1().

Referenced by AlterOpFamilyAdd(), and DefineOpClass().

◆ CreateOpFamily()

static ObjectAddress CreateOpFamily ( CreateOpFamilyStmt stmt,
const char *  opfname,
Oid  namespaceoid,
Oid  amoid 
)
static

Definition at line 242 of file opclasscmds.c.

244 {
245  Oid opfamilyoid;
246  Relation rel;
247  HeapTuple tup;
248  Datum values[Natts_pg_opfamily];
249  bool nulls[Natts_pg_opfamily];
250  NameData opfName;
251  ObjectAddress myself,
252  referenced;
253 
254  rel = table_open(OperatorFamilyRelationId, RowExclusiveLock);
255 
256  /*
257  * Make sure there is no existing opfamily of this name (this is just to
258  * give a more friendly error message than "duplicate key").
259  */
260  if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
261  ObjectIdGetDatum(amoid),
262  CStringGetDatum(opfname),
263  ObjectIdGetDatum(namespaceoid)))
264  ereport(ERROR,
266  errmsg("operator family \"%s\" for access method \"%s\" already exists",
267  opfname, stmt->amname)));
268 
269  /*
270  * Okay, let's create the pg_opfamily entry.
271  */
272  memset(values, 0, sizeof(values));
273  memset(nulls, false, sizeof(nulls));
274 
275  opfamilyoid = GetNewOidWithIndex(rel, OpfamilyOidIndexId,
276  Anum_pg_opfamily_oid);
277  values[Anum_pg_opfamily_oid - 1] = ObjectIdGetDatum(opfamilyoid);
278  values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
279  namestrcpy(&opfName, opfname);
280  values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
281  values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
282  values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
283 
284  tup = heap_form_tuple(rel->rd_att, values, nulls);
285 
286  CatalogTupleInsert(rel, tup);
287 
288  heap_freetuple(tup);
289 
290  /*
291  * Create dependencies for the opfamily proper.
292  */
293  myself.classId = OperatorFamilyRelationId;
294  myself.objectId = opfamilyoid;
295  myself.objectSubId = 0;
296 
297  /* dependency on access method */
298  referenced.classId = AccessMethodRelationId;
299  referenced.objectId = amoid;
300  referenced.objectSubId = 0;
301  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
302 
303  /* dependency on namespace */
304  referenced.classId = NamespaceRelationId;
305  referenced.objectId = namespaceoid;
306  referenced.objectSubId = 0;
307  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
308 
309  /* dependency on owner */
310  recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
311 
312  /* dependency on extension */
313  recordDependencyOnCurrentExtension(&myself, false);
314 
315  /* Report the new operator family to possibly interested event triggers */
317  (Node *) stmt);
318 
319  /* Post creation hook for new operator family */
320  InvokeObjectPostCreateHook(OperatorFamilyRelationId, opfamilyoid, 0);
321 
323 
324  return myself;
325 }
static Datum values[MAXATTR]
Definition: bootstrap.c:152
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:391
@ DEPENDENCY_AUTO
Definition: dependency.h:34
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
void EventTriggerCollectSimpleCommand(ObjectAddress address, ObjectAddress secondaryObject, Node *parsetree)
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:233
#define RowExclusiveLock
Definition: lockdefs.h:38
void namestrcpy(Name name, const char *str)
Definition: name.c:233
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
const ObjectAddress InvalidObjectAddress
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
void recordDependencyOnCurrentExtension(const ObjectAddress *object, bool isReplace)
Definition: pg_depend.c:192
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:160
uintptr_t Datum
Definition: postgres.h:64
static Datum NameGetDatum(const NameData *X)
Definition: postgres.h:373
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
Definition: nodes.h:129
TupleDesc rd_att
Definition: rel.h:112
Definition: c.h:728
#define SearchSysCacheExists3(cacheId, key1, key2, key3)
Definition: syscache.h:99
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40

References CatalogTupleInsert(), ObjectAddress::classId, CStringGetDatum(), DEPENDENCY_AUTO, DEPENDENCY_NORMAL, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, EventTriggerCollectSimpleCommand(), GetNewOidWithIndex(), GetUserId(), heap_form_tuple(), heap_freetuple(), InvalidObjectAddress, InvokeObjectPostCreateHook, NameGetDatum(), namestrcpy(), ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, RelationData::rd_att, recordDependencyOn(), recordDependencyOnCurrentExtension(), recordDependencyOnOwner(), RowExclusiveLock, SearchSysCacheExists3, stmt, table_close(), table_open(), and values.

Referenced by DefineOpClass(), and DefineOpFamily().

◆ DefineOpClass()

ObjectAddress DefineOpClass ( CreateOpClassStmt stmt)

Definition at line 332 of file opclasscmds.c.

333 {
334  char *opcname; /* name of opclass we're creating */
335  Oid amoid, /* our AM's oid */
336  typeoid, /* indexable datatype oid */
337  storageoid, /* storage datatype oid, if any */
338  namespaceoid, /* namespace to create opclass in */
339  opfamilyoid, /* oid of containing opfamily */
340  opclassoid; /* oid of opclass we create */
341  int maxOpNumber, /* amstrategies value */
342  optsProcNumber, /* amoptsprocnum value */
343  maxProcNumber; /* amsupport value */
344  bool amstorage; /* amstorage flag */
345  List *operators; /* OpFamilyMember list for operators */
346  List *procedures; /* OpFamilyMember list for support procs */
347  ListCell *l;
348  Relation rel;
349  HeapTuple tup;
350  Form_pg_am amform;
351  IndexAmRoutine *amroutine;
352  Datum values[Natts_pg_opclass];
353  bool nulls[Natts_pg_opclass];
354  AclResult aclresult;
355  NameData opcName;
356  ObjectAddress myself,
357  referenced;
358 
359  /* Convert list of names to a name and namespace */
360  namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,
361  &opcname);
362 
363  /* Check we have creation rights in target namespace */
364  aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
365  if (aclresult != ACLCHECK_OK)
366  aclcheck_error(aclresult, OBJECT_SCHEMA,
367  get_namespace_name(namespaceoid));
368 
369  /* Get necessary info about access method */
370  tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
371  if (!HeapTupleIsValid(tup))
372  ereport(ERROR,
373  (errcode(ERRCODE_UNDEFINED_OBJECT),
374  errmsg("access method \"%s\" does not exist",
375  stmt->amname)));
376 
377  amform = (Form_pg_am) GETSTRUCT(tup);
378  amoid = amform->oid;
379  amroutine = GetIndexAmRoutineByAmId(amoid, false);
380  ReleaseSysCache(tup);
381 
382  maxOpNumber = amroutine->amstrategies;
383  /* if amstrategies is zero, just enforce that op numbers fit in int16 */
384  if (maxOpNumber <= 0)
385  maxOpNumber = SHRT_MAX;
386  maxProcNumber = amroutine->amsupport;
387  optsProcNumber = amroutine->amoptsprocnum;
388  amstorage = amroutine->amstorage;
389 
390  /* XXX Should we make any privilege check against the AM? */
391 
392  /*
393  * The question of appropriate permissions for CREATE OPERATOR CLASS is
394  * interesting. Creating an opclass is tantamount to granting public
395  * execute access on the functions involved, since the index machinery
396  * generally does not check access permission before using the functions.
397  * A minimum expectation therefore is that the caller have execute
398  * privilege with grant option. Since we don't have a way to make the
399  * opclass go away if the grant option is revoked, we choose instead to
400  * require ownership of the functions. It's also not entirely clear what
401  * permissions should be required on the datatype, but ownership seems
402  * like a safe choice.
403  *
404  * Currently, we require superuser privileges to create an opclass. This
405  * seems necessary because we have no way to validate that the offered set
406  * of operators and functions are consistent with the AM's expectations.
407  * It would be nice to provide such a check someday, if it can be done
408  * without solving the halting problem :-(
409  *
410  * XXX re-enable NOT_USED code sections below if you remove this test.
411  */
412  if (!superuser())
413  ereport(ERROR,
414  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
415  errmsg("must be superuser to create an operator class")));
416 
417  /* Look up the datatype */
418  typeoid = typenameTypeId(NULL, stmt->datatype);
419 
420 #ifdef NOT_USED
421  /* XXX this is unnecessary given the superuser check above */
422  /* Check we have ownership of the datatype */
423  if (!object_ownercheck(TypeRelationId, typeoid, GetUserId()))
425 #endif
426 
427  /*
428  * Look up the containing operator family, or create one if FAMILY option
429  * was omitted and there's not a match already.
430  */
431  if (stmt->opfamilyname)
432  {
433  opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
434  }
435  else
436  {
437  /* Lookup existing family of same name and namespace */
438  tup = SearchSysCache3(OPFAMILYAMNAMENSP,
439  ObjectIdGetDatum(amoid),
440  PointerGetDatum(opcname),
441  ObjectIdGetDatum(namespaceoid));
442  if (HeapTupleIsValid(tup))
443  {
444  opfamilyoid = ((Form_pg_opfamily) GETSTRUCT(tup))->oid;
445 
446  /*
447  * XXX given the superuser check above, there's no need for an
448  * ownership check here
449  */
450  ReleaseSysCache(tup);
451  }
452  else
453  {
454  CreateOpFamilyStmt *opfstmt;
455  ObjectAddress tmpAddr;
456 
457  opfstmt = makeNode(CreateOpFamilyStmt);
458  opfstmt->opfamilyname = stmt->opclassname;
459  opfstmt->amname = stmt->amname;
460 
461  /*
462  * Create it ... again no need for more permissions ...
463  */
464  tmpAddr = CreateOpFamily(opfstmt, opcname, namespaceoid, amoid);
465  opfamilyoid = tmpAddr.objectId;
466  }
467  }
468 
469  operators = NIL;
470  procedures = NIL;
471 
472  /* Storage datatype is optional */
473  storageoid = InvalidOid;
474 
475  /*
476  * Scan the "items" list to obtain additional info.
477  */
478  foreach(l, stmt->items)
479  {
481  Oid operOid;
482  Oid funcOid;
483  Oid sortfamilyOid;
484  OpFamilyMember *member;
485 
486  switch (item->itemtype)
487  {
489  if (item->number <= 0 || item->number > maxOpNumber)
490  ereport(ERROR,
491  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
492  errmsg("invalid operator number %d,"
493  " must be between 1 and %d",
494  item->number, maxOpNumber)));
495  if (item->name->objargs != NIL)
496  operOid = LookupOperWithArgs(item->name, false);
497  else
498  {
499  /* Default to binary op on input datatype */
500  operOid = LookupOperName(NULL, item->name->objname,
501  typeoid, typeoid,
502  false, -1);
503  }
504 
505  if (item->order_family)
506  sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
507  item->order_family,
508  false);
509  else
510  sortfamilyOid = InvalidOid;
511 
512 #ifdef NOT_USED
513  /* XXX this is unnecessary given the superuser check above */
514  /* Caller must own operator and its underlying function */
515  if (!object_ownercheck(OperatorRelationId, operOid, GetUserId()))
517  get_opname(operOid));
518  funcOid = get_opcode(operOid);
519  if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
521  get_func_name(funcOid));
522 #endif
523 
524  /* Save the info */
525  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
526  member->is_func = false;
527  member->object = operOid;
528  member->number = item->number;
529  member->sortfamily = sortfamilyOid;
530  assignOperTypes(member, amoid, typeoid);
531  addFamilyMember(&operators, member);
532  break;
534  if (item->number <= 0 || item->number > maxProcNumber)
535  ereport(ERROR,
536  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
537  errmsg("invalid function number %d,"
538  " must be between 1 and %d",
539  item->number, maxProcNumber)));
540  funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
541 #ifdef NOT_USED
542  /* XXX this is unnecessary given the superuser check above */
543  /* Caller must own function */
544  if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
546  get_func_name(funcOid));
547 #endif
548  /* Save the info */
549  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
550  member->is_func = true;
551  member->object = funcOid;
552  member->number = item->number;
553 
554  /* allow overriding of the function's actual arg types */
555  if (item->class_args)
557  &member->lefttype, &member->righttype);
558 
559  assignProcTypes(member, amoid, typeoid, optsProcNumber);
560  addFamilyMember(&procedures, member);
561  break;
563  if (OidIsValid(storageoid))
564  ereport(ERROR,
565  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
566  errmsg("storage type specified more than once")));
567  storageoid = typenameTypeId(NULL, item->storedtype);
568 
569 #ifdef NOT_USED
570  /* XXX this is unnecessary given the superuser check above */
571  /* Check we have ownership of the datatype */
572  if (!object_ownercheck(TypeRelationId, storageoid, GetUserId()))
574 #endif
575  break;
576  default:
577  elog(ERROR, "unrecognized item type: %d", item->itemtype);
578  break;
579  }
580  }
581 
582  /*
583  * If storagetype is specified, make sure it's legal.
584  */
585  if (OidIsValid(storageoid))
586  {
587  /* Just drop the spec if same as column datatype */
588  if (storageoid == typeoid)
589  storageoid = InvalidOid;
590  else if (!amstorage)
591  ereport(ERROR,
592  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
593  errmsg("storage type cannot be different from data type for access method \"%s\"",
594  stmt->amname)));
595  }
596 
597  rel = table_open(OperatorClassRelationId, RowExclusiveLock);
598 
599  /*
600  * Make sure there is no existing opclass of this name (this is just to
601  * give a more friendly error message than "duplicate key").
602  */
603  if (SearchSysCacheExists3(CLAAMNAMENSP,
604  ObjectIdGetDatum(amoid),
605  CStringGetDatum(opcname),
606  ObjectIdGetDatum(namespaceoid)))
607  ereport(ERROR,
609  errmsg("operator class \"%s\" for access method \"%s\" already exists",
610  opcname, stmt->amname)));
611 
612  /*
613  * If we are creating a default opclass, check there isn't one already.
614  * (Note we do not restrict this test to visible opclasses; this ensures
615  * that typcache.c can find unique solutions to its questions.)
616  */
617  if (stmt->isDefault)
618  {
619  ScanKeyData skey[1];
620  SysScanDesc scan;
621 
622  ScanKeyInit(&skey[0],
623  Anum_pg_opclass_opcmethod,
624  BTEqualStrategyNumber, F_OIDEQ,
625  ObjectIdGetDatum(amoid));
626 
627  scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
628  NULL, 1, skey);
629 
630  while (HeapTupleIsValid(tup = systable_getnext(scan)))
631  {
632  Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
633 
634  if (opclass->opcintype == typeoid && opclass->opcdefault)
635  ereport(ERROR,
637  errmsg("could not make operator class \"%s\" be default for type %s",
638  opcname,
639  TypeNameToString(stmt->datatype)),
640  errdetail("Operator class \"%s\" already is the default.",
641  NameStr(opclass->opcname))));
642  }
643 
644  systable_endscan(scan);
645  }
646 
647  /*
648  * Okay, let's create the pg_opclass entry.
649  */
650  memset(values, 0, sizeof(values));
651  memset(nulls, false, sizeof(nulls));
652 
653  opclassoid = GetNewOidWithIndex(rel, OpclassOidIndexId,
654  Anum_pg_opclass_oid);
655  values[Anum_pg_opclass_oid - 1] = ObjectIdGetDatum(opclassoid);
656  values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
657  namestrcpy(&opcName, opcname);
658  values[Anum_pg_opclass_opcname - 1] = NameGetDatum(&opcName);
659  values[Anum_pg_opclass_opcnamespace - 1] = ObjectIdGetDatum(namespaceoid);
660  values[Anum_pg_opclass_opcowner - 1] = ObjectIdGetDatum(GetUserId());
661  values[Anum_pg_opclass_opcfamily - 1] = ObjectIdGetDatum(opfamilyoid);
662  values[Anum_pg_opclass_opcintype - 1] = ObjectIdGetDatum(typeoid);
663  values[Anum_pg_opclass_opcdefault - 1] = BoolGetDatum(stmt->isDefault);
664  values[Anum_pg_opclass_opckeytype - 1] = ObjectIdGetDatum(storageoid);
665 
666  tup = heap_form_tuple(rel->rd_att, values, nulls);
667 
668  CatalogTupleInsert(rel, tup);
669 
670  heap_freetuple(tup);
671 
672  /*
673  * Now that we have the opclass OID, set up default dependency info for
674  * the pg_amop and pg_amproc entries. Historically, CREATE OPERATOR CLASS
675  * has created hard dependencies on the opclass, so that's what we use.
676  */
677  foreach(l, operators)
678  {
679  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
680 
681  op->ref_is_hard = true;
682  op->ref_is_family = false;
683  op->refobjid = opclassoid;
684  }
685  foreach(l, procedures)
686  {
687  OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
688 
689  proc->ref_is_hard = true;
690  proc->ref_is_family = false;
691  proc->refobjid = opclassoid;
692  }
693 
694  /*
695  * Let the index AM editorialize on the dependency choices. It could also
696  * do further validation on the operators and functions, if it likes.
697  */
698  if (amroutine->amadjustmembers)
699  amroutine->amadjustmembers(opfamilyoid,
700  opclassoid,
701  operators,
702  procedures);
703 
704  /*
705  * Now add tuples to pg_amop and pg_amproc tying in the operators and
706  * functions. Dependencies on them are inserted, too.
707  */
708  storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
709  operators, false);
710  storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
711  procedures, false);
712 
713  /* let event triggers know what happened */
714  EventTriggerCollectCreateOpClass(stmt, opclassoid, operators, procedures);
715 
716  /*
717  * Create dependencies for the opclass proper. Note: we do not need a
718  * dependency link to the AM, because that exists through the opfamily.
719  */
720  myself.classId = OperatorClassRelationId;
721  myself.objectId = opclassoid;
722  myself.objectSubId = 0;
723 
724  /* dependency on namespace */
725  referenced.classId = NamespaceRelationId;
726  referenced.objectId = namespaceoid;
727  referenced.objectSubId = 0;
728  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
729 
730  /* dependency on opfamily */
731  referenced.classId = OperatorFamilyRelationId;
732  referenced.objectId = opfamilyoid;
733  referenced.objectSubId = 0;
734  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
735 
736  /* dependency on indexed datatype */
737  referenced.classId = TypeRelationId;
738  referenced.objectId = typeoid;
739  referenced.objectSubId = 0;
740  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
741 
742  /* dependency on storage datatype */
743  if (OidIsValid(storageoid))
744  {
745  referenced.classId = TypeRelationId;
746  referenced.objectId = storageoid;
747  referenced.objectSubId = 0;
748  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
749  }
750 
751  /* dependency on owner */
752  recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
753 
754  /* dependency on extension */
755  recordDependencyOnCurrentExtension(&myself, false);
756 
757  /* Post creation hook for new operator class */
758  InvokeObjectPostCreateHook(OperatorClassRelationId, opclassoid, 0);
759 
761 
762  return myself;
763 }
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3876
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:3007
#define NameStr(name)
Definition: c.h:733
int errdetail(const char *fmt,...)
Definition: elog.c:1205
void EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid, List *operators, List *procedures)
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:596
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:503
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:384
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3344
Oid QualifiedNameGetCreationNamespace(const List *names, char **objname_p)
Definition: namespace.c:3472
#define makeNode(_type_)
Definition: nodes.h:155
static ObjectAddress CreateOpFamily(CreateOpFamilyStmt *stmt, const char *opfname, Oid namespaceoid, Oid amoid)
Definition: opclasscmds.c:242
Oid LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright, bool noError, int location)
Definition: parse_oper.c:99
char * TypeNameToString(const TypeName *typeName)
Definition: parse_type.c:478
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:291
@ OBJECT_SCHEMA
Definition: parsenodes.h:2215
#define ACL_CREATE
Definition: parsenodes.h:85
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:51
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define BTEqualStrategyNumber
Definition: stratnum.h:31
TypeName * storedtype
Definition: parsenodes.h:3119
bool amstorage
Definition: amapi.h:239
HeapTuple SearchSysCache3(int cacheId, Datum key1, Datum key2, Datum key3)
Definition: syscache.c:240

References ACL_CREATE, aclcheck_error(), aclcheck_error_type(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, addFamilyMember(), IndexAmRoutine::amadjustmembers, CreateOpFamilyStmt::amname, IndexAmRoutine::amoptsprocnum, IndexAmRoutine::amstorage, IndexAmRoutine::amstrategies, IndexAmRoutine::amsupport, assignOperTypes(), assignProcTypes(), BoolGetDatum(), BTEqualStrategyNumber, CatalogTupleInsert(), CreateOpClassItem::class_args, ObjectAddress::classId, CreateOpFamily(), CStringGetDatum(), DEPENDENCY_AUTO, DEPENDENCY_NORMAL, elog, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail(), errmsg(), ERROR, EventTriggerCollectCreateOpClass(), get_func_name(), get_namespace_name(), get_opcode(), get_opfamily_oid(), get_opname(), GetIndexAmRoutineByAmId(), GetNewOidWithIndex(), GETSTRUCT, GetUserId(), heap_form_tuple(), heap_freetuple(), HeapTupleIsValid, InvalidOid, InvokeObjectPostCreateHook, OpFamilyMember::is_func, CreateOpClassItem::itemtype, OpFamilyMember::lefttype, lfirst, lfirst_node, LookupFuncWithArgs(), LookupOperName(), LookupOperWithArgs(), makeNode, CreateOpClassItem::name, NameGetDatum(), NameStr, namestrcpy(), NIL, OpFamilyMember::number, CreateOpClassItem::number, ObjectWithArgs::objargs, OpFamilyMember::object, object_aclcheck(), OBJECT_FUNCTION, OBJECT_OPERATOR, object_ownercheck(), OBJECT_SCHEMA, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, ObjectWithArgs::objname, OidIsValid, OPCLASS_ITEM_FUNCTION, OPCLASS_ITEM_OPERATOR, OPCLASS_ITEM_STORAGETYPE, CreateOpFamilyStmt::opfamilyname, CreateOpClassItem::order_family, palloc0(), PointerGetDatum(), processTypesSpec(), QualifiedNameGetCreationNamespace(), RelationData::rd_att, recordDependencyOn(), recordDependencyOnCurrentExtension(), recordDependencyOnOwner(), OpFamilyMember::ref_is_family, OpFamilyMember::ref_is_hard, OpFamilyMember::refobjid, ReleaseSysCache(), OpFamilyMember::righttype, RowExclusiveLock, ScanKeyInit(), SearchSysCache1(), SearchSysCache3(), SearchSysCacheExists3, OpFamilyMember::sortfamily, stmt, CreateOpClassItem::storedtype, storeOperators(), storeProcedures(), superuser(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TypeNameToString(), typenameTypeId(), and values.

Referenced by ProcessUtilitySlow().

◆ DefineOpFamily()

ObjectAddress DefineOpFamily ( CreateOpFamilyStmt stmt)

Definition at line 771 of file opclasscmds.c.

772 {
773  char *opfname; /* name of opfamily we're creating */
774  Oid amoid, /* our AM's oid */
775  namespaceoid; /* namespace to create opfamily in */
776  AclResult aclresult;
777 
778  /* Convert list of names to a name and namespace */
779  namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
780  &opfname);
781 
782  /* Check we have creation rights in target namespace */
783  aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
784  if (aclresult != ACLCHECK_OK)
785  aclcheck_error(aclresult, OBJECT_SCHEMA,
786  get_namespace_name(namespaceoid));
787 
788  /* Get access method OID, throwing an error if it doesn't exist. */
789  amoid = get_index_am_oid(stmt->amname, false);
790 
791  /* XXX Should we make any privilege check against the AM? */
792 
793  /*
794  * Currently, we require superuser privileges to create an opfamily. See
795  * comments in DefineOpClass.
796  */
797  if (!superuser())
798  ereport(ERROR,
799  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
800  errmsg("must be superuser to create an operator family")));
801 
802  /* Insert pg_opfamily catalog entry */
803  return CreateOpFamily(stmt, opfname, namespaceoid, amoid);
804 }
Oid get_index_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:163

References ACL_CREATE, aclcheck_error(), ACLCHECK_OK, CreateOpFamily(), ereport, errcode(), errmsg(), ERROR, get_index_am_oid(), get_namespace_name(), GetUserId(), object_aclcheck(), OBJECT_SCHEMA, QualifiedNameGetCreationNamespace(), stmt, and superuser().

Referenced by ProcessUtilitySlow().

◆ dropOperators()

static void dropOperators ( List opfamilyname,
Oid  amoid,
Oid  opfamilyoid,
List operators 
)
static

Definition at line 1627 of file opclasscmds.c.

1629 {
1630  ListCell *l;
1631 
1632  foreach(l, operators)
1633  {
1634  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1635  Oid amopid;
1636  ObjectAddress object;
1637 
1638  amopid = GetSysCacheOid4(AMOPSTRATEGY, Anum_pg_amop_oid,
1639  ObjectIdGetDatum(opfamilyoid),
1642  Int16GetDatum(op->number));
1643  if (!OidIsValid(amopid))
1644  ereport(ERROR,
1645  (errcode(ERRCODE_UNDEFINED_OBJECT),
1646  errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"",
1647  op->number,
1648  format_type_be(op->lefttype),
1650  NameListToString(opfamilyname))));
1651 
1652  object.classId = AccessMethodOperatorRelationId;
1653  object.objectId = amopid;
1654  object.objectSubId = 0;
1655 
1656  performDeletion(&object, DROP_RESTRICT, 0);
1657  }
1658 }
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:273
char * NameListToString(const List *names)
Definition: namespace.c:3579
@ DROP_RESTRICT
Definition: parsenodes.h:2252
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:172
#define GetSysCacheOid4(cacheId, oidcol, key1, key2, key3, key4)
Definition: syscache.h:110

References DROP_RESTRICT, ereport, errcode(), errmsg(), ERROR, format_type_be(), GetSysCacheOid4, Int16GetDatum(), OpFamilyMember::lefttype, lfirst, NameListToString(), OpFamilyMember::number, ObjectIdGetDatum(), OidIsValid, performDeletion(), and OpFamilyMember::righttype.

Referenced by AlterOpFamilyDrop().

◆ dropProcedures()

static void dropProcedures ( List opfamilyname,
Oid  amoid,
Oid  opfamilyoid,
List procedures 
)
static

Definition at line 1667 of file opclasscmds.c.

1669 {
1670  ListCell *l;
1671 
1672  foreach(l, procedures)
1673  {
1674  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1675  Oid amprocid;
1676  ObjectAddress object;
1677 
1678  amprocid = GetSysCacheOid4(AMPROCNUM, Anum_pg_amproc_oid,
1679  ObjectIdGetDatum(opfamilyoid),
1682  Int16GetDatum(op->number));
1683  if (!OidIsValid(amprocid))
1684  ereport(ERROR,
1685  (errcode(ERRCODE_UNDEFINED_OBJECT),
1686  errmsg("function %d(%s,%s) does not exist in operator family \"%s\"",
1687  op->number,
1688  format_type_be(op->lefttype),
1690  NameListToString(opfamilyname))));
1691 
1692  object.classId = AccessMethodProcedureRelationId;
1693  object.objectId = amprocid;
1694  object.objectSubId = 0;
1695 
1696  performDeletion(&object, DROP_RESTRICT, 0);
1697  }
1698 }

References DROP_RESTRICT, ereport, errcode(), errmsg(), ERROR, format_type_be(), GetSysCacheOid4, Int16GetDatum(), OpFamilyMember::lefttype, lfirst, NameListToString(), OpFamilyMember::number, ObjectIdGetDatum(), OidIsValid, performDeletion(), and OpFamilyMember::righttype.

Referenced by AlterOpFamilyDrop().

◆ get_opclass_oid()

Oid get_opclass_oid ( Oid  amID,
List opclassname,
bool  missing_ok 
)

Definition at line 219 of file opclasscmds.c.

220 {
221  HeapTuple htup;
222  Form_pg_opclass opcform;
223  Oid opcID;
224 
225  htup = OpClassCacheLookup(amID, opclassname, missing_ok);
226  if (!HeapTupleIsValid(htup))
227  return InvalidOid;
228  opcform = (Form_pg_opclass) GETSTRUCT(htup);
229  opcID = opcform->oid;
230  ReleaseSysCache(htup);
231 
232  return opcID;
233 }
static HeapTuple OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok)
Definition: opclasscmds.c:161

References GETSTRUCT, HeapTupleIsValid, InvalidOid, OpClassCacheLookup(), and ReleaseSysCache().

Referenced by findRangeSubOpclass(), get_object_address_opcf(), and resolve_unique_index_expr().

◆ get_opfamily_oid()

Oid get_opfamily_oid ( Oid  amID,
List opfamilyname,
bool  missing_ok 
)

Definition at line 138 of file opclasscmds.c.

139 {
140  HeapTuple htup;
141  Form_pg_opfamily opfamform;
142  Oid opfID;
143 
144  htup = OpFamilyCacheLookup(amID, opfamilyname, missing_ok);
145  if (!HeapTupleIsValid(htup))
146  return InvalidOid;
147  opfamform = (Form_pg_opfamily) GETSTRUCT(htup);
148  opfID = opfamform->oid;
149  ReleaseSysCache(htup);
150 
151  return opfID;
152 }
static HeapTuple OpFamilyCacheLookup(Oid amID, List *opfamilyname, bool missing_ok)
Definition: opclasscmds.c:80

References GETSTRUCT, HeapTupleIsValid, InvalidOid, OpFamilyCacheLookup(), and ReleaseSysCache().

Referenced by AlterOpFamily(), AlterOpFamilyAdd(), DefineOpClass(), and get_object_address_opcf().

◆ IsThereOpClassInNamespace()

void IsThereOpClassInNamespace ( const char *  opcname,
Oid  opcmethod,
Oid  opcnamespace 
)

Definition at line 1707 of file opclasscmds.c.

1709 {
1710  /* make sure the new name doesn't exist */
1711  if (SearchSysCacheExists3(CLAAMNAMENSP,
1712  ObjectIdGetDatum(opcmethod),
1713  CStringGetDatum(opcname),
1714  ObjectIdGetDatum(opcnamespace)))
1715  ereport(ERROR,
1717  errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1718  opcname,
1719  get_am_name(opcmethod),
1720  get_namespace_name(opcnamespace))));
1721 }

References CStringGetDatum(), ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, get_am_name(), get_namespace_name(), ObjectIdGetDatum(), and SearchSysCacheExists3.

Referenced by AlterObjectNamespace_internal(), and AlterObjectRename_internal().

◆ IsThereOpFamilyInNamespace()

void IsThereOpFamilyInNamespace ( const char *  opfname,
Oid  opfmethod,
Oid  opfnamespace 
)

Definition at line 1730 of file opclasscmds.c.

1732 {
1733  /* make sure the new name doesn't exist */
1734  if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
1735  ObjectIdGetDatum(opfmethod),
1736  CStringGetDatum(opfname),
1737  ObjectIdGetDatum(opfnamespace)))
1738  ereport(ERROR,
1740  errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1741  opfname,
1742  get_am_name(opfmethod),
1743  get_namespace_name(opfnamespace))));
1744 }

References CStringGetDatum(), ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, get_am_name(), get_namespace_name(), ObjectIdGetDatum(), and SearchSysCacheExists3.

Referenced by AlterObjectNamespace_internal(), and AlterObjectRename_internal().

◆ OpClassCacheLookup()

static HeapTuple OpClassCacheLookup ( Oid  amID,
List opclassname,
bool  missing_ok 
)
static

Definition at line 161 of file opclasscmds.c.

162 {
163  char *schemaname;
164  char *opcname;
165  HeapTuple htup;
166 
167  /* deconstruct the name list */
168  DeconstructQualifiedName(opclassname, &schemaname, &opcname);
169 
170  if (schemaname)
171  {
172  /* Look in specific schema only */
173  Oid namespaceId;
174 
175  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
176  if (!OidIsValid(namespaceId))
177  htup = NULL;
178  else
179  htup = SearchSysCache3(CLAAMNAMENSP,
180  ObjectIdGetDatum(amID),
181  PointerGetDatum(opcname),
182  ObjectIdGetDatum(namespaceId));
183  }
184  else
185  {
186  /* Unqualified opclass name, so search the search path */
187  Oid opcID = OpclassnameGetOpcid(amID, opcname);
188 
189  if (!OidIsValid(opcID))
190  htup = NULL;
191  else
192  htup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcID));
193  }
194 
195  if (!HeapTupleIsValid(htup) && !missing_ok)
196  {
197  HeapTuple amtup;
198 
199  amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
200  if (!HeapTupleIsValid(amtup))
201  elog(ERROR, "cache lookup failed for access method %u", amID);
202  ereport(ERROR,
203  (errcode(ERRCODE_UNDEFINED_OBJECT),
204  errmsg("operator class \"%s\" does not exist for access method \"%s\"",
205  NameListToString(opclassname),
206  NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
207  }
208 
209  return htup;
210 }
Oid OpclassnameGetOpcid(Oid amid, const char *opcname)
Definition: namespace.c:2106
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:3370
void DeconstructQualifiedName(const List *names, char **nspname_p, char **objname_p)
Definition: namespace.c:3286

References DeconstructQualifiedName(), elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, HeapTupleIsValid, LookupExplicitNamespace(), NameListToString(), NameStr, ObjectIdGetDatum(), OidIsValid, OpclassnameGetOpcid(), PointerGetDatum(), SearchSysCache1(), and SearchSysCache3().

Referenced by get_opclass_oid().

◆ OpFamilyCacheLookup()

static HeapTuple OpFamilyCacheLookup ( Oid  amID,
List opfamilyname,
bool  missing_ok 
)
static

Definition at line 80 of file opclasscmds.c.

81 {
82  char *schemaname;
83  char *opfname;
84  HeapTuple htup;
85 
86  /* deconstruct the name list */
87  DeconstructQualifiedName(opfamilyname, &schemaname, &opfname);
88 
89  if (schemaname)
90  {
91  /* Look in specific schema only */
92  Oid namespaceId;
93 
94  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
95  if (!OidIsValid(namespaceId))
96  htup = NULL;
97  else
98  htup = SearchSysCache3(OPFAMILYAMNAMENSP,
99  ObjectIdGetDatum(amID),
100  PointerGetDatum(opfname),
101  ObjectIdGetDatum(namespaceId));
102  }
103  else
104  {
105  /* Unqualified opfamily name, so search the search path */
106  Oid opfID = OpfamilynameGetOpfid(amID, opfname);
107 
108  if (!OidIsValid(opfID))
109  htup = NULL;
110  else
111  htup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfID));
112  }
113 
114  if (!HeapTupleIsValid(htup) && !missing_ok)
115  {
116  HeapTuple amtup;
117 
118  amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
119  if (!HeapTupleIsValid(amtup))
120  elog(ERROR, "cache lookup failed for access method %u", amID);
121  ereport(ERROR,
122  (errcode(ERRCODE_UNDEFINED_OBJECT),
123  errmsg("operator family \"%s\" does not exist for access method \"%s\"",
124  NameListToString(opfamilyname),
125  NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
126  }
127 
128  return htup;
129 }
Oid OpfamilynameGetOpfid(Oid amid, const char *opfname)
Definition: namespace.c:2208

References DeconstructQualifiedName(), elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, HeapTupleIsValid, LookupExplicitNamespace(), NameListToString(), NameStr, ObjectIdGetDatum(), OidIsValid, OpfamilynameGetOpfid(), PointerGetDatum(), SearchSysCache1(), and SearchSysCache3().

Referenced by get_opfamily_oid().

◆ processTypesSpec()

static void processTypesSpec ( List args,
Oid lefttype,
Oid righttype 
)
static

Definition at line 1107 of file opclasscmds.c.

1108 {
1109  TypeName *typeName;
1110 
1111  Assert(args != NIL);
1112 
1113  typeName = (TypeName *) linitial(args);
1114  *lefttype = typenameTypeId(NULL, typeName);
1115 
1116  if (list_length(args) > 1)
1117  {
1118  typeName = (TypeName *) lsecond(args);
1119  *righttype = typenameTypeId(NULL, typeName);
1120  }
1121  else
1122  *righttype = *lefttype;
1123 
1124  if (list_length(args) > 2)
1125  ereport(ERROR,
1126  (errcode(ERRCODE_SYNTAX_ERROR),
1127  errmsg("one or two argument types must be specified")));
1128 }
Assert(fmt[strlen(fmt) - 1] !='\n')
static int list_length(const List *l)
Definition: pg_list.h:152
#define linitial(l)
Definition: pg_list.h:178
#define lsecond(l)
Definition: pg_list.h:183

References generate_unaccent_rules::args, Assert(), ereport, errcode(), errmsg(), ERROR, linitial, list_length(), lsecond, NIL, and typenameTypeId().

Referenced by AlterOpFamilyAdd(), AlterOpFamilyDrop(), and DefineOpClass().

◆ storeOperators()

static void storeOperators ( List opfamilyname,
Oid  amoid,
Oid  opfamilyoid,
List operators,
bool  isAdd 
)
static

Definition at line 1428 of file opclasscmds.c.

1430 {
1431  Relation rel;
1432  Datum values[Natts_pg_amop];
1433  bool nulls[Natts_pg_amop];
1434  HeapTuple tup;
1435  Oid entryoid;
1436  ObjectAddress myself,
1437  referenced;
1438  ListCell *l;
1439 
1440  rel = table_open(AccessMethodOperatorRelationId, RowExclusiveLock);
1441 
1442  foreach(l, operators)
1443  {
1444  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1445  char oppurpose;
1446 
1447  /*
1448  * If adding to an existing family, check for conflict with an
1449  * existing pg_amop entry (just to give a nicer error message)
1450  */
1451  if (isAdd &&
1452  SearchSysCacheExists4(AMOPSTRATEGY,
1453  ObjectIdGetDatum(opfamilyoid),
1456  Int16GetDatum(op->number)))
1457  ereport(ERROR,
1459  errmsg("operator %d(%s,%s) already exists in operator family \"%s\"",
1460  op->number,
1461  format_type_be(op->lefttype),
1463  NameListToString(opfamilyname))));
1464 
1465  oppurpose = OidIsValid(op->sortfamily) ? AMOP_ORDER : AMOP_SEARCH;
1466 
1467  /* Create the pg_amop entry */
1468  memset(values, 0, sizeof(values));
1469  memset(nulls, false, sizeof(nulls));
1470 
1471  entryoid = GetNewOidWithIndex(rel, AccessMethodOperatorOidIndexId,
1472  Anum_pg_amop_oid);
1473  values[Anum_pg_amop_oid - 1] = ObjectIdGetDatum(entryoid);
1474  values[Anum_pg_amop_amopfamily - 1] = ObjectIdGetDatum(opfamilyoid);
1475  values[Anum_pg_amop_amoplefttype - 1] = ObjectIdGetDatum(op->lefttype);
1476  values[Anum_pg_amop_amoprighttype - 1] = ObjectIdGetDatum(op->righttype);
1477  values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
1478  values[Anum_pg_amop_amoppurpose - 1] = CharGetDatum(oppurpose);
1479  values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
1480  values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
1481  values[Anum_pg_amop_amopsortfamily - 1] = ObjectIdGetDatum(op->sortfamily);
1482 
1483  tup = heap_form_tuple(rel->rd_att, values, nulls);
1484 
1485  CatalogTupleInsert(rel, tup);
1486 
1487  heap_freetuple(tup);
1488 
1489  /* Make its dependencies */
1490  myself.classId = AccessMethodOperatorRelationId;
1491  myself.objectId = entryoid;
1492  myself.objectSubId = 0;
1493 
1494  referenced.classId = OperatorRelationId;
1495  referenced.objectId = op->object;
1496  referenced.objectSubId = 0;
1497 
1498  /* see comments in amapi.h about dependency strength */
1499  recordDependencyOn(&myself, &referenced,
1501 
1502  referenced.classId = op->ref_is_family ? OperatorFamilyRelationId :
1503  OperatorClassRelationId;
1504  referenced.objectId = op->refobjid;
1505  referenced.objectSubId = 0;
1506 
1507  recordDependencyOn(&myself, &referenced,
1509 
1510  /* A search operator also needs a dep on the referenced opfamily */
1511  if (OidIsValid(op->sortfamily))
1512  {
1513  referenced.classId = OperatorFamilyRelationId;
1514  referenced.objectId = op->sortfamily;
1515  referenced.objectSubId = 0;
1516 
1517  recordDependencyOn(&myself, &referenced,
1519  }
1520 
1521  /* Post create hook of this access method operator */
1522  InvokeObjectPostCreateHook(AccessMethodOperatorRelationId,
1523  entryoid, 0);
1524  }
1525 
1527 }
@ DEPENDENCY_INTERNAL
Definition: dependency.h:35
static Datum CharGetDatum(char X)
Definition: postgres.h:122
#define SearchSysCacheExists4(cacheId, key1, key2, key3, key4)
Definition: syscache.h:101

References CatalogTupleInsert(), CharGetDatum(), ObjectAddress::classId, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, format_type_be(), GetNewOidWithIndex(), heap_form_tuple(), heap_freetuple(), Int16GetDatum(), InvokeObjectPostCreateHook, OpFamilyMember::lefttype, lfirst, NameListToString(), OpFamilyMember::number, OpFamilyMember::object, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, OidIsValid, RelationData::rd_att, recordDependencyOn(), OpFamilyMember::ref_is_family, OpFamilyMember::ref_is_hard, OpFamilyMember::refobjid, OpFamilyMember::righttype, RowExclusiveLock, SearchSysCacheExists4, OpFamilyMember::sortfamily, table_close(), table_open(), and values.

Referenced by AlterOpFamilyAdd(), and DefineOpClass().

◆ storeProcedures()

static void storeProcedures ( List opfamilyname,
Oid  amoid,
Oid  opfamilyoid,
List procedures,
bool  isAdd 
)
static

Definition at line 1535 of file opclasscmds.c.

1537 {
1538  Relation rel;
1539  Datum values[Natts_pg_amproc];
1540  bool nulls[Natts_pg_amproc];
1541  HeapTuple tup;
1542  Oid entryoid;
1543  ObjectAddress myself,
1544  referenced;
1545  ListCell *l;
1546 
1547  rel = table_open(AccessMethodProcedureRelationId, RowExclusiveLock);
1548 
1549  foreach(l, procedures)
1550  {
1551  OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
1552 
1553  /*
1554  * If adding to an existing family, check for conflict with an
1555  * existing pg_amproc entry (just to give a nicer error message)
1556  */
1557  if (isAdd &&
1558  SearchSysCacheExists4(AMPROCNUM,
1559  ObjectIdGetDatum(opfamilyoid),
1560  ObjectIdGetDatum(proc->lefttype),
1561  ObjectIdGetDatum(proc->righttype),
1562  Int16GetDatum(proc->number)))
1563  ereport(ERROR,
1565  errmsg("function %d(%s,%s) already exists in operator family \"%s\"",
1566  proc->number,
1567  format_type_be(proc->lefttype),
1568  format_type_be(proc->righttype),
1569  NameListToString(opfamilyname))));
1570 
1571  /* Create the pg_amproc entry */
1572  memset(values, 0, sizeof(values));
1573  memset(nulls, false, sizeof(nulls));
1574 
1575  entryoid = GetNewOidWithIndex(rel, AccessMethodProcedureOidIndexId,
1576  Anum_pg_amproc_oid);
1577  values[Anum_pg_amproc_oid - 1] = ObjectIdGetDatum(entryoid);
1578  values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
1579  values[Anum_pg_amproc_amproclefttype - 1] = ObjectIdGetDatum(proc->lefttype);
1580  values[Anum_pg_amproc_amprocrighttype - 1] = ObjectIdGetDatum(proc->righttype);
1581  values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
1582  values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);
1583 
1584  tup = heap_form_tuple(rel->rd_att, values, nulls);
1585 
1586  CatalogTupleInsert(rel, tup);
1587 
1588  heap_freetuple(tup);
1589 
1590  /* Make its dependencies */
1591  myself.classId = AccessMethodProcedureRelationId;
1592  myself.objectId = entryoid;
1593  myself.objectSubId = 0;
1594 
1595  referenced.classId = ProcedureRelationId;
1596  referenced.objectId = proc->object;
1597  referenced.objectSubId = 0;
1598 
1599  /* see comments in amapi.h about dependency strength */
1600  recordDependencyOn(&myself, &referenced,
1602 
1603  referenced.classId = proc->ref_is_family ? OperatorFamilyRelationId :
1604  OperatorClassRelationId;
1605  referenced.objectId = proc->refobjid;
1606  referenced.objectSubId = 0;
1607 
1608  recordDependencyOn(&myself, &referenced,
1610 
1611  /* Post create hook of access method procedure */
1612  InvokeObjectPostCreateHook(AccessMethodProcedureRelationId,
1613  entryoid, 0);
1614  }
1615 
1617 }

References CatalogTupleInsert(), ObjectAddress::classId, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, DEPENDENCY_NORMAL, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, format_type_be(), GetNewOidWithIndex(), heap_form_tuple(), heap_freetuple(), Int16GetDatum(), InvokeObjectPostCreateHook, OpFamilyMember::lefttype, lfirst, NameListToString(), OpFamilyMember::number, OpFamilyMember::object, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, RelationData::rd_att, recordDependencyOn(), OpFamilyMember::ref_is_family, OpFamilyMember::ref_is_hard, OpFamilyMember::refobjid, OpFamilyMember::righttype, RowExclusiveLock, SearchSysCacheExists4, table_close(), table_open(), and values.

Referenced by AlterOpFamilyAdd(), and DefineOpClass().