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 bool typeDepNeeded (Oid typid, OpFamilyMember *member)
 
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 1392 of file opclasscmds.c.

1393 {
1394  ListCell *l;
1395 
1396  foreach(l, *list)
1397  {
1398  OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
1399 
1400  if (old->number == member->number &&
1401  old->lefttype == member->lefttype &&
1402  old->righttype == member->righttype)
1403  {
1404  if (member->is_func)
1405  ereport(ERROR,
1406  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1407  errmsg("function number %d for (%s,%s) appears more than once",
1408  member->number,
1409  format_type_be(member->lefttype),
1410  format_type_be(member->righttype))));
1411  else
1412  ereport(ERROR,
1413  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1414  errmsg("operator number %d for (%s,%s) appears more than once",
1415  member->number,
1416  format_type_be(member->lefttype),
1417  format_type_be(member->righttype))));
1418  }
1419  }
1420  *list = lappend(*list, member);
1421 }
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#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:89
Oid righttype
Definition: amapi.h:90
int number
Definition: amapi.h:88
bool is_func
Definition: amapi.h:86

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 817 of file opclasscmds.c.

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

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 881 of file opclasscmds.c.

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

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 1030 of file opclasscmds.c.

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

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 1137 of file opclasscmds.c.

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

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 1203 of file opclasscmds.c.

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

245 {
246  Oid opfamilyoid;
247  Relation rel;
248  HeapTuple tup;
249  Datum values[Natts_pg_opfamily];
250  bool nulls[Natts_pg_opfamily];
251  NameData opfName;
252  ObjectAddress myself,
253  referenced;
254 
255  rel = table_open(OperatorFamilyRelationId, RowExclusiveLock);
256 
257  /*
258  * Make sure there is no existing opfamily of this name (this is just to
259  * give a more friendly error message than "duplicate key").
260  */
261  if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
262  ObjectIdGetDatum(amoid),
263  CStringGetDatum(opfname),
264  ObjectIdGetDatum(namespaceoid)))
265  ereport(ERROR,
267  errmsg("operator family \"%s\" for access method \"%s\" already exists",
268  opfname, stmt->amname)));
269 
270  /*
271  * Okay, let's create the pg_opfamily entry.
272  */
273  memset(values, 0, sizeof(values));
274  memset(nulls, false, sizeof(nulls));
275 
276  opfamilyoid = GetNewOidWithIndex(rel, OpfamilyOidIndexId,
277  Anum_pg_opfamily_oid);
278  values[Anum_pg_opfamily_oid - 1] = ObjectIdGetDatum(opfamilyoid);
279  values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
280  namestrcpy(&opfName, opfname);
281  values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
282  values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
283  values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
284 
285  tup = heap_form_tuple(rel->rd_att, values, nulls);
286 
287  CatalogTupleInsert(rel, tup);
288 
289  heap_freetuple(tup);
290 
291  /*
292  * Create dependencies for the opfamily proper.
293  */
294  myself.classId = OperatorFamilyRelationId;
295  myself.objectId = opfamilyoid;
296  myself.objectSubId = 0;
297 
298  /* dependency on access method */
299  referenced.classId = AccessMethodRelationId;
300  referenced.objectId = amoid;
301  referenced.objectSubId = 0;
302  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
303 
304  /* dependency on namespace */
305  referenced.classId = NamespaceRelationId;
306  referenced.objectId = namespaceoid;
307  referenced.objectSubId = 0;
308  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
309 
310  /* dependency on owner */
311  recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
312 
313  /* dependency on extension */
314  recordDependencyOnCurrentExtension(&myself, false);
315 
316  /* Report the new operator family to possibly interested event triggers */
318  (Node *) stmt);
319 
320  /* Post creation hook for new operator family */
321  InvokeObjectPostCreateHook(OperatorFamilyRelationId, opfamilyoid, 0);
322 
324 
325  return myself;
326 }
static Datum values[MAXATTR]
Definition: bootstrap.c:151
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:419
@ 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:45
void recordDependencyOnCurrentExtension(const ObjectAddress *object, bool isReplace)
Definition: pg_depend.c:193
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:168
uintptr_t Datum
Definition: postgres.h:64
static Datum NameGetDatum(const NameData *X)
Definition: postgres.h:373
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
Definition: nodes.h:129
TupleDesc rd_att
Definition: rel.h:112
Definition: c.h:695
#define SearchSysCacheExists3(cacheId, key1, key2, key3)
Definition: syscache.h:104
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 333 of file opclasscmds.c.

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

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 772 of file opclasscmds.c.

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

1727 {
1728  ListCell *l;
1729 
1730  foreach(l, operators)
1731  {
1732  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1733  Oid amopid;
1734  ObjectAddress object;
1735 
1736  amopid = GetSysCacheOid4(AMOPSTRATEGY, Anum_pg_amop_oid,
1737  ObjectIdGetDatum(opfamilyoid),
1740  Int16GetDatum(op->number));
1741  if (!OidIsValid(amopid))
1742  ereport(ERROR,
1743  (errcode(ERRCODE_UNDEFINED_OBJECT),
1744  errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"",
1745  op->number,
1746  format_type_be(op->lefttype),
1748  NameListToString(opfamilyname))));
1749 
1750  object.classId = AccessMethodOperatorRelationId;
1751  object.objectId = amopid;
1752  object.objectSubId = 0;
1753 
1754  performDeletion(&object, DROP_RESTRICT, 0);
1755  }
1756 }
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:273
char * NameListToString(const List *names)
Definition: namespace.c:3594
@ DROP_RESTRICT
Definition: parsenodes.h:2341
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:172
#define GetSysCacheOid4(cacheId, oidcol, key1, key2, key3, key4)
Definition: syscache.h:115

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 1765 of file opclasscmds.c.

1767 {
1768  ListCell *l;
1769 
1770  foreach(l, procedures)
1771  {
1772  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1773  Oid amprocid;
1774  ObjectAddress object;
1775 
1776  amprocid = GetSysCacheOid4(AMPROCNUM, Anum_pg_amproc_oid,
1777  ObjectIdGetDatum(opfamilyoid),
1780  Int16GetDatum(op->number));
1781  if (!OidIsValid(amprocid))
1782  ereport(ERROR,
1783  (errcode(ERRCODE_UNDEFINED_OBJECT),
1784  errmsg("function %d(%s,%s) does not exist in operator family \"%s\"",
1785  op->number,
1786  format_type_be(op->lefttype),
1788  NameListToString(opfamilyname))));
1789 
1790  object.classId = AccessMethodProcedureRelationId;
1791  object.objectId = amprocid;
1792  object.objectSubId = 0;
1793 
1794  performDeletion(&object, DROP_RESTRICT, 0);
1795  }
1796 }

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 220 of file opclasscmds.c.

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

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 139 of file opclasscmds.c.

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

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 1805 of file opclasscmds.c.

1807 {
1808  /* make sure the new name doesn't exist */
1809  if (SearchSysCacheExists3(CLAAMNAMENSP,
1810  ObjectIdGetDatum(opcmethod),
1811  CStringGetDatum(opcname),
1812  ObjectIdGetDatum(opcnamespace)))
1813  ereport(ERROR,
1815  errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1816  opcname,
1817  get_am_name(opcmethod),
1818  get_namespace_name(opcnamespace))));
1819 }

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 1828 of file opclasscmds.c.

1830 {
1831  /* make sure the new name doesn't exist */
1832  if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
1833  ObjectIdGetDatum(opfmethod),
1834  CStringGetDatum(opfname),
1835  ObjectIdGetDatum(opfnamespace)))
1836  ereport(ERROR,
1838  errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1839  opfname,
1840  get_am_name(opfmethod),
1841  get_namespace_name(opfnamespace))));
1842 }

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 162 of file opclasscmds.c.

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

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 81 of file opclasscmds.c.

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

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 1108 of file opclasscmds.c.

1109 {
1110  TypeName *typeName;
1111 
1112  Assert(args != NIL);
1113 
1114  typeName = (TypeName *) linitial(args);
1115  *lefttype = typenameTypeId(NULL, typeName);
1116 
1117  if (list_length(args) > 1)
1118  {
1119  typeName = (TypeName *) lsecond(args);
1120  *righttype = typenameTypeId(NULL, typeName);
1121  }
1122  else
1123  *righttype = *lefttype;
1124 
1125  if (list_length(args) > 2)
1126  ereport(ERROR,
1127  (errcode(ERRCODE_SYNTAX_ERROR),
1128  errmsg("one or two argument types must be specified")));
1129 }
#define Assert(condition)
Definition: c.h:812
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 1429 of file opclasscmds.c.

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

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(), typeDepNeeded(), 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 1559 of file opclasscmds.c.

1561 {
1562  Relation rel;
1563  Datum values[Natts_pg_amproc];
1564  bool nulls[Natts_pg_amproc];
1565  HeapTuple tup;
1566  Oid entryoid;
1567  ObjectAddress myself,
1568  referenced;
1569  ListCell *l;
1570 
1571  rel = table_open(AccessMethodProcedureRelationId, RowExclusiveLock);
1572 
1573  foreach(l, procedures)
1574  {
1575  OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
1576 
1577  /*
1578  * If adding to an existing family, check for conflict with an
1579  * existing pg_amproc entry (just to give a nicer error message)
1580  */
1581  if (isAdd &&
1582  SearchSysCacheExists4(AMPROCNUM,
1583  ObjectIdGetDatum(opfamilyoid),
1584  ObjectIdGetDatum(proc->lefttype),
1585  ObjectIdGetDatum(proc->righttype),
1586  Int16GetDatum(proc->number)))
1587  ereport(ERROR,
1589  errmsg("function %d(%s,%s) already exists in operator family \"%s\"",
1590  proc->number,
1591  format_type_be(proc->lefttype),
1592  format_type_be(proc->righttype),
1593  NameListToString(opfamilyname))));
1594 
1595  /* Create the pg_amproc entry */
1596  memset(values, 0, sizeof(values));
1597  memset(nulls, false, sizeof(nulls));
1598 
1599  entryoid = GetNewOidWithIndex(rel, AccessMethodProcedureOidIndexId,
1600  Anum_pg_amproc_oid);
1601  values[Anum_pg_amproc_oid - 1] = ObjectIdGetDatum(entryoid);
1602  values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
1603  values[Anum_pg_amproc_amproclefttype - 1] = ObjectIdGetDatum(proc->lefttype);
1604  values[Anum_pg_amproc_amprocrighttype - 1] = ObjectIdGetDatum(proc->righttype);
1605  values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
1606  values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);
1607 
1608  tup = heap_form_tuple(rel->rd_att, values, nulls);
1609 
1610  CatalogTupleInsert(rel, tup);
1611 
1612  heap_freetuple(tup);
1613 
1614  /* Make its dependencies */
1615  myself.classId = AccessMethodProcedureRelationId;
1616  myself.objectId = entryoid;
1617  myself.objectSubId = 0;
1618 
1619  referenced.classId = ProcedureRelationId;
1620  referenced.objectId = proc->object;
1621  referenced.objectSubId = 0;
1622 
1623  /* see comments in amapi.h about dependency strength */
1624  recordDependencyOn(&myself, &referenced,
1626 
1627  referenced.classId = proc->ref_is_family ? OperatorFamilyRelationId :
1628  OperatorClassRelationId;
1629  referenced.objectId = proc->refobjid;
1630  referenced.objectSubId = 0;
1631 
1632  recordDependencyOn(&myself, &referenced,
1634 
1635  if (typeDepNeeded(proc->lefttype, proc))
1636  {
1637  referenced.classId = TypeRelationId;
1638  referenced.objectId = proc->lefttype;
1639  referenced.objectSubId = 0;
1640 
1641  /* see comments in amapi.h about dependency strength */
1642  recordDependencyOn(&myself, &referenced,
1644  }
1645 
1646  if (proc->lefttype != proc->righttype &&
1647  typeDepNeeded(proc->righttype, proc))
1648  {
1649  referenced.classId = TypeRelationId;
1650  referenced.objectId = proc->righttype;
1651  referenced.objectSubId = 0;
1652 
1653  /* see comments in amapi.h about dependency strength */
1654  recordDependencyOn(&myself, &referenced,
1656  }
1657 
1658  /* Post create hook of access method procedure */
1659  InvokeObjectPostCreateHook(AccessMethodProcedureRelationId,
1660  entryoid, 0);
1661  }
1662 
1664 }

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(), typeDepNeeded(), and values.

Referenced by AlterOpFamilyAdd(), and DefineOpClass().

◆ typeDepNeeded()

static bool typeDepNeeded ( Oid  typid,
OpFamilyMember member 
)
static

Definition at line 1675 of file opclasscmds.c.

1676 {
1677  bool result = true;
1678 
1679  /*
1680  * If the type is pinned, we don't need a dependency. This is a bit of a
1681  * layering violation perhaps (recordDependencyOn would ignore the request
1682  * anyway), but it's a cheap test and will frequently save a syscache
1683  * lookup here.
1684  */
1685  if (IsPinnedObject(TypeRelationId, typid))
1686  return false;
1687 
1688  /* Nope, so check the input types of the function or operator. */
1689  if (member->is_func)
1690  {
1691  Oid *argtypes;
1692  int nargs;
1693 
1694  (void) get_func_signature(member->object, &argtypes, &nargs);
1695  for (int i = 0; i < nargs; i++)
1696  {
1697  if (typid == argtypes[i])
1698  {
1699  result = false; /* match, no dependency needed */
1700  break;
1701  }
1702  }
1703  pfree(argtypes);
1704  }
1705  else
1706  {
1707  Oid lefttype,
1708  righttype;
1709 
1710  op_input_types(member->object, &lefttype, &righttype);
1711  if (typid == lefttype || typid == righttype)
1712  result = false; /* match, no dependency needed */
1713  }
1714  return result;
1715 }
bool IsPinnedObject(Oid classId, Oid objectId)
Definition: catalog.c:341
int i
Definition: isn.c:72
Oid get_func_signature(Oid funcid, Oid **argtypes, int *nargs)
Definition: lsyscache.c:1696
void op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
Definition: lsyscache.c:1358
void pfree(void *pointer)
Definition: mcxt.c:1521

References get_func_signature(), i, OpFamilyMember::is_func, IsPinnedObject(), OpFamilyMember::object, op_input_types(), and pfree().

Referenced by storeOperators(), and storeProcedures().