PostgreSQL Source Code  git master
opclasscmds.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * opclasscmds.c
4  *
5  * Routines for opclass (and opfamily) manipulation commands
6  *
7  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  * src/backend/commands/opclasscmds.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17 
18 #include <limits.h>
19 
20 #include "access/genam.h"
21 #include "access/hash.h"
22 #include "access/heapam.h"
23 #include "access/nbtree.h"
24 #include "access/htup_details.h"
25 #include "access/sysattr.h"
26 #include "catalog/dependency.h"
27 #include "catalog/indexing.h"
28 #include "catalog/objectaccess.h"
29 #include "catalog/opfam_internal.h"
30 #include "catalog/pg_am.h"
31 #include "catalog/pg_amop.h"
32 #include "catalog/pg_amproc.h"
33 #include "catalog/pg_namespace.h"
34 #include "catalog/pg_opclass.h"
35 #include "catalog/pg_operator.h"
36 #include "catalog/pg_opfamily.h"
37 #include "catalog/pg_proc.h"
38 #include "catalog/pg_type.h"
39 #include "commands/alter.h"
40 #include "commands/defrem.h"
41 #include "commands/event_trigger.h"
42 #include "miscadmin.h"
43 #include "parser/parse_func.h"
44 #include "parser/parse_oper.h"
45 #include "parser/parse_type.h"
46 #include "utils/builtins.h"
47 #include "utils/fmgroids.h"
48 #include "utils/lsyscache.h"
49 #include "utils/rel.h"
50 #include "utils/syscache.h"
51 #include "utils/tqual.h"
52 
53 
54 static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt,
55  Oid amoid, Oid opfamilyoid,
56  int maxOpNumber, int maxProcNumber,
57  List *items);
58 static void AlterOpFamilyDrop(AlterOpFamilyStmt *stmt,
59  Oid amoid, Oid opfamilyoid,
60  int maxOpNumber, int maxProcNumber,
61  List *items);
62 static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
63 static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
64 static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
65 static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc);
66 static void storeOperators(List *opfamilyname, Oid amoid,
67  Oid opfamilyoid, Oid opclassoid,
68  List *operators, bool isAdd);
69 static void storeProcedures(List *opfamilyname, Oid amoid,
70  Oid opfamilyoid, Oid opclassoid,
71  List *procedures, bool isAdd);
72 static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
73  List *operators);
74 static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
75  List *procedures);
76 
77 /*
78  * OpFamilyCacheLookup
79  * Look up an existing opfamily by name.
80  *
81  * Returns a syscache tuple reference, or NULL if not found.
82  */
83 static HeapTuple
84 OpFamilyCacheLookup(Oid amID, List *opfamilyname, bool missing_ok)
85 {
86  char *schemaname;
87  char *opfname;
88  HeapTuple htup;
89 
90  /* deconstruct the name list */
91  DeconstructQualifiedName(opfamilyname, &schemaname, &opfname);
92 
93  if (schemaname)
94  {
95  /* Look in specific schema only */
96  Oid namespaceId;
97 
98  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
99  if (!OidIsValid(namespaceId))
100  htup = NULL;
101  else
103  ObjectIdGetDatum(amID),
104  PointerGetDatum(opfname),
105  ObjectIdGetDatum(namespaceId));
106  }
107  else
108  {
109  /* Unqualified opfamily name, so search the search path */
110  Oid opfID = OpfamilynameGetOpfid(amID, opfname);
111 
112  if (!OidIsValid(opfID))
113  htup = NULL;
114  else
116  }
117 
118  if (!HeapTupleIsValid(htup) && !missing_ok)
119  {
120  HeapTuple amtup;
121 
122  amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
123  if (!HeapTupleIsValid(amtup))
124  elog(ERROR, "cache lookup failed for access method %u", amID);
125  ereport(ERROR,
126  (errcode(ERRCODE_UNDEFINED_OBJECT),
127  errmsg("operator family \"%s\" does not exist for access method \"%s\"",
128  NameListToString(opfamilyname),
129  NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
130  }
131 
132  return htup;
133 }
134 
135 /*
136  * get_opfamily_oid
137  * find an opfamily OID by possibly qualified name
138  *
139  * If not found, returns InvalidOid if missing_ok, else throws error.
140  */
141 Oid
142 get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok)
143 {
144  HeapTuple htup;
145  Oid opfID;
146 
147  htup = OpFamilyCacheLookup(amID, opfamilyname, missing_ok);
148  if (!HeapTupleIsValid(htup))
149  return InvalidOid;
150  opfID = HeapTupleGetOid(htup);
151  ReleaseSysCache(htup);
152 
153  return opfID;
154 }
155 
156 /*
157  * OpClassCacheLookup
158  * Look up an existing opclass by name.
159  *
160  * Returns a syscache tuple reference, or NULL if not found.
161  */
162 static HeapTuple
163 OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok)
164 {
165  char *schemaname;
166  char *opcname;
167  HeapTuple htup;
168 
169  /* deconstruct the name list */
170  DeconstructQualifiedName(opclassname, &schemaname, &opcname);
171 
172  if (schemaname)
173  {
174  /* Look in specific schema only */
175  Oid namespaceId;
176 
177  namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
178  if (!OidIsValid(namespaceId))
179  htup = NULL;
180  else
182  ObjectIdGetDatum(amID),
183  PointerGetDatum(opcname),
184  ObjectIdGetDatum(namespaceId));
185  }
186  else
187  {
188  /* Unqualified opclass name, so search the search path */
189  Oid opcID = OpclassnameGetOpcid(amID, opcname);
190 
191  if (!OidIsValid(opcID))
192  htup = NULL;
193  else
194  htup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcID));
195  }
196 
197  if (!HeapTupleIsValid(htup) && !missing_ok)
198  {
199  HeapTuple amtup;
200 
201  amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
202  if (!HeapTupleIsValid(amtup))
203  elog(ERROR, "cache lookup failed for access method %u", amID);
204  ereport(ERROR,
205  (errcode(ERRCODE_UNDEFINED_OBJECT),
206  errmsg("operator class \"%s\" does not exist for access method \"%s\"",
207  NameListToString(opclassname),
208  NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
209  }
210 
211  return htup;
212 }
213 
214 /*
215  * get_opclass_oid
216  * find an opclass OID by possibly qualified name
217  *
218  * If not found, returns InvalidOid if missing_ok, else throws error.
219  */
220 Oid
221 get_opclass_oid(Oid amID, List *opclassname, bool missing_ok)
222 {
223  HeapTuple htup;
224  Oid opcID;
225 
226  htup = OpClassCacheLookup(amID, opclassname, missing_ok);
227  if (!HeapTupleIsValid(htup))
228  return InvalidOid;
229  opcID = HeapTupleGetOid(htup);
230  ReleaseSysCache(htup);
231 
232  return opcID;
233 }
234 
235 /*
236  * CreateOpFamily
237  * Internal routine to make the catalog entry for a new operator family.
238  *
239  * Caller must have done permissions checks etc. already.
240  */
241 static ObjectAddress
242 CreateOpFamily(const char *amname, const char *opfname, Oid namespaceoid, Oid amoid)
243 {
244  Oid opfamilyoid;
245  Relation rel;
246  HeapTuple tup;
247  Datum values[Natts_pg_opfamily];
248  bool nulls[Natts_pg_opfamily];
249  NameData opfName;
250  ObjectAddress myself,
251  referenced;
252 
253  rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
254 
255  /*
256  * Make sure there is no existing opfamily of this name (this is just to
257  * give a more friendly error message than "duplicate key").
258  */
260  ObjectIdGetDatum(amoid),
261  CStringGetDatum(opfname),
262  ObjectIdGetDatum(namespaceoid)))
263  ereport(ERROR,
265  errmsg("operator family \"%s\" for access method \"%s\" already exists",
266  opfname, amname)));
267 
268  /*
269  * Okay, let's create the pg_opfamily entry.
270  */
271  memset(values, 0, sizeof(values));
272  memset(nulls, false, sizeof(nulls));
273 
274  values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
275  namestrcpy(&opfName, opfname);
276  values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
277  values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
278  values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
279 
280  tup = heap_form_tuple(rel->rd_att, values, nulls);
281 
282  opfamilyoid = CatalogTupleInsert(rel, tup);
283 
284  heap_freetuple(tup);
285 
286  /*
287  * Create dependencies for the opfamily proper.
288  */
289  myself.classId = OperatorFamilyRelationId;
290  myself.objectId = opfamilyoid;
291  myself.objectSubId = 0;
292 
293  /* dependency on access method */
294  referenced.classId = AccessMethodRelationId;
295  referenced.objectId = amoid;
296  referenced.objectSubId = 0;
297  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
298 
299  /* dependency on namespace */
300  referenced.classId = NamespaceRelationId;
301  referenced.objectId = namespaceoid;
302  referenced.objectSubId = 0;
303  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
304 
305  /* dependency on owner */
306  recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
307 
308  /* dependency on extension */
309  recordDependencyOnCurrentExtension(&myself, false);
310 
311  /* Post creation hook for new operator family */
312  InvokeObjectPostCreateHook(OperatorFamilyRelationId, opfamilyoid, 0);
313 
315 
316  return myself;
317 }
318 
319 /*
320  * DefineOpClass
321  * Define a new index operator class.
322  */
325 {
326  char *opcname; /* name of opclass we're creating */
327  Oid amoid, /* our AM's oid */
328  typeoid, /* indexable datatype oid */
329  storageoid, /* storage datatype oid, if any */
330  namespaceoid, /* namespace to create opclass in */
331  opfamilyoid, /* oid of containing opfamily */
332  opclassoid; /* oid of opclass we create */
333  int maxOpNumber, /* amstrategies value */
334  maxProcNumber; /* amsupport value */
335  bool amstorage; /* amstorage flag */
336  List *operators; /* OpFamilyMember list for operators */
337  List *procedures; /* OpFamilyMember list for support procs */
338  ListCell *l;
339  Relation rel;
340  HeapTuple tup;
341  IndexAmRoutine *amroutine;
342  Datum values[Natts_pg_opclass];
343  bool nulls[Natts_pg_opclass];
344  AclResult aclresult;
345  NameData opcName;
346  ObjectAddress myself,
347  referenced;
348 
349  /* Convert list of names to a name and namespace */
350  namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,
351  &opcname);
352 
353  /* Check we have creation rights in target namespace */
354  aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
355  if (aclresult != ACLCHECK_OK)
356  aclcheck_error(aclresult, OBJECT_SCHEMA,
357  get_namespace_name(namespaceoid));
358 
359  /* Get necessary info about access method */
361  if (!HeapTupleIsValid(tup))
362  ereport(ERROR,
363  (errcode(ERRCODE_UNDEFINED_OBJECT),
364  errmsg("access method \"%s\" does not exist",
365  stmt->amname)));
366 
367  amoid = HeapTupleGetOid(tup);
368  amroutine = GetIndexAmRoutineByAmId(amoid, false);
369  ReleaseSysCache(tup);
370 
371  maxOpNumber = amroutine->amstrategies;
372  /* if amstrategies is zero, just enforce that op numbers fit in int16 */
373  if (maxOpNumber <= 0)
374  maxOpNumber = SHRT_MAX;
375  maxProcNumber = amroutine->amsupport;
376  amstorage = amroutine->amstorage;
377 
378  /* XXX Should we make any privilege check against the AM? */
379 
380  /*
381  * The question of appropriate permissions for CREATE OPERATOR CLASS is
382  * interesting. Creating an opclass is tantamount to granting public
383  * execute access on the functions involved, since the index machinery
384  * generally does not check access permission before using the functions.
385  * A minimum expectation therefore is that the caller have execute
386  * privilege with grant option. Since we don't have a way to make the
387  * opclass go away if the grant option is revoked, we choose instead to
388  * require ownership of the functions. It's also not entirely clear what
389  * permissions should be required on the datatype, but ownership seems
390  * like a safe choice.
391  *
392  * Currently, we require superuser privileges to create an opclass. This
393  * seems necessary because we have no way to validate that the offered set
394  * of operators and functions are consistent with the AM's expectations.
395  * It would be nice to provide such a check someday, if it can be done
396  * without solving the halting problem :-(
397  *
398  * XXX re-enable NOT_USED code sections below if you remove this test.
399  */
400  if (!superuser())
401  ereport(ERROR,
402  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
403  errmsg("must be superuser to create an operator class")));
404 
405  /* Look up the datatype */
406  typeoid = typenameTypeId(NULL, stmt->datatype);
407 
408 #ifdef NOT_USED
409  /* XXX this is unnecessary given the superuser check above */
410  /* Check we have ownership of the datatype */
411  if (!pg_type_ownercheck(typeoid, GetUserId()))
413 #endif
414 
415  /*
416  * Look up the containing operator family, or create one if FAMILY option
417  * was omitted and there's not a match already.
418  */
419  if (stmt->opfamilyname)
420  {
421  opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
422  }
423  else
424  {
425  /* Lookup existing family of same name and namespace */
427  ObjectIdGetDatum(amoid),
428  PointerGetDatum(opcname),
429  ObjectIdGetDatum(namespaceoid));
430  if (HeapTupleIsValid(tup))
431  {
432  opfamilyoid = HeapTupleGetOid(tup);
433 
434  /*
435  * XXX given the superuser check above, there's no need for an
436  * ownership check here
437  */
438  ReleaseSysCache(tup);
439  }
440  else
441  {
442  ObjectAddress tmpAddr;
443 
444  /*
445  * Create it ... again no need for more permissions ...
446  */
447  tmpAddr = CreateOpFamily(stmt->amname, opcname,
448  namespaceoid, amoid);
449  opfamilyoid = tmpAddr.objectId;
450  }
451  }
452 
453  operators = NIL;
454  procedures = NIL;
455 
456  /* Storage datatype is optional */
457  storageoid = InvalidOid;
458 
459  /*
460  * Scan the "items" list to obtain additional info.
461  */
462  foreach(l, stmt->items)
463  {
465  Oid operOid;
466  Oid funcOid;
467  Oid sortfamilyOid;
469 
470  switch (item->itemtype)
471  {
473  if (item->number <= 0 || item->number > maxOpNumber)
474  ereport(ERROR,
475  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
476  errmsg("invalid operator number %d,"
477  " must be between 1 and %d",
478  item->number, maxOpNumber)));
479  if (item->name->objargs != NIL)
480  operOid = LookupOperWithArgs(item->name, false);
481  else
482  {
483  /* Default to binary op on input datatype */
484  operOid = LookupOperName(NULL, item->name->objname,
485  typeoid, typeoid,
486  false, -1);
487  }
488 
489  if (item->order_family)
490  sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
491  item->order_family,
492  false);
493  else
494  sortfamilyOid = InvalidOid;
495 
496 #ifdef NOT_USED
497  /* XXX this is unnecessary given the superuser check above */
498  /* Caller must own operator and its underlying function */
499  if (!pg_oper_ownercheck(operOid, GetUserId()))
501  get_opname(operOid));
502  funcOid = get_opcode(operOid);
503  if (!pg_proc_ownercheck(funcOid, GetUserId()))
505  get_func_name(funcOid));
506 #endif
507 
508  /* Save the info */
509  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
510  member->object = operOid;
511  member->number = item->number;
512  member->sortfamily = sortfamilyOid;
513  assignOperTypes(member, amoid, typeoid);
514  addFamilyMember(&operators, member, false);
515  break;
517  if (item->number <= 0 || item->number > maxProcNumber)
518  ereport(ERROR,
519  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
520  errmsg("invalid procedure number %d,"
521  " must be between 1 and %d",
522  item->number, maxProcNumber)));
523  funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
524 #ifdef NOT_USED
525  /* XXX this is unnecessary given the superuser check above */
526  /* Caller must own function */
527  if (!pg_proc_ownercheck(funcOid, GetUserId()))
529  get_func_name(funcOid));
530 #endif
531 
532  /* Save the info */
533  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
534  member->object = funcOid;
535  member->number = item->number;
536 
537  /* allow overriding of the function's actual arg types */
538  if (item->class_args)
540  &member->lefttype, &member->righttype);
541 
542  assignProcTypes(member, amoid, typeoid);
543  addFamilyMember(&procedures, member, true);
544  break;
546  if (OidIsValid(storageoid))
547  ereport(ERROR,
548  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
549  errmsg("storage type specified more than once")));
550  storageoid = typenameTypeId(NULL, item->storedtype);
551 
552 #ifdef NOT_USED
553  /* XXX this is unnecessary given the superuser check above */
554  /* Check we have ownership of the datatype */
555  if (!pg_type_ownercheck(storageoid, GetUserId()))
557 #endif
558  break;
559  default:
560  elog(ERROR, "unrecognized item type: %d", item->itemtype);
561  break;
562  }
563  }
564 
565  /*
566  * If storagetype is specified, make sure it's legal.
567  */
568  if (OidIsValid(storageoid))
569  {
570  /* Just drop the spec if same as column datatype */
571  if (storageoid == typeoid)
572  storageoid = InvalidOid;
573  else if (!amstorage)
574  ereport(ERROR,
575  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
576  errmsg("storage type cannot be different from data type for access method \"%s\"",
577  stmt->amname)));
578  }
579 
580  rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
581 
582  /*
583  * Make sure there is no existing opclass of this name (this is just to
584  * give a more friendly error message than "duplicate key").
585  */
587  ObjectIdGetDatum(amoid),
588  CStringGetDatum(opcname),
589  ObjectIdGetDatum(namespaceoid)))
590  ereport(ERROR,
592  errmsg("operator class \"%s\" for access method \"%s\" already exists",
593  opcname, stmt->amname)));
594 
595  /*
596  * If we are creating a default opclass, check there isn't one already.
597  * (Note we do not restrict this test to visible opclasses; this ensures
598  * that typcache.c can find unique solutions to its questions.)
599  */
600  if (stmt->isDefault)
601  {
602  ScanKeyData skey[1];
603  SysScanDesc scan;
604 
605  ScanKeyInit(&skey[0],
606  Anum_pg_opclass_opcmethod,
607  BTEqualStrategyNumber, F_OIDEQ,
608  ObjectIdGetDatum(amoid));
609 
611  NULL, 1, skey);
612 
613  while (HeapTupleIsValid(tup = systable_getnext(scan)))
614  {
615  Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
616 
617  if (opclass->opcintype == typeoid && opclass->opcdefault)
618  ereport(ERROR,
620  errmsg("could not make operator class \"%s\" be default for type %s",
621  opcname,
622  TypeNameToString(stmt->datatype)),
623  errdetail("Operator class \"%s\" already is the default.",
624  NameStr(opclass->opcname))));
625  }
626 
627  systable_endscan(scan);
628  }
629 
630  /*
631  * Okay, let's create the pg_opclass entry.
632  */
633  memset(values, 0, sizeof(values));
634  memset(nulls, false, sizeof(nulls));
635 
636  values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
637  namestrcpy(&opcName, opcname);
638  values[Anum_pg_opclass_opcname - 1] = NameGetDatum(&opcName);
639  values[Anum_pg_opclass_opcnamespace - 1] = ObjectIdGetDatum(namespaceoid);
640  values[Anum_pg_opclass_opcowner - 1] = ObjectIdGetDatum(GetUserId());
641  values[Anum_pg_opclass_opcfamily - 1] = ObjectIdGetDatum(opfamilyoid);
642  values[Anum_pg_opclass_opcintype - 1] = ObjectIdGetDatum(typeoid);
643  values[Anum_pg_opclass_opcdefault - 1] = BoolGetDatum(stmt->isDefault);
644  values[Anum_pg_opclass_opckeytype - 1] = ObjectIdGetDatum(storageoid);
645 
646  tup = heap_form_tuple(rel->rd_att, values, nulls);
647 
648  opclassoid = CatalogTupleInsert(rel, tup);
649 
650  heap_freetuple(tup);
651 
652  /*
653  * Now add tuples to pg_amop and pg_amproc tying in the operators and
654  * functions. Dependencies on them are inserted, too.
655  */
656  storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
657  opclassoid, operators, false);
658  storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
659  opclassoid, procedures, false);
660 
661  /* let event triggers know what happened */
662  EventTriggerCollectCreateOpClass(stmt, opclassoid, operators, procedures);
663 
664  /*
665  * Create dependencies for the opclass proper. Note: we do not need a
666  * dependency link to the AM, because that exists through the opfamily.
667  */
668  myself.classId = OperatorClassRelationId;
669  myself.objectId = opclassoid;
670  myself.objectSubId = 0;
671 
672  /* dependency on namespace */
673  referenced.classId = NamespaceRelationId;
674  referenced.objectId = namespaceoid;
675  referenced.objectSubId = 0;
676  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
677 
678  /* dependency on opfamily */
679  referenced.classId = OperatorFamilyRelationId;
680  referenced.objectId = opfamilyoid;
681  referenced.objectSubId = 0;
682  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
683 
684  /* dependency on indexed datatype */
685  referenced.classId = TypeRelationId;
686  referenced.objectId = typeoid;
687  referenced.objectSubId = 0;
688  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
689 
690  /* dependency on storage datatype */
691  if (OidIsValid(storageoid))
692  {
693  referenced.classId = TypeRelationId;
694  referenced.objectId = storageoid;
695  referenced.objectSubId = 0;
696  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
697  }
698 
699  /* dependency on owner */
700  recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
701 
702  /* dependency on extension */
703  recordDependencyOnCurrentExtension(&myself, false);
704 
705  /* Post creation hook for new operator class */
706  InvokeObjectPostCreateHook(OperatorClassRelationId, opclassoid, 0);
707 
709 
710  return myself;
711 }
712 
713 
714 /*
715  * DefineOpFamily
716  * Define a new index operator family.
717  */
720 {
721  char *opfname; /* name of opfamily we're creating */
722  Oid amoid, /* our AM's oid */
723  namespaceoid; /* namespace to create opfamily in */
724  AclResult aclresult;
725 
726  /* Convert list of names to a name and namespace */
727  namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
728  &opfname);
729 
730  /* Check we have creation rights in target namespace */
731  aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
732  if (aclresult != ACLCHECK_OK)
733  aclcheck_error(aclresult, OBJECT_SCHEMA,
734  get_namespace_name(namespaceoid));
735 
736  /* Get access method OID, throwing an error if it doesn't exist. */
737  amoid = get_index_am_oid(stmt->amname, false);
738 
739  /* XXX Should we make any privilege check against the AM? */
740 
741  /*
742  * Currently, we require superuser privileges to create an opfamily. See
743  * comments in DefineOpClass.
744  */
745  if (!superuser())
746  ereport(ERROR,
747  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
748  errmsg("must be superuser to create an operator family")));
749 
750  /* Insert pg_opfamily catalog entry */
751  return CreateOpFamily(stmt->amname, opfname, namespaceoid, amoid);
752 }
753 
754 
755 /*
756  * AlterOpFamily
757  * Add or remove operators/procedures within an existing operator family.
758  *
759  * Note: this implements only ALTER OPERATOR FAMILY ... ADD/DROP. Some
760  * other commands called ALTER OPERATOR FAMILY exist, but go through
761  * different code paths.
762  */
763 Oid
765 {
766  Oid amoid, /* our AM's oid */
767  opfamilyoid; /* oid of opfamily */
768  int maxOpNumber, /* amstrategies value */
769  maxProcNumber; /* amsupport value */
770  HeapTuple tup;
771  IndexAmRoutine *amroutine;
772 
773  /* Get necessary info about access method */
775  if (!HeapTupleIsValid(tup))
776  ereport(ERROR,
777  (errcode(ERRCODE_UNDEFINED_OBJECT),
778  errmsg("access method \"%s\" does not exist",
779  stmt->amname)));
780 
781  amoid = HeapTupleGetOid(tup);
782  amroutine = GetIndexAmRoutineByAmId(amoid, false);
783  ReleaseSysCache(tup);
784 
785  maxOpNumber = amroutine->amstrategies;
786  /* if amstrategies is zero, just enforce that op numbers fit in int16 */
787  if (maxOpNumber <= 0)
788  maxOpNumber = SHRT_MAX;
789  maxProcNumber = amroutine->amsupport;
790 
791  /* XXX Should we make any privilege check against the AM? */
792 
793  /* Look up the opfamily */
794  opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
795 
796  /*
797  * Currently, we require superuser privileges to alter an opfamily.
798  *
799  * XXX re-enable NOT_USED code sections below if you remove this test.
800  */
801  if (!superuser())
802  ereport(ERROR,
803  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
804  errmsg("must be superuser to alter an operator family")));
805 
806  /*
807  * ADD and DROP cases need separate code from here on down.
808  */
809  if (stmt->isDrop)
810  AlterOpFamilyDrop(stmt, amoid, opfamilyoid,
811  maxOpNumber, maxProcNumber, stmt->items);
812  else
813  AlterOpFamilyAdd(stmt, amoid, opfamilyoid,
814  maxOpNumber, maxProcNumber, stmt->items);
815 
816  return opfamilyoid;
817 }
818 
819 /*
820  * ADD part of ALTER OP FAMILY
821  */
822 static void
823 AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
824  int maxOpNumber, int maxProcNumber, List *items)
825 {
826  List *operators; /* OpFamilyMember list for operators */
827  List *procedures; /* OpFamilyMember list for support procs */
828  ListCell *l;
829 
830  operators = NIL;
831  procedures = NIL;
832 
833  /*
834  * Scan the "items" list to obtain additional info.
835  */
836  foreach(l, items)
837  {
839  Oid operOid;
840  Oid funcOid;
841  Oid sortfamilyOid;
843 
844  switch (item->itemtype)
845  {
847  if (item->number <= 0 || item->number > maxOpNumber)
848  ereport(ERROR,
849  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
850  errmsg("invalid operator number %d,"
851  " must be between 1 and %d",
852  item->number, maxOpNumber)));
853  if (item->name->objargs != NIL)
854  operOid = LookupOperWithArgs(item->name, false);
855  else
856  {
857  ereport(ERROR,
858  (errcode(ERRCODE_SYNTAX_ERROR),
859  errmsg("operator argument types must be specified in ALTER OPERATOR FAMILY")));
860  operOid = InvalidOid; /* keep compiler quiet */
861  }
862 
863  if (item->order_family)
864  sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
865  item->order_family,
866  false);
867  else
868  sortfamilyOid = InvalidOid;
869 
870 #ifdef NOT_USED
871  /* XXX this is unnecessary given the superuser check above */
872  /* Caller must own operator and its underlying function */
873  if (!pg_oper_ownercheck(operOid, GetUserId()))
875  get_opname(operOid));
876  funcOid = get_opcode(operOid);
877  if (!pg_proc_ownercheck(funcOid, GetUserId()))
879  get_func_name(funcOid));
880 #endif
881 
882  /* Save the info */
883  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
884  member->object = operOid;
885  member->number = item->number;
886  member->sortfamily = sortfamilyOid;
887  assignOperTypes(member, amoid, InvalidOid);
888  addFamilyMember(&operators, member, false);
889  break;
891  if (item->number <= 0 || item->number > maxProcNumber)
892  ereport(ERROR,
893  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
894  errmsg("invalid procedure number %d,"
895  " must be between 1 and %d",
896  item->number, maxProcNumber)));
897  funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
898 #ifdef NOT_USED
899  /* XXX this is unnecessary given the superuser check above */
900  /* Caller must own function */
901  if (!pg_proc_ownercheck(funcOid, GetUserId()))
903  get_func_name(funcOid));
904 #endif
905 
906  /* Save the info */
907  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
908  member->object = funcOid;
909  member->number = item->number;
910 
911  /* allow overriding of the function's actual arg types */
912  if (item->class_args)
914  &member->lefttype, &member->righttype);
915 
916  assignProcTypes(member, amoid, InvalidOid);
917  addFamilyMember(&procedures, member, true);
918  break;
920  ereport(ERROR,
921  (errcode(ERRCODE_SYNTAX_ERROR),
922  errmsg("STORAGE cannot be specified in ALTER OPERATOR FAMILY")));
923  break;
924  default:
925  elog(ERROR, "unrecognized item type: %d", item->itemtype);
926  break;
927  }
928  }
929 
930  /*
931  * Add tuples to pg_amop and pg_amproc tying in the operators and
932  * functions. Dependencies on them are inserted, too.
933  */
934  storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
935  InvalidOid, operators, true);
936  storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
937  InvalidOid, procedures, true);
938 
939  /* make information available to event triggers */
940  EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
941  operators, procedures);
942 }
943 
944 /*
945  * DROP part of ALTER OP FAMILY
946  */
947 static void
948 AlterOpFamilyDrop(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
949  int maxOpNumber, int maxProcNumber, List *items)
950 {
951  List *operators; /* OpFamilyMember list for operators */
952  List *procedures; /* OpFamilyMember list for support procs */
953  ListCell *l;
954 
955  operators = NIL;
956  procedures = NIL;
957 
958  /*
959  * Scan the "items" list to obtain additional info.
960  */
961  foreach(l, items)
962  {
964  Oid lefttype,
965  righttype;
967 
968  switch (item->itemtype)
969  {
971  if (item->number <= 0 || item->number > maxOpNumber)
972  ereport(ERROR,
973  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
974  errmsg("invalid operator number %d,"
975  " must be between 1 and %d",
976  item->number, maxOpNumber)));
977  processTypesSpec(item->class_args, &lefttype, &righttype);
978  /* Save the info */
979  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
980  member->number = item->number;
981  member->lefttype = lefttype;
982  member->righttype = righttype;
983  addFamilyMember(&operators, member, false);
984  break;
986  if (item->number <= 0 || item->number > maxProcNumber)
987  ereport(ERROR,
988  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
989  errmsg("invalid procedure number %d,"
990  " must be between 1 and %d",
991  item->number, maxProcNumber)));
992  processTypesSpec(item->class_args, &lefttype, &righttype);
993  /* Save the info */
994  member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
995  member->number = item->number;
996  member->lefttype = lefttype;
997  member->righttype = righttype;
998  addFamilyMember(&procedures, member, true);
999  break;
1001  /* grammar prevents this from appearing */
1002  default:
1003  elog(ERROR, "unrecognized item type: %d", item->itemtype);
1004  break;
1005  }
1006  }
1007 
1008  /*
1009  * Remove tuples from pg_amop and pg_amproc.
1010  */
1011  dropOperators(stmt->opfamilyname, amoid, opfamilyoid, operators);
1012  dropProcedures(stmt->opfamilyname, amoid, opfamilyoid, procedures);
1013 
1014  /* make information available to event triggers */
1015  EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
1016  operators, procedures);
1017 }
1018 
1019 
1020 /*
1021  * Deal with explicit arg types used in ALTER ADD/DROP
1022  */
1023 static void
1024 processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
1025 {
1026  TypeName *typeName;
1027 
1028  Assert(args != NIL);
1029 
1030  typeName = (TypeName *) linitial(args);
1031  *lefttype = typenameTypeId(NULL, typeName);
1032 
1033  if (list_length(args) > 1)
1034  {
1035  typeName = (TypeName *) lsecond(args);
1036  *righttype = typenameTypeId(NULL, typeName);
1037  }
1038  else
1039  *righttype = *lefttype;
1040 
1041  if (list_length(args) > 2)
1042  ereport(ERROR,
1043  (errcode(ERRCODE_SYNTAX_ERROR),
1044  errmsg("one or two argument types must be specified")));
1045 }
1046 
1047 
1048 /*
1049  * Determine the lefttype/righttype to assign to an operator,
1050  * and do any validity checking we can manage.
1051  */
1052 static void
1054 {
1055  Operator optup;
1056  Form_pg_operator opform;
1057 
1058  /* Fetch the operator definition */
1059  optup = SearchSysCache1(OPEROID, ObjectIdGetDatum(member->object));
1060  if (optup == NULL)
1061  elog(ERROR, "cache lookup failed for operator %u", member->object);
1062  opform = (Form_pg_operator) GETSTRUCT(optup);
1063 
1064  /*
1065  * Opfamily operators must be binary.
1066  */
1067  if (opform->oprkind != 'b')
1068  ereport(ERROR,
1069  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1070  errmsg("index operators must be binary")));
1071 
1072  if (OidIsValid(member->sortfamily))
1073  {
1074  /*
1075  * Ordering op, check index supports that. (We could perhaps also
1076  * check that the operator returns a type supported by the sortfamily,
1077  * but that seems more trouble than it's worth here. If it does not,
1078  * the operator will never be matchable to any ORDER BY clause, but no
1079  * worse consequences can ensue. Also, trying to check that would
1080  * create an ordering hazard during dump/reload: it's possible that
1081  * the family has been created but not yet populated with the required
1082  * operators.)
1083  */
1084  IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
1085 
1086  if (!amroutine->amcanorderbyop)
1087  ereport(ERROR,
1088  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1089  errmsg("access method \"%s\" does not support ordering operators",
1090  get_am_name(amoid))));
1091  }
1092  else
1093  {
1094  /*
1095  * Search operators must return boolean.
1096  */
1097  if (opform->oprresult != BOOLOID)
1098  ereport(ERROR,
1099  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1100  errmsg("index search operators must return boolean")));
1101  }
1102 
1103  /*
1104  * If lefttype/righttype isn't specified, use the operator's input types
1105  */
1106  if (!OidIsValid(member->lefttype))
1107  member->lefttype = opform->oprleft;
1108  if (!OidIsValid(member->righttype))
1109  member->righttype = opform->oprright;
1110 
1111  ReleaseSysCache(optup);
1112 }
1113 
1114 /*
1115  * Determine the lefttype/righttype to assign to a support procedure,
1116  * and do any validity checking we can manage.
1117  */
1118 static void
1120 {
1121  HeapTuple proctup;
1122  Form_pg_proc procform;
1123 
1124  /* Fetch the procedure definition */
1125  proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(member->object));
1126  if (proctup == NULL)
1127  elog(ERROR, "cache lookup failed for function %u", member->object);
1128  procform = (Form_pg_proc) GETSTRUCT(proctup);
1129 
1130  /*
1131  * btree comparison procs must be 2-arg procs returning int4. btree
1132  * sortsupport procs must take internal and return void. btree in_range
1133  * procs must be 5-arg procs returning bool. hash support proc 1 must be
1134  * a 1-arg proc returning int4, while proc 2 must be a 2-arg proc
1135  * returning int8. Otherwise we don't know.
1136  */
1137  if (amoid == BTREE_AM_OID)
1138  {
1139  if (member->number == BTORDER_PROC)
1140  {
1141  if (procform->pronargs != 2)
1142  ereport(ERROR,
1143  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1144  errmsg("btree comparison procedures must have two arguments")));
1145  if (procform->prorettype != INT4OID)
1146  ereport(ERROR,
1147  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1148  errmsg("btree comparison procedures must return integer")));
1149 
1150  /*
1151  * If lefttype/righttype isn't specified, use the proc's input
1152  * types
1153  */
1154  if (!OidIsValid(member->lefttype))
1155  member->lefttype = procform->proargtypes.values[0];
1156  if (!OidIsValid(member->righttype))
1157  member->righttype = procform->proargtypes.values[1];
1158  }
1159  else if (member->number == BTSORTSUPPORT_PROC)
1160  {
1161  if (procform->pronargs != 1 ||
1162  procform->proargtypes.values[0] != INTERNALOID)
1163  ereport(ERROR,
1164  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1165  errmsg("btree sort support procedures must accept type \"internal\"")));
1166  if (procform->prorettype != VOIDOID)
1167  ereport(ERROR,
1168  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1169  errmsg("btree sort support procedures must return void")));
1170 
1171  /*
1172  * Can't infer lefttype/righttype from proc, so use default rule
1173  */
1174  }
1175  else if (member->number == BTINRANGE_PROC)
1176  {
1177  if (procform->pronargs != 5)
1178  ereport(ERROR,
1179  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1180  errmsg("btree in_range procedures must have five arguments")));
1181  if (procform->prorettype != BOOLOID)
1182  ereport(ERROR,
1183  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1184  errmsg("btree in_range procedures must return boolean")));
1185 
1186  /*
1187  * If lefttype/righttype isn't specified, use the proc's input
1188  * types (we look at the test-value and offset arguments)
1189  */
1190  if (!OidIsValid(member->lefttype))
1191  member->lefttype = procform->proargtypes.values[0];
1192  if (!OidIsValid(member->righttype))
1193  member->righttype = procform->proargtypes.values[2];
1194  }
1195  }
1196  else if (amoid == HASH_AM_OID)
1197  {
1198  if (member->number == HASHSTANDARD_PROC)
1199  {
1200  if (procform->pronargs != 1)
1201  ereport(ERROR,
1202  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1203  errmsg("hash procedure 1 must have one argument")));
1204  if (procform->prorettype != INT4OID)
1205  ereport(ERROR,
1206  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1207  errmsg("hash procedure 1 must return integer")));
1208  }
1209  else if (member->number == HASHEXTENDED_PROC)
1210  {
1211  if (procform->pronargs != 2)
1212  ereport(ERROR,
1213  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1214  errmsg("hash procedure 2 must have two arguments")));
1215  if (procform->prorettype != INT8OID)
1216  ereport(ERROR,
1217  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1218  errmsg("hash procedure 2 must return bigint")));
1219  }
1220 
1221  /*
1222  * If lefttype/righttype isn't specified, use the proc's input type
1223  */
1224  if (!OidIsValid(member->lefttype))
1225  member->lefttype = procform->proargtypes.values[0];
1226  if (!OidIsValid(member->righttype))
1227  member->righttype = procform->proargtypes.values[0];
1228  }
1229 
1230  /*
1231  * The default in CREATE OPERATOR CLASS is to use the class' opcintype as
1232  * lefttype and righttype. In CREATE or ALTER OPERATOR FAMILY, opcintype
1233  * isn't available, so make the user specify the types.
1234  */
1235  if (!OidIsValid(member->lefttype))
1236  member->lefttype = typeoid;
1237  if (!OidIsValid(member->righttype))
1238  member->righttype = typeoid;
1239 
1240  if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
1241  ereport(ERROR,
1242  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1243  errmsg("associated data types must be specified for index support procedure")));
1244 
1245  ReleaseSysCache(proctup);
1246 }
1247 
1248 /*
1249  * Add a new family member to the appropriate list, after checking for
1250  * duplicated strategy or proc number.
1251  */
1252 static void
1254 {
1255  ListCell *l;
1256 
1257  foreach(l, *list)
1258  {
1259  OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
1260 
1261  if (old->number == member->number &&
1262  old->lefttype == member->lefttype &&
1263  old->righttype == member->righttype)
1264  {
1265  if (isProc)
1266  ereport(ERROR,
1267  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1268  errmsg("procedure number %d for (%s,%s) appears more than once",
1269  member->number,
1270  format_type_be(member->lefttype),
1271  format_type_be(member->righttype))));
1272  else
1273  ereport(ERROR,
1274  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1275  errmsg("operator number %d for (%s,%s) appears more than once",
1276  member->number,
1277  format_type_be(member->lefttype),
1278  format_type_be(member->righttype))));
1279  }
1280  }
1281  *list = lappend(*list, member);
1282 }
1283 
1284 /*
1285  * Dump the operators to pg_amop
1286  *
1287  * We also make dependency entries in pg_depend for the opfamily entries.
1288  * If opclassoid is valid then make an INTERNAL dependency on that opclass,
1289  * else make an AUTO dependency on the opfamily.
1290  */
1291 static void
1292 storeOperators(List *opfamilyname, Oid amoid,
1293  Oid opfamilyoid, Oid opclassoid,
1294  List *operators, bool isAdd)
1295 {
1296  Relation rel;
1297  Datum values[Natts_pg_amop];
1298  bool nulls[Natts_pg_amop];
1299  HeapTuple tup;
1300  Oid entryoid;
1301  ObjectAddress myself,
1302  referenced;
1303  ListCell *l;
1304 
1305  rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
1306 
1307  foreach(l, operators)
1308  {
1309  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1310  char oppurpose;
1311 
1312  /*
1313  * If adding to an existing family, check for conflict with an
1314  * existing pg_amop entry (just to give a nicer error message)
1315  */
1316  if (isAdd &&
1318  ObjectIdGetDatum(opfamilyoid),
1321  Int16GetDatum(op->number)))
1322  ereport(ERROR,
1324  errmsg("operator %d(%s,%s) already exists in operator family \"%s\"",
1325  op->number,
1326  format_type_be(op->lefttype),
1328  NameListToString(opfamilyname))));
1329 
1330  oppurpose = OidIsValid(op->sortfamily) ? AMOP_ORDER : AMOP_SEARCH;
1331 
1332  /* Create the pg_amop entry */
1333  memset(values, 0, sizeof(values));
1334  memset(nulls, false, sizeof(nulls));
1335 
1336  values[Anum_pg_amop_amopfamily - 1] = ObjectIdGetDatum(opfamilyoid);
1337  values[Anum_pg_amop_amoplefttype - 1] = ObjectIdGetDatum(op->lefttype);
1338  values[Anum_pg_amop_amoprighttype - 1] = ObjectIdGetDatum(op->righttype);
1339  values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
1340  values[Anum_pg_amop_amoppurpose - 1] = CharGetDatum(oppurpose);
1341  values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
1342  values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
1343  values[Anum_pg_amop_amopsortfamily - 1] = ObjectIdGetDatum(op->sortfamily);
1344 
1345  tup = heap_form_tuple(rel->rd_att, values, nulls);
1346 
1347  entryoid = CatalogTupleInsert(rel, tup);
1348 
1349  heap_freetuple(tup);
1350 
1351  /* Make its dependencies */
1352  myself.classId = AccessMethodOperatorRelationId;
1353  myself.objectId = entryoid;
1354  myself.objectSubId = 0;
1355 
1356  referenced.classId = OperatorRelationId;
1357  referenced.objectId = op->object;
1358  referenced.objectSubId = 0;
1359 
1360  if (OidIsValid(opclassoid))
1361  {
1362  /* if contained in an opclass, use a NORMAL dep on operator */
1363  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1364 
1365  /* ... and an INTERNAL dep on the opclass */
1366  referenced.classId = OperatorClassRelationId;
1367  referenced.objectId = opclassoid;
1368  referenced.objectSubId = 0;
1369  recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1370  }
1371  else
1372  {
1373  /* if "loose" in the opfamily, use a AUTO dep on operator */
1374  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1375 
1376  /* ... and an AUTO dep on the opfamily */
1377  referenced.classId = OperatorFamilyRelationId;
1378  referenced.objectId = opfamilyoid;
1379  referenced.objectSubId = 0;
1380  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1381  }
1382 
1383  /* A search operator also needs a dep on the referenced opfamily */
1384  if (OidIsValid(op->sortfamily))
1385  {
1386  referenced.classId = OperatorFamilyRelationId;
1387  referenced.objectId = op->sortfamily;
1388  referenced.objectSubId = 0;
1389  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1390  }
1391  /* Post create hook of this access method operator */
1392  InvokeObjectPostCreateHook(AccessMethodOperatorRelationId,
1393  entryoid, 0);
1394  }
1395 
1397 }
1398 
1399 /*
1400  * Dump the procedures (support routines) to pg_amproc
1401  *
1402  * We also make dependency entries in pg_depend for the opfamily entries.
1403  * If opclassoid is valid then make an INTERNAL dependency on that opclass,
1404  * else make an AUTO dependency on the opfamily.
1405  */
1406 static void
1407 storeProcedures(List *opfamilyname, Oid amoid,
1408  Oid opfamilyoid, Oid opclassoid,
1409  List *procedures, bool isAdd)
1410 {
1411  Relation rel;
1412  Datum values[Natts_pg_amproc];
1413  bool nulls[Natts_pg_amproc];
1414  HeapTuple tup;
1415  Oid entryoid;
1416  ObjectAddress myself,
1417  referenced;
1418  ListCell *l;
1419 
1420  rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
1421 
1422  foreach(l, procedures)
1423  {
1424  OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
1425 
1426  /*
1427  * If adding to an existing family, check for conflict with an
1428  * existing pg_amproc entry (just to give a nicer error message)
1429  */
1430  if (isAdd &&
1432  ObjectIdGetDatum(opfamilyoid),
1433  ObjectIdGetDatum(proc->lefttype),
1434  ObjectIdGetDatum(proc->righttype),
1435  Int16GetDatum(proc->number)))
1436  ereport(ERROR,
1438  errmsg("function %d(%s,%s) already exists in operator family \"%s\"",
1439  proc->number,
1440  format_type_be(proc->lefttype),
1441  format_type_be(proc->righttype),
1442  NameListToString(opfamilyname))));
1443 
1444  /* Create the pg_amproc entry */
1445  memset(values, 0, sizeof(values));
1446  memset(nulls, false, sizeof(nulls));
1447 
1448  values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
1449  values[Anum_pg_amproc_amproclefttype - 1] = ObjectIdGetDatum(proc->lefttype);
1450  values[Anum_pg_amproc_amprocrighttype - 1] = ObjectIdGetDatum(proc->righttype);
1451  values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
1452  values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);
1453 
1454  tup = heap_form_tuple(rel->rd_att, values, nulls);
1455 
1456  entryoid = CatalogTupleInsert(rel, tup);
1457 
1458  heap_freetuple(tup);
1459 
1460  /* Make its dependencies */
1461  myself.classId = AccessMethodProcedureRelationId;
1462  myself.objectId = entryoid;
1463  myself.objectSubId = 0;
1464 
1465  referenced.classId = ProcedureRelationId;
1466  referenced.objectId = proc->object;
1467  referenced.objectSubId = 0;
1468 
1469  if (OidIsValid(opclassoid))
1470  {
1471  /* if contained in an opclass, use a NORMAL dep on procedure */
1472  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1473 
1474  /* ... and an INTERNAL dep on the opclass */
1475  referenced.classId = OperatorClassRelationId;
1476  referenced.objectId = opclassoid;
1477  referenced.objectSubId = 0;
1478  recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1479  }
1480  else
1481  {
1482  /* if "loose" in the opfamily, use a AUTO dep on procedure */
1483  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1484 
1485  /* ... and an AUTO dep on the opfamily */
1486  referenced.classId = OperatorFamilyRelationId;
1487  referenced.objectId = opfamilyoid;
1488  referenced.objectSubId = 0;
1489  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1490  }
1491  /* Post create hook of access method procedure */
1492  InvokeObjectPostCreateHook(AccessMethodProcedureRelationId,
1493  entryoid, 0);
1494  }
1495 
1497 }
1498 
1499 
1500 /*
1501  * Remove operator entries from an opfamily.
1502  *
1503  * Note: this is only allowed for "loose" members of an opfamily, hence
1504  * behavior is always RESTRICT.
1505  */
1506 static void
1507 dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
1508  List *operators)
1509 {
1510  ListCell *l;
1511 
1512  foreach(l, operators)
1513  {
1514  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1515  Oid amopid;
1516  ObjectAddress object;
1517 
1518  amopid = GetSysCacheOid4(AMOPSTRATEGY,
1519  ObjectIdGetDatum(opfamilyoid),
1522  Int16GetDatum(op->number));
1523  if (!OidIsValid(amopid))
1524  ereport(ERROR,
1525  (errcode(ERRCODE_UNDEFINED_OBJECT),
1526  errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"",
1527  op->number,
1528  format_type_be(op->lefttype),
1530  NameListToString(opfamilyname))));
1531 
1532  object.classId = AccessMethodOperatorRelationId;
1533  object.objectId = amopid;
1534  object.objectSubId = 0;
1535 
1536  performDeletion(&object, DROP_RESTRICT, 0);
1537  }
1538 }
1539 
1540 /*
1541  * Remove procedure entries from an opfamily.
1542  *
1543  * Note: this is only allowed for "loose" members of an opfamily, hence
1544  * behavior is always RESTRICT.
1545  */
1546 static void
1547 dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
1548  List *procedures)
1549 {
1550  ListCell *l;
1551 
1552  foreach(l, procedures)
1553  {
1554  OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1555  Oid amprocid;
1556  ObjectAddress object;
1557 
1558  amprocid = GetSysCacheOid4(AMPROCNUM,
1559  ObjectIdGetDatum(opfamilyoid),
1562  Int16GetDatum(op->number));
1563  if (!OidIsValid(amprocid))
1564  ereport(ERROR,
1565  (errcode(ERRCODE_UNDEFINED_OBJECT),
1566  errmsg("function %d(%s,%s) does not exist in operator family \"%s\"",
1567  op->number,
1568  format_type_be(op->lefttype),
1570  NameListToString(opfamilyname))));
1571 
1572  object.classId = AccessMethodProcedureRelationId;
1573  object.objectId = amprocid;
1574  object.objectSubId = 0;
1575 
1576  performDeletion(&object, DROP_RESTRICT, 0);
1577  }
1578 }
1579 
1580 /*
1581  * Deletion subroutines for use by dependency.c.
1582  */
1583 void
1585 {
1586  Relation rel;
1587  HeapTuple tup;
1588 
1589  rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
1590 
1591  tup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyOid));
1592  if (!HeapTupleIsValid(tup)) /* should not happen */
1593  elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);
1594 
1595  CatalogTupleDelete(rel, &tup->t_self);
1596 
1597  ReleaseSysCache(tup);
1598 
1600 }
1601 
1602 void
1604 {
1605  Relation rel;
1606  HeapTuple tup;
1607 
1608  rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
1609 
1610  tup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassOid));
1611  if (!HeapTupleIsValid(tup)) /* should not happen */
1612  elog(ERROR, "cache lookup failed for opclass %u", opclassOid);
1613 
1614  CatalogTupleDelete(rel, &tup->t_self);
1615 
1616  ReleaseSysCache(tup);
1617 
1619 }
1620 
1621 void
1623 {
1624  Relation rel;
1625  HeapTuple tup;
1626  ScanKeyData skey[1];
1627  SysScanDesc scan;
1628 
1629  ScanKeyInit(&skey[0],
1631  BTEqualStrategyNumber, F_OIDEQ,
1632  ObjectIdGetDatum(entryOid));
1633 
1634  rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
1635 
1637  NULL, 1, skey);
1638 
1639  /* we expect exactly one match */
1640  tup = systable_getnext(scan);
1641  if (!HeapTupleIsValid(tup))
1642  elog(ERROR, "could not find tuple for amop entry %u", entryOid);
1643 
1644  CatalogTupleDelete(rel, &tup->t_self);
1645 
1646  systable_endscan(scan);
1648 }
1649 
1650 void
1652 {
1653  Relation rel;
1654  HeapTuple tup;
1655  ScanKeyData skey[1];
1656  SysScanDesc scan;
1657 
1658  ScanKeyInit(&skey[0],
1660  BTEqualStrategyNumber, F_OIDEQ,
1661  ObjectIdGetDatum(entryOid));
1662 
1663  rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
1664 
1666  NULL, 1, skey);
1667 
1668  /* we expect exactly one match */
1669  tup = systable_getnext(scan);
1670  if (!HeapTupleIsValid(tup))
1671  elog(ERROR, "could not find tuple for amproc entry %u", entryOid);
1672 
1673  CatalogTupleDelete(rel, &tup->t_self);
1674 
1675  systable_endscan(scan);
1677 }
1678 
1679 /*
1680  * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
1681  *
1682  * Is there an operator class with the given name and signature already
1683  * in the given namespace? If so, raise an appropriate error message.
1684  */
1685 void
1686 IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
1687  Oid opcnamespace)
1688 {
1689  /* make sure the new name doesn't exist */
1691  ObjectIdGetDatum(opcmethod),
1692  CStringGetDatum(opcname),
1693  ObjectIdGetDatum(opcnamespace)))
1694  ereport(ERROR,
1696  errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1697  opcname,
1698  get_am_name(opcmethod),
1699  get_namespace_name(opcnamespace))));
1700 }
1701 
1702 /*
1703  * Subroutine for ALTER OPERATOR FAMILY SET SCHEMA/RENAME
1704  *
1705  * Is there an operator family with the given name and signature already
1706  * in the given namespace? If so, raise an appropriate error message.
1707  */
1708 void
1709 IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
1710  Oid opfnamespace)
1711 {
1712  /* make sure the new name doesn't exist */
1714  ObjectIdGetDatum(opfmethod),
1715  CStringGetDatum(opfname),
1716  ObjectIdGetDatum(opfnamespace)))
1717  ereport(ERROR,
1719  errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
1720  opfname,
1721  get_am_name(opfmethod),
1722  get_namespace_name(opfnamespace))));
1723 }
Oid LookupOperWithArgs(ObjectWithArgs *oper, bool noError)
Definition: parse_oper.c:140
#define NIL
Definition: pg_list.h:69
#define GetSysCacheOid4(cacheId, key1, key2, key3, key4)
Definition: syscache.h:197
#define NameGetDatum(X)
Definition: postgres.h:578
uint16 amsupport
Definition: amapi.h:170
#define BTORDER_PROC
Definition: nbtree.h:290
Oid LookupExplicitNamespace(const char *nspname, bool missing_ok)
Definition: namespace.c:2872
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:502
#define GETSTRUCT(TUP)
Definition: htup_details.h:673
static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
Definition: opclasscmds.c:1053
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:145
Definition: syscache.h:36
#define BTSORTSUPPORT_PROC
Definition: nbtree.h:291
void RemoveOpClassById(Oid opclassOid)
Definition: opclasscmds.c:1603
Oid GetUserId(void)
Definition: miscinit.c:379
TypeName * storedtype
Definition: parsenodes.h:2557
#define ObjectIdAttributeNumber
Definition: sysattr.h:22
bool amcanorderbyop
Definition: amapi.h:174
Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p)
Definition: namespace.c:2975
#define PointerGetDatum(X)
Definition: postgres.h:539
char * TypeNameToString(const TypeName *typeName)
Definition: parse_type.c:459
static HeapTuple OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok)
Definition: opclasscmds.c:163
bool amstorage
Definition: amapi.h:188
#define Int16GetDatum(X)
Definition: postgres.h:434
#define SearchSysCacheExists4(cacheId, key1, key2, key3, key4)
Definition: syscache.h:188
#define AccessMethodOperatorOidIndexId
Definition: indexing.h:79
bool pg_oper_ownercheck(Oid oper_oid, Oid roleid)
Definition: aclchk.c:4800
int errcode(int sqlerrcode)
Definition: elog.c:575
static HeapTuple OpFamilyCacheLookup(Oid amID, List *opfamilyname, bool missing_ok)
Definition: opclasscmds.c:84
static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, List *procedures)
Definition: opclasscmds.c:1547
#define HASHEXTENDED_PROC
Definition: hash.h:352
bool superuser(void)
Definition: superuser.c:47
char * format_type_be(Oid type_oid)
Definition: format_type.c:328
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:256
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
void DeconstructQualifiedName(List *names, char **nspname_p, char **objname_p)
Definition: namespace.c:2788
Oid LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright, bool noError, int location)
Definition: parse_oper.c:102
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1074
#define heap_close(r, l)
Definition: heapam.h:97
Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok)
Definition: opclasscmds.c:221
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:159
void RemoveAmProcEntryById(Oid entryOid)
Definition: opclasscmds.c:1651
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1773
unsigned int Oid
Definition: postgres_ext.h:31
bool pg_type_ownercheck(Oid type_oid, Oid roleid)
Definition: aclchk.c:4774
ObjectAddress DefineOpClass(CreateOpClassStmt *stmt)
Definition: opclasscmds.c:324
int namestrcpy(Name name, const char *str)
Definition: name.c:216
Oid OpclassnameGetOpcid(Oid amid, const char *opcname)
Definition: namespace.c:1779
#define OidIsValid(objectId)
Definition: c.h:605
AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4686
void IsThereOpClassInNamespace(const char *opcname, Oid opcmethod, Oid opcnamespace)
Definition: opclasscmds.c:1686
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:331
#define OpclassAmNameNspIndexId
Definition: indexing.h:200
#define lsecond(l)
Definition: pg_list.h:116
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:3659
#define AccessMethodProcedureOidIndexId
Definition: indexing.h:84
#define OPCLASS_ITEM_OPERATOR
Definition: parsenodes.h:2543
char * get_opname(Oid opno)
Definition: lsyscache.c:1104
static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, List *operators)
Definition: opclasscmds.c:1507
Oid member
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3348
Oid OpfamilynameGetOpfid(Oid amid, const char *opfname)
Definition: namespace.c:1862
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:419
static void AlterOpFamilyDrop(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, int maxOpNumber, int maxProcNumber, List *items)
Definition: opclasscmds.c:948
#define linitial(l)
Definition: pg_list.h:111
#define BTINRANGE_PROC
Definition: nbtree.h:292
#define ObjectIdGetDatum(X)
Definition: postgres.h:490
#define ERROR
Definition: elog.h:43
#define ACL_CREATE
Definition: parsenodes.h:84
#define SearchSysCacheExists3(cacheId, key1, key2, key3)
Definition: syscache.h:186
Oid CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:163
IndexAmRoutine * GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
Definition: amapi.c:56
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1397
HeapTuple SearchSysCache3(int cacheId, Datum key1, Datum key2, Datum key3)
Definition: syscache.c:1134
ItemPointerData t_self
Definition: htup.h:65
#define lfirst_node(type, lc)
Definition: pg_list.h:109
Definition: c.h:570
TypeName * datatype
Definition: parsenodes.h:2538
void RemoveOpFamilyById(Oid opfamilyOid)
Definition: opclasscmds.c:1584
ObjectWithArgs * name
Definition: parsenodes.h:2551
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3051
Oid get_index_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:183
char * get_am_name(Oid amOid)
Definition: amcmds.c:202
Oid LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
Definition: parse_func.c:2041
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:873
static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
Definition: opclasscmds.c:1253
#define CStringGetDatum(X)
Definition: postgres.h:561
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:300
#define HASHSTANDARD_PROC
Definition: hash.h:351
#define ereport(elevel, rest)
Definition: elog.h:122
static ObjectAddress CreateOpFamily(const char *amname, const char *opfname, Oid namespaceoid, Oid amoid)
Definition: opclasscmds.c:242
void EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid, List *operators, List *procedures)
List * lappend(List *list, void *datum)
Definition: list.c:128
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
char * NameListToString(List *names)
Definition: namespace.c:3082
void RemoveAmOpEntryById(Oid entryOid)
Definition: opclasscmds.c:1622
static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
Definition: opclasscmds.c:1024
void * palloc0(Size size)
Definition: mcxt.c:955
AclResult
Definition: acl.h:178
uintptr_t Datum
Definition: postgres.h:365
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1294
TupleDesc rd_att
Definition: rel.h:85
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:132
#define BoolGetDatum(X)
Definition: postgres.h:385
#define InvalidOid
Definition: postgres_ext.h:36
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1079
#define OPCLASS_ITEM_FUNCTION
Definition: parsenodes.h:2544
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
void recordDependencyOnCurrentExtension(const ObjectAddress *object, bool isReplace)
Definition: pg_depend.c:139
static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid, int maxOpNumber, int maxProcNumber, List *items)
Definition: opclasscmds.c:823
#define OPCLASS_ITEM_STORAGETYPE
Definition: parsenodes.h:2545
void EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid, List *operators, List *procedures)
Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok)
Definition: opclasscmds.c:142
static int list_length(const List *l)
Definition: pg_list.h:89
static void storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid, Oid opclassoid, List *procedures, bool isAdd)
Definition: opclasscmds.c:1407
FormData_pg_operator * Form_pg_operator
Definition: pg_operator.h:82
#define CharGetDatum(X)
Definition: postgres.h:399
Oid AlterOpFamily(AlterOpFamilyStmt *stmt)
Definition: opclasscmds.c:764
ObjectAddress DefineOpFamily(CreateOpFamilyStmt *stmt)
Definition: opclasscmds.c:719
uint16 amstrategies
Definition: amapi.h:168
static Datum values[MAXATTR]
Definition: bootstrap.c:164
FormData_pg_am * Form_pg_am
Definition: pg_am.h:46
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define NameStr(name)
Definition: c.h:576
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define elog
Definition: elog.h:219
#define HeapTupleGetOid(tuple)
Definition: htup_details.h:712
static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
Definition: opclasscmds.c:1119
static void storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid, Oid opclassoid, List *operators, bool isAdd)
Definition: opclasscmds.c:1292
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:81
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
Definition: pg_list.h:45
#define BTEqualStrategyNumber
Definition: stratnum.h:31
bool pg_proc_ownercheck(Oid proc_oid, Oid roleid)
Definition: aclchk.c:4826
void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod, Oid opfnamespace)
Definition: opclasscmds.c:1709
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:274