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
OpFamilyCacheLookup(Oid amID,List * opfamilyname,bool missing_ok)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
102 			htup = SearchSysCache3(OPFAMILYAMNAMENSP,
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
115 			htup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfID));
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
get_opfamily_oid(Oid amID,List * opfamilyname,bool missing_ok)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
OpClassCacheLookup(Oid amID,List * opclassname,bool missing_ok)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
181 			htup = SearchSysCache3(CLAAMNAMENSP,
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
get_opclass_oid(Oid amID,List * opclassname,bool missing_ok)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
CreateOpFamily(const char * amname,const char * opfname,Oid namespaceoid,Oid amoid)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 	 */
259 	if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
260 							  ObjectIdGetDatum(amoid),
261 							  CStringGetDatum(opfname),
262 							  ObjectIdGetDatum(namespaceoid)))
263 		ereport(ERROR,
264 				(errcode(ERRCODE_DUPLICATE_OBJECT),
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 
314 	heap_close(rel, RowExclusiveLock);
315 
316 	return myself;
317 }
318 
319 /*
320  * DefineOpClass
321  *		Define a new index operator class.
322  */
323 ObjectAddress
DefineOpClass(CreateOpClassStmt * stmt)324 DefineOpClass(CreateOpClassStmt *stmt)
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 */
360 	tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
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()))
412 		aclcheck_error_type(ACLCHECK_NOT_OWNER, typeoid);
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 */
426 		tup = SearchSysCache3(OPFAMILYAMNAMENSP,
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 	{
464 		CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
465 		Oid			operOid;
466 		Oid			funcOid;
467 		Oid			sortfamilyOid;
468 		OpFamilyMember *member;
469 
470 		switch (item->itemtype)
471 		{
472 			case OPCLASS_ITEM_OPERATOR:
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()))
500 					aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
501 								   get_opname(operOid));
502 				funcOid = get_opcode(operOid);
503 				if (!pg_proc_ownercheck(funcOid, GetUserId()))
504 					aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
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;
516 			case OPCLASS_ITEM_FUNCTION:
517 				if (item->number <= 0 || item->number > maxProcNumber)
518 					ereport(ERROR,
519 							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
520 							 errmsg("invalid function 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()))
528 					aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
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)
539 					processTypesSpec(item->class_args,
540 									 &member->lefttype, &member->righttype);
541 
542 				assignProcTypes(member, amoid, typeoid);
543 				addFamilyMember(&procedures, member, true);
544 				break;
545 			case OPCLASS_ITEM_STORAGETYPE:
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()))
556 					aclcheck_error_type(ACLCHECK_NOT_OWNER, storageoid);
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 	 */
586 	if (SearchSysCacheExists3(CLAAMNAMENSP,
587 							  ObjectIdGetDatum(amoid),
588 							  CStringGetDatum(opcname),
589 							  ObjectIdGetDatum(namespaceoid)))
590 		ereport(ERROR,
591 				(errcode(ERRCODE_DUPLICATE_OBJECT),
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 
610 		scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
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,
619 						(errcode(ERRCODE_DUPLICATE_OBJECT),
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 
708 	heap_close(rel, RowExclusiveLock);
709 
710 	return myself;
711 }
712 
713 
714 /*
715  * DefineOpFamily
716  *		Define a new index operator family.
717  */
718 ObjectAddress
DefineOpFamily(CreateOpFamilyStmt * stmt)719 DefineOpFamily(CreateOpFamilyStmt *stmt)
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
AlterOpFamily(AlterOpFamilyStmt * stmt)764 AlterOpFamily(AlterOpFamilyStmt *stmt)
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 */
774 	tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
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
AlterOpFamilyAdd(AlterOpFamilyStmt * stmt,Oid amoid,Oid opfamilyoid,int maxOpNumber,int maxProcNumber,List * items)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 	{
838 		CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
839 		Oid			operOid;
840 		Oid			funcOid;
841 		Oid			sortfamilyOid;
842 		OpFamilyMember *member;
843 
844 		switch (item->itemtype)
845 		{
846 			case OPCLASS_ITEM_OPERATOR:
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()))
874 					aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
875 								   get_opname(operOid));
876 				funcOid = get_opcode(operOid);
877 				if (!pg_proc_ownercheck(funcOid, GetUserId()))
878 					aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
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;
890 			case OPCLASS_ITEM_FUNCTION:
891 				if (item->number <= 0 || item->number > maxProcNumber)
892 					ereport(ERROR,
893 							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
894 							 errmsg("invalid function 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()))
902 					aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
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)
913 					processTypesSpec(item->class_args,
914 									 &member->lefttype, &member->righttype);
915 
916 				assignProcTypes(member, amoid, InvalidOid);
917 				addFamilyMember(&procedures, member, true);
918 				break;
919 			case OPCLASS_ITEM_STORAGETYPE:
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
AlterOpFamilyDrop(AlterOpFamilyStmt * stmt,Oid amoid,Oid opfamilyoid,int maxOpNumber,int maxProcNumber,List * items)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 	{
963 		CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
964 		Oid			lefttype,
965 					righttype;
966 		OpFamilyMember *member;
967 
968 		switch (item->itemtype)
969 		{
970 			case OPCLASS_ITEM_OPERATOR:
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;
985 			case OPCLASS_ITEM_FUNCTION:
986 				if (item->number <= 0 || item->number > maxProcNumber)
987 					ereport(ERROR,
988 							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
989 							 errmsg("invalid function 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;
1000 			case OPCLASS_ITEM_STORAGETYPE:
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
processTypesSpec(List * args,Oid * lefttype,Oid * righttype)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
assignOperTypes(OpFamilyMember * member,Oid amoid,Oid typeoid)1053 assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
1054 {
1055 	Operator	optup;
1056 	Form_pg_operator opform;
1057 
1058 	/* Fetch the operator definition */
1059 	optup = SearchSysCache1(OPEROID, ObjectIdGetDatum(member->object));
1060 	if (!HeapTupleIsValid(optup))
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
assignProcTypes(OpFamilyMember * member,Oid amoid,Oid typeoid)1119 assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
1120 {
1121 	HeapTuple	proctup;
1122 	Form_pg_proc procform;
1123 
1124 	/* Fetch the procedure definition */
1125 	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(member->object));
1126 	if (!HeapTupleIsValid(proctup))
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 functions must have two arguments")));
1145 			if (procform->prorettype != INT4OID)
1146 				ereport(ERROR,
1147 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1148 						 errmsg("btree comparison functions 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 functions must accept type \"internal\"")));
1166 			if (procform->prorettype != VOIDOID)
1167 				ereport(ERROR,
1168 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1169 						 errmsg("btree sort support functions 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 functions must have five arguments")));
1181 			if (procform->prorettype != BOOLOID)
1182 				ereport(ERROR,
1183 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1184 						 errmsg("btree in_range functions 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 function 1 must have one argument")));
1204 			if (procform->prorettype != INT4OID)
1205 				ereport(ERROR,
1206 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1207 						 errmsg("hash function 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 function 2 must have two arguments")));
1215 			if (procform->prorettype != INT8OID)
1216 				ereport(ERROR,
1217 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1218 						 errmsg("hash function 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 function")));
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
addFamilyMember(List ** list,OpFamilyMember * member,bool isProc)1253 addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
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("function 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
storeOperators(List * opfamilyname,Oid amoid,Oid opfamilyoid,Oid opclassoid,List * operators,bool isAdd)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 &&
1317 			SearchSysCacheExists4(AMOPSTRATEGY,
1318 								  ObjectIdGetDatum(opfamilyoid),
1319 								  ObjectIdGetDatum(op->lefttype),
1320 								  ObjectIdGetDatum(op->righttype),
1321 								  Int16GetDatum(op->number)))
1322 			ereport(ERROR,
1323 					(errcode(ERRCODE_DUPLICATE_OBJECT),
1324 					 errmsg("operator %d(%s,%s) already exists in operator family \"%s\"",
1325 							op->number,
1326 							format_type_be(op->lefttype),
1327 							format_type_be(op->righttype),
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 
1396 	heap_close(rel, RowExclusiveLock);
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
storeProcedures(List * opfamilyname,Oid amoid,Oid opfamilyoid,Oid opclassoid,List * procedures,bool isAdd)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 &&
1431 			SearchSysCacheExists4(AMPROCNUM,
1432 								  ObjectIdGetDatum(opfamilyoid),
1433 								  ObjectIdGetDatum(proc->lefttype),
1434 								  ObjectIdGetDatum(proc->righttype),
1435 								  Int16GetDatum(proc->number)))
1436 			ereport(ERROR,
1437 					(errcode(ERRCODE_DUPLICATE_OBJECT),
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 
1496 	heap_close(rel, RowExclusiveLock);
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
dropOperators(List * opfamilyname,Oid amoid,Oid opfamilyoid,List * operators)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),
1520 								 ObjectIdGetDatum(op->lefttype),
1521 								 ObjectIdGetDatum(op->righttype),
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),
1529 							format_type_be(op->righttype),
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
dropProcedures(List * opfamilyname,Oid amoid,Oid opfamilyoid,List * procedures)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),
1560 								   ObjectIdGetDatum(op->lefttype),
1561 								   ObjectIdGetDatum(op->righttype),
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),
1569 							format_type_be(op->righttype),
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
RemoveOpFamilyById(Oid opfamilyOid)1584 RemoveOpFamilyById(Oid opfamilyOid)
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 
1599 	heap_close(rel, RowExclusiveLock);
1600 }
1601 
1602 void
RemoveOpClassById(Oid opclassOid)1603 RemoveOpClassById(Oid opclassOid)
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 
1618 	heap_close(rel, RowExclusiveLock);
1619 }
1620 
1621 void
RemoveAmOpEntryById(Oid entryOid)1622 RemoveAmOpEntryById(Oid entryOid)
1623 {
1624 	Relation	rel;
1625 	HeapTuple	tup;
1626 	ScanKeyData skey[1];
1627 	SysScanDesc scan;
1628 
1629 	ScanKeyInit(&skey[0],
1630 				ObjectIdAttributeNumber,
1631 				BTEqualStrategyNumber, F_OIDEQ,
1632 				ObjectIdGetDatum(entryOid));
1633 
1634 	rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
1635 
1636 	scan = systable_beginscan(rel, AccessMethodOperatorOidIndexId, true,
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);
1647 	heap_close(rel, RowExclusiveLock);
1648 }
1649 
1650 void
RemoveAmProcEntryById(Oid entryOid)1651 RemoveAmProcEntryById(Oid entryOid)
1652 {
1653 	Relation	rel;
1654 	HeapTuple	tup;
1655 	ScanKeyData skey[1];
1656 	SysScanDesc scan;
1657 
1658 	ScanKeyInit(&skey[0],
1659 				ObjectIdAttributeNumber,
1660 				BTEqualStrategyNumber, F_OIDEQ,
1661 				ObjectIdGetDatum(entryOid));
1662 
1663 	rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
1664 
1665 	scan = systable_beginscan(rel, AccessMethodProcedureOidIndexId, true,
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);
1676 	heap_close(rel, RowExclusiveLock);
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
IsThereOpClassInNamespace(const char * opcname,Oid opcmethod,Oid opcnamespace)1686 IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
1687 						  Oid opcnamespace)
1688 {
1689 	/* make sure the new name doesn't exist */
1690 	if (SearchSysCacheExists3(CLAAMNAMENSP,
1691 							  ObjectIdGetDatum(opcmethod),
1692 							  CStringGetDatum(opcname),
1693 							  ObjectIdGetDatum(opcnamespace)))
1694 		ereport(ERROR,
1695 				(errcode(ERRCODE_DUPLICATE_OBJECT),
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
IsThereOpFamilyInNamespace(const char * opfname,Oid opfmethod,Oid opfnamespace)1709 IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
1710 						   Oid opfnamespace)
1711 {
1712 	/* make sure the new name doesn't exist */
1713 	if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
1714 							  ObjectIdGetDatum(opfmethod),
1715 							  CStringGetDatum(opfname),
1716 							  ObjectIdGetDatum(opfnamespace)))
1717 		ereport(ERROR,
1718 				(errcode(ERRCODE_DUPLICATE_OBJECT),
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 }
1724