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