1 /*-------------------------------------------------------------------------
2  *
3  * typecmds.c
4  *	  Routines for SQL commands that manipulate types (and domains).
5  *
6  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *	  src/backend/commands/typecmds.c
12  *
13  * DESCRIPTION
14  *	  The "DefineFoo" routines take the parse tree and pick out the
15  *	  appropriate arguments/flags, passing the results to the
16  *	  corresponding "FooDefine" routines (in src/catalog) that do
17  *	  the actual catalog-munging.  These routines also verify permission
18  *	  of the user to execute the command.
19  *
20  * NOTES
21  *	  These things must be defined and committed in the following order:
22  *		"create function":
23  *				input/output, recv/send functions
24  *		"create type":
25  *				type
26  *		"create operator":
27  *				operators
28  *
29  *
30  *-------------------------------------------------------------------------
31  */
32 #include "postgres.h"
33 
34 #include "access/genam.h"
35 #include "access/heapam.h"
36 #include "access/htup_details.h"
37 #include "access/tableam.h"
38 #include "access/xact.h"
39 #include "catalog/binary_upgrade.h"
40 #include "catalog/catalog.h"
41 #include "catalog/heap.h"
42 #include "catalog/objectaccess.h"
43 #include "catalog/pg_am.h"
44 #include "catalog/pg_authid.h"
45 #include "catalog/pg_cast.h"
46 #include "catalog/pg_collation.h"
47 #include "catalog/pg_constraint.h"
48 #include "catalog/pg_depend.h"
49 #include "catalog/pg_enum.h"
50 #include "catalog/pg_language.h"
51 #include "catalog/pg_namespace.h"
52 #include "catalog/pg_proc.h"
53 #include "catalog/pg_range.h"
54 #include "catalog/pg_type.h"
55 #include "commands/defrem.h"
56 #include "commands/tablecmds.h"
57 #include "commands/typecmds.h"
58 #include "executor/executor.h"
59 #include "miscadmin.h"
60 #include "nodes/makefuncs.h"
61 #include "optimizer/optimizer.h"
62 #include "parser/parse_coerce.h"
63 #include "parser/parse_collate.h"
64 #include "parser/parse_expr.h"
65 #include "parser/parse_func.h"
66 #include "parser/parse_type.h"
67 #include "utils/builtins.h"
68 #include "utils/fmgroids.h"
69 #include "utils/inval.h"
70 #include "utils/lsyscache.h"
71 #include "utils/memutils.h"
72 #include "utils/rel.h"
73 #include "utils/ruleutils.h"
74 #include "utils/snapmgr.h"
75 #include "utils/syscache.h"
76 
77 
78 /* result structure for get_rels_with_domain() */
79 typedef struct
80 {
81 	Relation	rel;			/* opened and locked relation */
82 	int			natts;			/* number of attributes of interest */
83 	int		   *atts;			/* attribute numbers */
84 	/* atts[] is of allocated length RelationGetNumberOfAttributes(rel) */
85 } RelToCheck;
86 
87 /* parameter structure for AlterTypeRecurse() */
88 typedef struct
89 {
90 	/* Flags indicating which type attributes to update */
91 	bool		updateStorage;
92 	bool		updateReceive;
93 	bool		updateSend;
94 	bool		updateTypmodin;
95 	bool		updateTypmodout;
96 	bool		updateAnalyze;
97 	bool		updateSubscript;
98 	/* New values for relevant attributes */
99 	char		storage;
100 	Oid			receiveOid;
101 	Oid			sendOid;
102 	Oid			typmodinOid;
103 	Oid			typmodoutOid;
104 	Oid			analyzeOid;
105 	Oid			subscriptOid;
106 } AlterTypeRecurseParams;
107 
108 /* Potentially set by pg_upgrade_support functions */
109 Oid			binary_upgrade_next_array_pg_type_oid = InvalidOid;
110 Oid			binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
111 Oid			binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
112 
113 static void makeRangeConstructors(const char *name, Oid namespace,
114 								  Oid rangeOid, Oid subtype);
115 static void makeMultirangeConstructors(const char *name, Oid namespace,
116 									   Oid multirangeOid, Oid rangeOid,
117 									   Oid rangeArrayOid, Oid *castFuncOid);
118 static Oid	findTypeInputFunction(List *procname, Oid typeOid);
119 static Oid	findTypeOutputFunction(List *procname, Oid typeOid);
120 static Oid	findTypeReceiveFunction(List *procname, Oid typeOid);
121 static Oid	findTypeSendFunction(List *procname, Oid typeOid);
122 static Oid	findTypeTypmodinFunction(List *procname);
123 static Oid	findTypeTypmodoutFunction(List *procname);
124 static Oid	findTypeAnalyzeFunction(List *procname, Oid typeOid);
125 static Oid	findTypeSubscriptingFunction(List *procname, Oid typeOid);
126 static Oid	findRangeSubOpclass(List *opcname, Oid subtype);
127 static Oid	findRangeCanonicalFunction(List *procname, Oid typeOid);
128 static Oid	findRangeSubtypeDiffFunction(List *procname, Oid subtype);
129 static void validateDomainConstraint(Oid domainoid, char *ccbin);
130 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
131 static void checkEnumOwner(HeapTuple tup);
132 static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
133 								 Oid baseTypeOid,
134 								 int typMod, Constraint *constr,
135 								 const char *domainName, ObjectAddress *constrAddr);
136 static Node *replace_domain_constraint_value(ParseState *pstate,
137 											 ColumnRef *cref);
138 static void AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
139 							 HeapTuple tup, Relation catalog,
140 							 AlterTypeRecurseParams *atparams);
141 
142 
143 /*
144  * DefineType
145  *		Registers a new base type.
146  */
147 ObjectAddress
DefineType(ParseState * pstate,List * names,List * parameters)148 DefineType(ParseState *pstate, List *names, List *parameters)
149 {
150 	char	   *typeName;
151 	Oid			typeNamespace;
152 	int16		internalLength = -1;	/* default: variable-length */
153 	List	   *inputName = NIL;
154 	List	   *outputName = NIL;
155 	List	   *receiveName = NIL;
156 	List	   *sendName = NIL;
157 	List	   *typmodinName = NIL;
158 	List	   *typmodoutName = NIL;
159 	List	   *analyzeName = NIL;
160 	List	   *subscriptName = NIL;
161 	char		category = TYPCATEGORY_USER;
162 	bool		preferred = false;
163 	char		delimiter = DEFAULT_TYPDELIM;
164 	Oid			elemType = InvalidOid;
165 	char	   *defaultValue = NULL;
166 	bool		byValue = false;
167 	char		alignment = TYPALIGN_INT;	/* default alignment */
168 	char		storage = TYPSTORAGE_PLAIN; /* default TOAST storage method */
169 	Oid			collation = InvalidOid;
170 	DefElem    *likeTypeEl = NULL;
171 	DefElem    *internalLengthEl = NULL;
172 	DefElem    *inputNameEl = NULL;
173 	DefElem    *outputNameEl = NULL;
174 	DefElem    *receiveNameEl = NULL;
175 	DefElem    *sendNameEl = NULL;
176 	DefElem    *typmodinNameEl = NULL;
177 	DefElem    *typmodoutNameEl = NULL;
178 	DefElem    *analyzeNameEl = NULL;
179 	DefElem    *subscriptNameEl = NULL;
180 	DefElem    *categoryEl = NULL;
181 	DefElem    *preferredEl = NULL;
182 	DefElem    *delimiterEl = NULL;
183 	DefElem    *elemTypeEl = NULL;
184 	DefElem    *defaultValueEl = NULL;
185 	DefElem    *byValueEl = NULL;
186 	DefElem    *alignmentEl = NULL;
187 	DefElem    *storageEl = NULL;
188 	DefElem    *collatableEl = NULL;
189 	Oid			inputOid;
190 	Oid			outputOid;
191 	Oid			receiveOid = InvalidOid;
192 	Oid			sendOid = InvalidOid;
193 	Oid			typmodinOid = InvalidOid;
194 	Oid			typmodoutOid = InvalidOid;
195 	Oid			analyzeOid = InvalidOid;
196 	Oid			subscriptOid = InvalidOid;
197 	char	   *array_type;
198 	Oid			array_oid;
199 	Oid			typoid;
200 	ListCell   *pl;
201 	ObjectAddress address;
202 
203 	/*
204 	 * As of Postgres 8.4, we require superuser privilege to create a base
205 	 * type.  This is simple paranoia: there are too many ways to mess up the
206 	 * system with an incorrect type definition (for instance, representation
207 	 * parameters that don't match what the C code expects).  In practice it
208 	 * takes superuser privilege to create the I/O functions, and so the
209 	 * former requirement that you own the I/O functions pretty much forced
210 	 * superuserness anyway.  We're just making doubly sure here.
211 	 *
212 	 * XXX re-enable NOT_USED code sections below if you remove this test.
213 	 */
214 	if (!superuser())
215 		ereport(ERROR,
216 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
217 				 errmsg("must be superuser to create a base type")));
218 
219 	/* Convert list of names to a name and namespace */
220 	typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
221 
222 #ifdef NOT_USED
223 	/* XXX this is unnecessary given the superuser check above */
224 	/* Check we have creation rights in target namespace */
225 	aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
226 	if (aclresult != ACLCHECK_OK)
227 		aclcheck_error(aclresult, OBJECT_SCHEMA,
228 					   get_namespace_name(typeNamespace));
229 #endif
230 
231 	/*
232 	 * Look to see if type already exists.
233 	 */
234 	typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
235 							 CStringGetDatum(typeName),
236 							 ObjectIdGetDatum(typeNamespace));
237 
238 	/*
239 	 * If it's not a shell, see if it's an autogenerated array type, and if so
240 	 * rename it out of the way.
241 	 */
242 	if (OidIsValid(typoid) && get_typisdefined(typoid))
243 	{
244 		if (moveArrayTypeName(typoid, typeName, typeNamespace))
245 			typoid = InvalidOid;
246 		else
247 			ereport(ERROR,
248 					(errcode(ERRCODE_DUPLICATE_OBJECT),
249 					 errmsg("type \"%s\" already exists", typeName)));
250 	}
251 
252 	/*
253 	 * If this command is a parameterless CREATE TYPE, then we're just here to
254 	 * make a shell type, so do that (or fail if there already is a shell).
255 	 */
256 	if (parameters == NIL)
257 	{
258 		if (OidIsValid(typoid))
259 			ereport(ERROR,
260 					(errcode(ERRCODE_DUPLICATE_OBJECT),
261 					 errmsg("type \"%s\" already exists", typeName)));
262 
263 		address = TypeShellMake(typeName, typeNamespace, GetUserId());
264 		return address;
265 	}
266 
267 	/*
268 	 * Otherwise, we must already have a shell type, since there is no other
269 	 * way that the I/O functions could have been created.
270 	 */
271 	if (!OidIsValid(typoid))
272 		ereport(ERROR,
273 				(errcode(ERRCODE_DUPLICATE_OBJECT),
274 				 errmsg("type \"%s\" does not exist", typeName),
275 				 errhint("Create the type as a shell type, then create its I/O functions, then do a full CREATE TYPE.")));
276 
277 	/* Extract the parameters from the parameter list */
278 	foreach(pl, parameters)
279 	{
280 		DefElem    *defel = (DefElem *) lfirst(pl);
281 		DefElem   **defelp;
282 
283 		if (strcmp(defel->defname, "like") == 0)
284 			defelp = &likeTypeEl;
285 		else if (strcmp(defel->defname, "internallength") == 0)
286 			defelp = &internalLengthEl;
287 		else if (strcmp(defel->defname, "input") == 0)
288 			defelp = &inputNameEl;
289 		else if (strcmp(defel->defname, "output") == 0)
290 			defelp = &outputNameEl;
291 		else if (strcmp(defel->defname, "receive") == 0)
292 			defelp = &receiveNameEl;
293 		else if (strcmp(defel->defname, "send") == 0)
294 			defelp = &sendNameEl;
295 		else if (strcmp(defel->defname, "typmod_in") == 0)
296 			defelp = &typmodinNameEl;
297 		else if (strcmp(defel->defname, "typmod_out") == 0)
298 			defelp = &typmodoutNameEl;
299 		else if (strcmp(defel->defname, "analyze") == 0 ||
300 				 strcmp(defel->defname, "analyse") == 0)
301 			defelp = &analyzeNameEl;
302 		else if (strcmp(defel->defname, "subscript") == 0)
303 			defelp = &subscriptNameEl;
304 		else if (strcmp(defel->defname, "category") == 0)
305 			defelp = &categoryEl;
306 		else if (strcmp(defel->defname, "preferred") == 0)
307 			defelp = &preferredEl;
308 		else if (strcmp(defel->defname, "delimiter") == 0)
309 			defelp = &delimiterEl;
310 		else if (strcmp(defel->defname, "element") == 0)
311 			defelp = &elemTypeEl;
312 		else if (strcmp(defel->defname, "default") == 0)
313 			defelp = &defaultValueEl;
314 		else if (strcmp(defel->defname, "passedbyvalue") == 0)
315 			defelp = &byValueEl;
316 		else if (strcmp(defel->defname, "alignment") == 0)
317 			defelp = &alignmentEl;
318 		else if (strcmp(defel->defname, "storage") == 0)
319 			defelp = &storageEl;
320 		else if (strcmp(defel->defname, "collatable") == 0)
321 			defelp = &collatableEl;
322 		else
323 		{
324 			/* WARNING, not ERROR, for historical backwards-compatibility */
325 			ereport(WARNING,
326 					(errcode(ERRCODE_SYNTAX_ERROR),
327 					 errmsg("type attribute \"%s\" not recognized",
328 							defel->defname),
329 					 parser_errposition(pstate, defel->location)));
330 			continue;
331 		}
332 		if (*defelp != NULL)
333 			ereport(ERROR,
334 					(errcode(ERRCODE_SYNTAX_ERROR),
335 					 errmsg("conflicting or redundant options"),
336 					 parser_errposition(pstate, defel->location)));
337 		*defelp = defel;
338 	}
339 
340 	/*
341 	 * Now interpret the options; we do this separately so that LIKE can be
342 	 * overridden by other options regardless of the ordering in the parameter
343 	 * list.
344 	 */
345 	if (likeTypeEl)
346 	{
347 		Type		likeType;
348 		Form_pg_type likeForm;
349 
350 		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
351 		likeForm = (Form_pg_type) GETSTRUCT(likeType);
352 		internalLength = likeForm->typlen;
353 		byValue = likeForm->typbyval;
354 		alignment = likeForm->typalign;
355 		storage = likeForm->typstorage;
356 		ReleaseSysCache(likeType);
357 	}
358 	if (internalLengthEl)
359 		internalLength = defGetTypeLength(internalLengthEl);
360 	if (inputNameEl)
361 		inputName = defGetQualifiedName(inputNameEl);
362 	if (outputNameEl)
363 		outputName = defGetQualifiedName(outputNameEl);
364 	if (receiveNameEl)
365 		receiveName = defGetQualifiedName(receiveNameEl);
366 	if (sendNameEl)
367 		sendName = defGetQualifiedName(sendNameEl);
368 	if (typmodinNameEl)
369 		typmodinName = defGetQualifiedName(typmodinNameEl);
370 	if (typmodoutNameEl)
371 		typmodoutName = defGetQualifiedName(typmodoutNameEl);
372 	if (analyzeNameEl)
373 		analyzeName = defGetQualifiedName(analyzeNameEl);
374 	if (subscriptNameEl)
375 		subscriptName = defGetQualifiedName(subscriptNameEl);
376 	if (categoryEl)
377 	{
378 		char	   *p = defGetString(categoryEl);
379 
380 		category = p[0];
381 		/* restrict to non-control ASCII */
382 		if (category < 32 || category > 126)
383 			ereport(ERROR,
384 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
385 					 errmsg("invalid type category \"%s\": must be simple ASCII",
386 							p)));
387 	}
388 	if (preferredEl)
389 		preferred = defGetBoolean(preferredEl);
390 	if (delimiterEl)
391 	{
392 		char	   *p = defGetString(delimiterEl);
393 
394 		delimiter = p[0];
395 		/* XXX shouldn't we restrict the delimiter? */
396 	}
397 	if (elemTypeEl)
398 	{
399 		elemType = typenameTypeId(NULL, defGetTypeName(elemTypeEl));
400 		/* disallow arrays of pseudotypes */
401 		if (get_typtype(elemType) == TYPTYPE_PSEUDO)
402 			ereport(ERROR,
403 					(errcode(ERRCODE_DATATYPE_MISMATCH),
404 					 errmsg("array element type cannot be %s",
405 							format_type_be(elemType))));
406 	}
407 	if (defaultValueEl)
408 		defaultValue = defGetString(defaultValueEl);
409 	if (byValueEl)
410 		byValue = defGetBoolean(byValueEl);
411 	if (alignmentEl)
412 	{
413 		char	   *a = defGetString(alignmentEl);
414 
415 		/*
416 		 * Note: if argument was an unquoted identifier, parser will have
417 		 * applied translations to it, so be prepared to recognize translated
418 		 * type names as well as the nominal form.
419 		 */
420 		if (pg_strcasecmp(a, "double") == 0 ||
421 			pg_strcasecmp(a, "float8") == 0 ||
422 			pg_strcasecmp(a, "pg_catalog.float8") == 0)
423 			alignment = TYPALIGN_DOUBLE;
424 		else if (pg_strcasecmp(a, "int4") == 0 ||
425 				 pg_strcasecmp(a, "pg_catalog.int4") == 0)
426 			alignment = TYPALIGN_INT;
427 		else if (pg_strcasecmp(a, "int2") == 0 ||
428 				 pg_strcasecmp(a, "pg_catalog.int2") == 0)
429 			alignment = TYPALIGN_SHORT;
430 		else if (pg_strcasecmp(a, "char") == 0 ||
431 				 pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
432 			alignment = TYPALIGN_CHAR;
433 		else
434 			ereport(ERROR,
435 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
436 					 errmsg("alignment \"%s\" not recognized", a)));
437 	}
438 	if (storageEl)
439 	{
440 		char	   *a = defGetString(storageEl);
441 
442 		if (pg_strcasecmp(a, "plain") == 0)
443 			storage = TYPSTORAGE_PLAIN;
444 		else if (pg_strcasecmp(a, "external") == 0)
445 			storage = TYPSTORAGE_EXTERNAL;
446 		else if (pg_strcasecmp(a, "extended") == 0)
447 			storage = TYPSTORAGE_EXTENDED;
448 		else if (pg_strcasecmp(a, "main") == 0)
449 			storage = TYPSTORAGE_MAIN;
450 		else
451 			ereport(ERROR,
452 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
453 					 errmsg("storage \"%s\" not recognized", a)));
454 	}
455 	if (collatableEl)
456 		collation = defGetBoolean(collatableEl) ? DEFAULT_COLLATION_OID : InvalidOid;
457 
458 	/*
459 	 * make sure we have our required definitions
460 	 */
461 	if (inputName == NIL)
462 		ereport(ERROR,
463 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
464 				 errmsg("type input function must be specified")));
465 	if (outputName == NIL)
466 		ereport(ERROR,
467 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
468 				 errmsg("type output function must be specified")));
469 
470 	if (typmodinName == NIL && typmodoutName != NIL)
471 		ereport(ERROR,
472 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
473 				 errmsg("type modifier output function is useless without a type modifier input function")));
474 
475 	/*
476 	 * Convert I/O proc names to OIDs
477 	 */
478 	inputOid = findTypeInputFunction(inputName, typoid);
479 	outputOid = findTypeOutputFunction(outputName, typoid);
480 	if (receiveName)
481 		receiveOid = findTypeReceiveFunction(receiveName, typoid);
482 	if (sendName)
483 		sendOid = findTypeSendFunction(sendName, typoid);
484 
485 	/*
486 	 * Convert typmodin/out function proc names to OIDs.
487 	 */
488 	if (typmodinName)
489 		typmodinOid = findTypeTypmodinFunction(typmodinName);
490 	if (typmodoutName)
491 		typmodoutOid = findTypeTypmodoutFunction(typmodoutName);
492 
493 	/*
494 	 * Convert analysis function proc name to an OID. If no analysis function
495 	 * is specified, we'll use zero to select the built-in default algorithm.
496 	 */
497 	if (analyzeName)
498 		analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
499 
500 	/*
501 	 * Likewise look up the subscripting function if any.  If it is not
502 	 * specified, but a typelem is specified, allow that if
503 	 * raw_array_subscript_handler can be used.  (This is for backwards
504 	 * compatibility; maybe someday we should throw an error instead.)
505 	 */
506 	if (subscriptName)
507 		subscriptOid = findTypeSubscriptingFunction(subscriptName, typoid);
508 	else if (OidIsValid(elemType))
509 	{
510 		if (internalLength > 0 && !byValue && get_typlen(elemType) > 0)
511 			subscriptOid = F_RAW_ARRAY_SUBSCRIPT_HANDLER;
512 		else
513 			ereport(ERROR,
514 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
515 					 errmsg("element type cannot be specified without a subscripting function")));
516 	}
517 
518 	/*
519 	 * Check permissions on functions.  We choose to require the creator/owner
520 	 * of a type to also own the underlying functions.  Since creating a type
521 	 * is tantamount to granting public execute access on the functions, the
522 	 * minimum sane check would be for execute-with-grant-option.  But we
523 	 * don't have a way to make the type go away if the grant option is
524 	 * revoked, so ownership seems better.
525 	 *
526 	 * XXX For now, this is all unnecessary given the superuser check above.
527 	 * If we ever relax that, these calls likely should be moved into
528 	 * findTypeInputFunction et al, where they could be shared by AlterType.
529 	 */
530 #ifdef NOT_USED
531 	if (inputOid && !pg_proc_ownercheck(inputOid, GetUserId()))
532 		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
533 					   NameListToString(inputName));
534 	if (outputOid && !pg_proc_ownercheck(outputOid, GetUserId()))
535 		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
536 					   NameListToString(outputName));
537 	if (receiveOid && !pg_proc_ownercheck(receiveOid, GetUserId()))
538 		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
539 					   NameListToString(receiveName));
540 	if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
541 		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
542 					   NameListToString(sendName));
543 	if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId()))
544 		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
545 					   NameListToString(typmodinName));
546 	if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId()))
547 		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
548 					   NameListToString(typmodoutName));
549 	if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
550 		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
551 					   NameListToString(analyzeName));
552 	if (subscriptOid && !pg_proc_ownercheck(subscriptOid, GetUserId()))
553 		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
554 					   NameListToString(subscriptName));
555 #endif
556 
557 	/*
558 	 * OK, we're done checking, time to make the type.  We must assign the
559 	 * array type OID ahead of calling TypeCreate, since the base type and
560 	 * array type each refer to the other.
561 	 */
562 	array_oid = AssignTypeArrayOid();
563 
564 	/*
565 	 * now have TypeCreate do all the real work.
566 	 *
567 	 * Note: the pg_type.oid is stored in user tables as array elements (base
568 	 * types) in ArrayType and in composite types in DatumTupleFields.  This
569 	 * oid must be preserved by binary upgrades.
570 	 */
571 	address =
572 		TypeCreate(InvalidOid,	/* no predetermined type OID */
573 				   typeName,	/* type name */
574 				   typeNamespace,	/* namespace */
575 				   InvalidOid,	/* relation oid (n/a here) */
576 				   0,			/* relation kind (ditto) */
577 				   GetUserId(), /* owner's ID */
578 				   internalLength,	/* internal size */
579 				   TYPTYPE_BASE,	/* type-type (base type) */
580 				   category,	/* type-category */
581 				   preferred,	/* is it a preferred type? */
582 				   delimiter,	/* array element delimiter */
583 				   inputOid,	/* input procedure */
584 				   outputOid,	/* output procedure */
585 				   receiveOid,	/* receive procedure */
586 				   sendOid,		/* send procedure */
587 				   typmodinOid, /* typmodin procedure */
588 				   typmodoutOid,	/* typmodout procedure */
589 				   analyzeOid,	/* analyze procedure */
590 				   subscriptOid,	/* subscript procedure */
591 				   elemType,	/* element type ID */
592 				   false,		/* this is not an implicit array type */
593 				   array_oid,	/* array type we are about to create */
594 				   InvalidOid,	/* base type ID (only for domains) */
595 				   defaultValue,	/* default type value */
596 				   NULL,		/* no binary form available */
597 				   byValue,		/* passed by value */
598 				   alignment,	/* required alignment */
599 				   storage,		/* TOAST strategy */
600 				   -1,			/* typMod (Domains only) */
601 				   0,			/* Array Dimensions of typbasetype */
602 				   false,		/* Type NOT NULL */
603 				   collation);	/* type's collation */
604 	Assert(typoid == address.objectId);
605 
606 	/*
607 	 * Create the array type that goes with it.
608 	 */
609 	array_type = makeArrayTypeName(typeName, typeNamespace);
610 
611 	/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for arrays */
612 	alignment = (alignment == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
613 
614 	TypeCreate(array_oid,		/* force assignment of this type OID */
615 			   array_type,		/* type name */
616 			   typeNamespace,	/* namespace */
617 			   InvalidOid,		/* relation oid (n/a here) */
618 			   0,				/* relation kind (ditto) */
619 			   GetUserId(),		/* owner's ID */
620 			   -1,				/* internal size (always varlena) */
621 			   TYPTYPE_BASE,	/* type-type (base type) */
622 			   TYPCATEGORY_ARRAY,	/* type-category (array) */
623 			   false,			/* array types are never preferred */
624 			   delimiter,		/* array element delimiter */
625 			   F_ARRAY_IN,		/* input procedure */
626 			   F_ARRAY_OUT,		/* output procedure */
627 			   F_ARRAY_RECV,	/* receive procedure */
628 			   F_ARRAY_SEND,	/* send procedure */
629 			   typmodinOid,		/* typmodin procedure */
630 			   typmodoutOid,	/* typmodout procedure */
631 			   F_ARRAY_TYPANALYZE,	/* analyze procedure */
632 			   F_ARRAY_SUBSCRIPT_HANDLER,	/* array subscript procedure */
633 			   typoid,			/* element type ID */
634 			   true,			/* yes this is an array type */
635 			   InvalidOid,		/* no further array type */
636 			   InvalidOid,		/* base type ID */
637 			   NULL,			/* never a default type value */
638 			   NULL,			/* binary default isn't sent either */
639 			   false,			/* never passed by value */
640 			   alignment,		/* see above */
641 			   TYPSTORAGE_EXTENDED, /* ARRAY is always toastable */
642 			   -1,				/* typMod (Domains only) */
643 			   0,				/* Array dimensions of typbasetype */
644 			   false,			/* Type NOT NULL */
645 			   collation);		/* type's collation */
646 
647 	pfree(array_type);
648 
649 	return address;
650 }
651 
652 /*
653  * Guts of type deletion.
654  */
655 void
RemoveTypeById(Oid typeOid)656 RemoveTypeById(Oid typeOid)
657 {
658 	Relation	relation;
659 	HeapTuple	tup;
660 
661 	relation = table_open(TypeRelationId, RowExclusiveLock);
662 
663 	tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
664 	if (!HeapTupleIsValid(tup))
665 		elog(ERROR, "cache lookup failed for type %u", typeOid);
666 
667 	CatalogTupleDelete(relation, &tup->t_self);
668 
669 	/*
670 	 * If it is an enum, delete the pg_enum entries too; we don't bother with
671 	 * making dependency entries for those, so it has to be done "by hand"
672 	 * here.
673 	 */
674 	if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
675 		EnumValuesDelete(typeOid);
676 
677 	/*
678 	 * If it is a range type, delete the pg_range entry too; we don't bother
679 	 * with making a dependency entry for that, so it has to be done "by hand"
680 	 * here.
681 	 */
682 	if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_RANGE)
683 		RangeDelete(typeOid);
684 
685 	ReleaseSysCache(tup);
686 
687 	table_close(relation, RowExclusiveLock);
688 }
689 
690 
691 /*
692  * DefineDomain
693  *		Registers a new domain.
694  */
695 ObjectAddress
DefineDomain(CreateDomainStmt * stmt)696 DefineDomain(CreateDomainStmt *stmt)
697 {
698 	char	   *domainName;
699 	char	   *domainArrayName;
700 	Oid			domainNamespace;
701 	AclResult	aclresult;
702 	int16		internalLength;
703 	Oid			inputProcedure;
704 	Oid			outputProcedure;
705 	Oid			receiveProcedure;
706 	Oid			sendProcedure;
707 	Oid			analyzeProcedure;
708 	bool		byValue;
709 	char		category;
710 	char		delimiter;
711 	char		alignment;
712 	char		storage;
713 	char		typtype;
714 	Datum		datum;
715 	bool		isnull;
716 	char	   *defaultValue = NULL;
717 	char	   *defaultValueBin = NULL;
718 	bool		saw_default = false;
719 	bool		typNotNull = false;
720 	bool		nullDefined = false;
721 	int32		typNDims = list_length(stmt->typeName->arrayBounds);
722 	HeapTuple	typeTup;
723 	List	   *schema = stmt->constraints;
724 	ListCell   *listptr;
725 	Oid			basetypeoid;
726 	Oid			old_type_oid;
727 	Oid			domaincoll;
728 	Oid			domainArrayOid;
729 	Form_pg_type baseType;
730 	int32		basetypeMod;
731 	Oid			baseColl;
732 	ObjectAddress address;
733 
734 	/* Convert list of names to a name and namespace */
735 	domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
736 														&domainName);
737 
738 	/* Check we have creation rights in target namespace */
739 	aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
740 									  ACL_CREATE);
741 	if (aclresult != ACLCHECK_OK)
742 		aclcheck_error(aclresult, OBJECT_SCHEMA,
743 					   get_namespace_name(domainNamespace));
744 
745 	/*
746 	 * Check for collision with an existing type name.  If there is one and
747 	 * it's an autogenerated array, we can rename it out of the way.
748 	 */
749 	old_type_oid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
750 								   CStringGetDatum(domainName),
751 								   ObjectIdGetDatum(domainNamespace));
752 	if (OidIsValid(old_type_oid))
753 	{
754 		if (!moveArrayTypeName(old_type_oid, domainName, domainNamespace))
755 			ereport(ERROR,
756 					(errcode(ERRCODE_DUPLICATE_OBJECT),
757 					 errmsg("type \"%s\" already exists", domainName)));
758 	}
759 
760 	/*
761 	 * Look up the base type.
762 	 */
763 	typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
764 	baseType = (Form_pg_type) GETSTRUCT(typeTup);
765 	basetypeoid = baseType->oid;
766 
767 	/*
768 	 * Base type must be a plain base type, a composite type, another domain,
769 	 * an enum or a range type.  Domains over pseudotypes would create a
770 	 * security hole.  (It would be shorter to code this to just check for
771 	 * pseudotypes; but it seems safer to call out the specific typtypes that
772 	 * are supported, rather than assume that all future typtypes would be
773 	 * automatically supported.)
774 	 */
775 	typtype = baseType->typtype;
776 	if (typtype != TYPTYPE_BASE &&
777 		typtype != TYPTYPE_COMPOSITE &&
778 		typtype != TYPTYPE_DOMAIN &&
779 		typtype != TYPTYPE_ENUM &&
780 		typtype != TYPTYPE_RANGE &&
781 		typtype != TYPTYPE_MULTIRANGE)
782 		ereport(ERROR,
783 				(errcode(ERRCODE_DATATYPE_MISMATCH),
784 				 errmsg("\"%s\" is not a valid base type for a domain",
785 						TypeNameToString(stmt->typeName))));
786 
787 	aclresult = pg_type_aclcheck(basetypeoid, GetUserId(), ACL_USAGE);
788 	if (aclresult != ACLCHECK_OK)
789 		aclcheck_error_type(aclresult, basetypeoid);
790 
791 	/*
792 	 * Collect the properties of the new domain.  Some are inherited from the
793 	 * base type, some are not.  If you change any of this inheritance
794 	 * behavior, be sure to update AlterTypeRecurse() to match!
795 	 */
796 
797 	/*
798 	 * Identify the collation if any
799 	 */
800 	baseColl = baseType->typcollation;
801 	if (stmt->collClause)
802 		domaincoll = get_collation_oid(stmt->collClause->collname, false);
803 	else
804 		domaincoll = baseColl;
805 
806 	/* Complain if COLLATE is applied to an uncollatable type */
807 	if (OidIsValid(domaincoll) && !OidIsValid(baseColl))
808 		ereport(ERROR,
809 				(errcode(ERRCODE_DATATYPE_MISMATCH),
810 				 errmsg("collations are not supported by type %s",
811 						format_type_be(basetypeoid))));
812 
813 	/* passed by value */
814 	byValue = baseType->typbyval;
815 
816 	/* Required Alignment */
817 	alignment = baseType->typalign;
818 
819 	/* TOAST Strategy */
820 	storage = baseType->typstorage;
821 
822 	/* Storage Length */
823 	internalLength = baseType->typlen;
824 
825 	/* Type Category */
826 	category = baseType->typcategory;
827 
828 	/* Array element Delimiter */
829 	delimiter = baseType->typdelim;
830 
831 	/* I/O Functions */
832 	inputProcedure = F_DOMAIN_IN;
833 	outputProcedure = baseType->typoutput;
834 	receiveProcedure = F_DOMAIN_RECV;
835 	sendProcedure = baseType->typsend;
836 
837 	/* Domains never accept typmods, so no typmodin/typmodout needed */
838 
839 	/* Analysis function */
840 	analyzeProcedure = baseType->typanalyze;
841 
842 	/*
843 	 * Domains don't need a subscript function, since they are not
844 	 * subscriptable on their own.  If the base type is subscriptable, the
845 	 * parser will reduce the type to the base type before subscripting.
846 	 */
847 
848 	/* Inherited default value */
849 	datum = SysCacheGetAttr(TYPEOID, typeTup,
850 							Anum_pg_type_typdefault, &isnull);
851 	if (!isnull)
852 		defaultValue = TextDatumGetCString(datum);
853 
854 	/* Inherited default binary value */
855 	datum = SysCacheGetAttr(TYPEOID, typeTup,
856 							Anum_pg_type_typdefaultbin, &isnull);
857 	if (!isnull)
858 		defaultValueBin = TextDatumGetCString(datum);
859 
860 	/*
861 	 * Run through constraints manually to avoid the additional processing
862 	 * conducted by DefineRelation() and friends.
863 	 */
864 	foreach(listptr, schema)
865 	{
866 		Constraint *constr = lfirst(listptr);
867 
868 		if (!IsA(constr, Constraint))
869 			elog(ERROR, "unrecognized node type: %d",
870 				 (int) nodeTag(constr));
871 		switch (constr->contype)
872 		{
873 			case CONSTR_DEFAULT:
874 
875 				/*
876 				 * The inherited default value may be overridden by the user
877 				 * with the DEFAULT <expr> clause ... but only once.
878 				 */
879 				if (saw_default)
880 					ereport(ERROR,
881 							(errcode(ERRCODE_SYNTAX_ERROR),
882 							 errmsg("multiple default expressions")));
883 				saw_default = true;
884 
885 				if (constr->raw_expr)
886 				{
887 					ParseState *pstate;
888 					Node	   *defaultExpr;
889 
890 					/* Create a dummy ParseState for transformExpr */
891 					pstate = make_parsestate(NULL);
892 
893 					/*
894 					 * Cook the constr->raw_expr into an expression. Note:
895 					 * name is strictly for error message
896 					 */
897 					defaultExpr = cookDefault(pstate, constr->raw_expr,
898 											  basetypeoid,
899 											  basetypeMod,
900 											  domainName,
901 											  0);
902 
903 					/*
904 					 * If the expression is just a NULL constant, we treat it
905 					 * like not having a default.
906 					 *
907 					 * Note that if the basetype is another domain, we'll see
908 					 * a CoerceToDomain expr here and not discard the default.
909 					 * This is critical because the domain default needs to be
910 					 * retained to override any default that the base domain
911 					 * might have.
912 					 */
913 					if (defaultExpr == NULL ||
914 						(IsA(defaultExpr, Const) &&
915 						 ((Const *) defaultExpr)->constisnull))
916 					{
917 						defaultValue = NULL;
918 						defaultValueBin = NULL;
919 					}
920 					else
921 					{
922 						/*
923 						 * Expression must be stored as a nodeToString result,
924 						 * but we also require a valid textual representation
925 						 * (mainly to make life easier for pg_dump).
926 						 */
927 						defaultValue =
928 							deparse_expression(defaultExpr,
929 											   NIL, false, false);
930 						defaultValueBin = nodeToString(defaultExpr);
931 					}
932 				}
933 				else
934 				{
935 					/* No default (can this still happen?) */
936 					defaultValue = NULL;
937 					defaultValueBin = NULL;
938 				}
939 				break;
940 
941 			case CONSTR_NOTNULL:
942 				if (nullDefined && !typNotNull)
943 					ereport(ERROR,
944 							(errcode(ERRCODE_SYNTAX_ERROR),
945 							 errmsg("conflicting NULL/NOT NULL constraints")));
946 				typNotNull = true;
947 				nullDefined = true;
948 				break;
949 
950 			case CONSTR_NULL:
951 				if (nullDefined && typNotNull)
952 					ereport(ERROR,
953 							(errcode(ERRCODE_SYNTAX_ERROR),
954 							 errmsg("conflicting NULL/NOT NULL constraints")));
955 				typNotNull = false;
956 				nullDefined = true;
957 				break;
958 
959 			case CONSTR_CHECK:
960 
961 				/*
962 				 * Check constraints are handled after domain creation, as
963 				 * they require the Oid of the domain; at this point we can
964 				 * only check that they're not marked NO INHERIT, because that
965 				 * would be bogus.
966 				 */
967 				if (constr->is_no_inherit)
968 					ereport(ERROR,
969 							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
970 							 errmsg("check constraints for domains cannot be marked NO INHERIT")));
971 				break;
972 
973 				/*
974 				 * All else are error cases
975 				 */
976 			case CONSTR_UNIQUE:
977 				ereport(ERROR,
978 						(errcode(ERRCODE_SYNTAX_ERROR),
979 						 errmsg("unique constraints not possible for domains")));
980 				break;
981 
982 			case CONSTR_PRIMARY:
983 				ereport(ERROR,
984 						(errcode(ERRCODE_SYNTAX_ERROR),
985 						 errmsg("primary key constraints not possible for domains")));
986 				break;
987 
988 			case CONSTR_EXCLUSION:
989 				ereport(ERROR,
990 						(errcode(ERRCODE_SYNTAX_ERROR),
991 						 errmsg("exclusion constraints not possible for domains")));
992 				break;
993 
994 			case CONSTR_FOREIGN:
995 				ereport(ERROR,
996 						(errcode(ERRCODE_SYNTAX_ERROR),
997 						 errmsg("foreign key constraints not possible for domains")));
998 				break;
999 
1000 			case CONSTR_ATTR_DEFERRABLE:
1001 			case CONSTR_ATTR_NOT_DEFERRABLE:
1002 			case CONSTR_ATTR_DEFERRED:
1003 			case CONSTR_ATTR_IMMEDIATE:
1004 				ereport(ERROR,
1005 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1006 						 errmsg("specifying constraint deferrability not supported for domains")));
1007 				break;
1008 
1009 			default:
1010 				elog(ERROR, "unrecognized constraint subtype: %d",
1011 					 (int) constr->contype);
1012 				break;
1013 		}
1014 	}
1015 
1016 	/* Allocate OID for array type */
1017 	domainArrayOid = AssignTypeArrayOid();
1018 
1019 	/*
1020 	 * Have TypeCreate do all the real work.
1021 	 */
1022 	address =
1023 		TypeCreate(InvalidOid,	/* no predetermined type OID */
1024 				   domainName,	/* type name */
1025 				   domainNamespace, /* namespace */
1026 				   InvalidOid,	/* relation oid (n/a here) */
1027 				   0,			/* relation kind (ditto) */
1028 				   GetUserId(), /* owner's ID */
1029 				   internalLength,	/* internal size */
1030 				   TYPTYPE_DOMAIN,	/* type-type (domain type) */
1031 				   category,	/* type-category */
1032 				   false,		/* domain types are never preferred */
1033 				   delimiter,	/* array element delimiter */
1034 				   inputProcedure,	/* input procedure */
1035 				   outputProcedure, /* output procedure */
1036 				   receiveProcedure,	/* receive procedure */
1037 				   sendProcedure,	/* send procedure */
1038 				   InvalidOid,	/* typmodin procedure - none */
1039 				   InvalidOid,	/* typmodout procedure - none */
1040 				   analyzeProcedure,	/* analyze procedure */
1041 				   InvalidOid,	/* subscript procedure - none */
1042 				   InvalidOid,	/* no array element type */
1043 				   false,		/* this isn't an array */
1044 				   domainArrayOid,	/* array type we are about to create */
1045 				   basetypeoid, /* base type ID */
1046 				   defaultValue,	/* default type value (text) */
1047 				   defaultValueBin, /* default type value (binary) */
1048 				   byValue,		/* passed by value */
1049 				   alignment,	/* required alignment */
1050 				   storage,		/* TOAST strategy */
1051 				   basetypeMod, /* typeMod value */
1052 				   typNDims,	/* Array dimensions for base type */
1053 				   typNotNull,	/* Type NOT NULL */
1054 				   domaincoll); /* type's collation */
1055 
1056 	/*
1057 	 * Create the array type that goes with it.
1058 	 */
1059 	domainArrayName = makeArrayTypeName(domainName, domainNamespace);
1060 
1061 	/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for arrays */
1062 	alignment = (alignment == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
1063 
1064 	TypeCreate(domainArrayOid,	/* force assignment of this type OID */
1065 			   domainArrayName, /* type name */
1066 			   domainNamespace, /* namespace */
1067 			   InvalidOid,		/* relation oid (n/a here) */
1068 			   0,				/* relation kind (ditto) */
1069 			   GetUserId(),		/* owner's ID */
1070 			   -1,				/* internal size (always varlena) */
1071 			   TYPTYPE_BASE,	/* type-type (base type) */
1072 			   TYPCATEGORY_ARRAY,	/* type-category (array) */
1073 			   false,			/* array types are never preferred */
1074 			   delimiter,		/* array element delimiter */
1075 			   F_ARRAY_IN,		/* input procedure */
1076 			   F_ARRAY_OUT,		/* output procedure */
1077 			   F_ARRAY_RECV,	/* receive procedure */
1078 			   F_ARRAY_SEND,	/* send procedure */
1079 			   InvalidOid,		/* typmodin procedure - none */
1080 			   InvalidOid,		/* typmodout procedure - none */
1081 			   F_ARRAY_TYPANALYZE,	/* analyze procedure */
1082 			   F_ARRAY_SUBSCRIPT_HANDLER,	/* array subscript procedure */
1083 			   address.objectId,	/* element type ID */
1084 			   true,			/* yes this is an array type */
1085 			   InvalidOid,		/* no further array type */
1086 			   InvalidOid,		/* base type ID */
1087 			   NULL,			/* never a default type value */
1088 			   NULL,			/* binary default isn't sent either */
1089 			   false,			/* never passed by value */
1090 			   alignment,		/* see above */
1091 			   TYPSTORAGE_EXTENDED, /* ARRAY is always toastable */
1092 			   -1,				/* typMod (Domains only) */
1093 			   0,				/* Array dimensions of typbasetype */
1094 			   false,			/* Type NOT NULL */
1095 			   domaincoll);		/* type's collation */
1096 
1097 	pfree(domainArrayName);
1098 
1099 	/*
1100 	 * Process constraints which refer to the domain ID returned by TypeCreate
1101 	 */
1102 	foreach(listptr, schema)
1103 	{
1104 		Constraint *constr = lfirst(listptr);
1105 
1106 		/* it must be a Constraint, per check above */
1107 
1108 		switch (constr->contype)
1109 		{
1110 			case CONSTR_CHECK:
1111 				domainAddConstraint(address.objectId, domainNamespace,
1112 									basetypeoid, basetypeMod,
1113 									constr, domainName, NULL);
1114 				break;
1115 
1116 				/* Other constraint types were fully processed above */
1117 
1118 			default:
1119 				break;
1120 		}
1121 
1122 		/* CCI so we can detect duplicate constraint names */
1123 		CommandCounterIncrement();
1124 	}
1125 
1126 	/*
1127 	 * Now we can clean up.
1128 	 */
1129 	ReleaseSysCache(typeTup);
1130 
1131 	return address;
1132 }
1133 
1134 
1135 /*
1136  * DefineEnum
1137  *		Registers a new enum.
1138  */
1139 ObjectAddress
DefineEnum(CreateEnumStmt * stmt)1140 DefineEnum(CreateEnumStmt *stmt)
1141 {
1142 	char	   *enumName;
1143 	char	   *enumArrayName;
1144 	Oid			enumNamespace;
1145 	AclResult	aclresult;
1146 	Oid			old_type_oid;
1147 	Oid			enumArrayOid;
1148 	ObjectAddress enumTypeAddr;
1149 
1150 	/* Convert list of names to a name and namespace */
1151 	enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
1152 													  &enumName);
1153 
1154 	/* Check we have creation rights in target namespace */
1155 	aclresult = pg_namespace_aclcheck(enumNamespace, GetUserId(), ACL_CREATE);
1156 	if (aclresult != ACLCHECK_OK)
1157 		aclcheck_error(aclresult, OBJECT_SCHEMA,
1158 					   get_namespace_name(enumNamespace));
1159 
1160 	/*
1161 	 * Check for collision with an existing type name.  If there is one and
1162 	 * it's an autogenerated array, we can rename it out of the way.
1163 	 */
1164 	old_type_oid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
1165 								   CStringGetDatum(enumName),
1166 								   ObjectIdGetDatum(enumNamespace));
1167 	if (OidIsValid(old_type_oid))
1168 	{
1169 		if (!moveArrayTypeName(old_type_oid, enumName, enumNamespace))
1170 			ereport(ERROR,
1171 					(errcode(ERRCODE_DUPLICATE_OBJECT),
1172 					 errmsg("type \"%s\" already exists", enumName)));
1173 	}
1174 
1175 	/* Allocate OID for array type */
1176 	enumArrayOid = AssignTypeArrayOid();
1177 
1178 	/* Create the pg_type entry */
1179 	enumTypeAddr =
1180 		TypeCreate(InvalidOid,	/* no predetermined type OID */
1181 				   enumName,	/* type name */
1182 				   enumNamespace,	/* namespace */
1183 				   InvalidOid,	/* relation oid (n/a here) */
1184 				   0,			/* relation kind (ditto) */
1185 				   GetUserId(), /* owner's ID */
1186 				   sizeof(Oid), /* internal size */
1187 				   TYPTYPE_ENUM,	/* type-type (enum type) */
1188 				   TYPCATEGORY_ENUM,	/* type-category (enum type) */
1189 				   false,		/* enum types are never preferred */
1190 				   DEFAULT_TYPDELIM,	/* array element delimiter */
1191 				   F_ENUM_IN,	/* input procedure */
1192 				   F_ENUM_OUT,	/* output procedure */
1193 				   F_ENUM_RECV, /* receive procedure */
1194 				   F_ENUM_SEND, /* send procedure */
1195 				   InvalidOid,	/* typmodin procedure - none */
1196 				   InvalidOid,	/* typmodout procedure - none */
1197 				   InvalidOid,	/* analyze procedure - default */
1198 				   InvalidOid,	/* subscript procedure - none */
1199 				   InvalidOid,	/* element type ID */
1200 				   false,		/* this is not an array type */
1201 				   enumArrayOid,	/* array type we are about to create */
1202 				   InvalidOid,	/* base type ID (only for domains) */
1203 				   NULL,		/* never a default type value */
1204 				   NULL,		/* binary default isn't sent either */
1205 				   true,		/* always passed by value */
1206 				   TYPALIGN_INT,	/* int alignment */
1207 				   TYPSTORAGE_PLAIN,	/* TOAST strategy always plain */
1208 				   -1,			/* typMod (Domains only) */
1209 				   0,			/* Array dimensions of typbasetype */
1210 				   false,		/* Type NOT NULL */
1211 				   InvalidOid); /* type's collation */
1212 
1213 	/* Enter the enum's values into pg_enum */
1214 	EnumValuesCreate(enumTypeAddr.objectId, stmt->vals);
1215 
1216 	/*
1217 	 * Create the array type that goes with it.
1218 	 */
1219 	enumArrayName = makeArrayTypeName(enumName, enumNamespace);
1220 
1221 	TypeCreate(enumArrayOid,	/* force assignment of this type OID */
1222 			   enumArrayName,	/* type name */
1223 			   enumNamespace,	/* namespace */
1224 			   InvalidOid,		/* relation oid (n/a here) */
1225 			   0,				/* relation kind (ditto) */
1226 			   GetUserId(),		/* owner's ID */
1227 			   -1,				/* internal size (always varlena) */
1228 			   TYPTYPE_BASE,	/* type-type (base type) */
1229 			   TYPCATEGORY_ARRAY,	/* type-category (array) */
1230 			   false,			/* array types are never preferred */
1231 			   DEFAULT_TYPDELIM,	/* array element delimiter */
1232 			   F_ARRAY_IN,		/* input procedure */
1233 			   F_ARRAY_OUT,		/* output procedure */
1234 			   F_ARRAY_RECV,	/* receive procedure */
1235 			   F_ARRAY_SEND,	/* send procedure */
1236 			   InvalidOid,		/* typmodin procedure - none */
1237 			   InvalidOid,		/* typmodout procedure - none */
1238 			   F_ARRAY_TYPANALYZE,	/* analyze procedure */
1239 			   F_ARRAY_SUBSCRIPT_HANDLER,	/* array subscript procedure */
1240 			   enumTypeAddr.objectId,	/* element type ID */
1241 			   true,			/* yes this is an array type */
1242 			   InvalidOid,		/* no further array type */
1243 			   InvalidOid,		/* base type ID */
1244 			   NULL,			/* never a default type value */
1245 			   NULL,			/* binary default isn't sent either */
1246 			   false,			/* never passed by value */
1247 			   TYPALIGN_INT,	/* enums have int align, so do their arrays */
1248 			   TYPSTORAGE_EXTENDED, /* ARRAY is always toastable */
1249 			   -1,				/* typMod (Domains only) */
1250 			   0,				/* Array dimensions of typbasetype */
1251 			   false,			/* Type NOT NULL */
1252 			   InvalidOid);		/* type's collation */
1253 
1254 	pfree(enumArrayName);
1255 
1256 	return enumTypeAddr;
1257 }
1258 
1259 /*
1260  * AlterEnum
1261  *		Adds a new label to an existing enum.
1262  */
1263 ObjectAddress
AlterEnum(AlterEnumStmt * stmt)1264 AlterEnum(AlterEnumStmt *stmt)
1265 {
1266 	Oid			enum_type_oid;
1267 	TypeName   *typename;
1268 	HeapTuple	tup;
1269 	ObjectAddress address;
1270 
1271 	/* Make a TypeName so we can use standard type lookup machinery */
1272 	typename = makeTypeNameFromNameList(stmt->typeName);
1273 	enum_type_oid = typenameTypeId(NULL, typename);
1274 
1275 	tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(enum_type_oid));
1276 	if (!HeapTupleIsValid(tup))
1277 		elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
1278 
1279 	/* Check it's an enum and check user has permission to ALTER the enum */
1280 	checkEnumOwner(tup);
1281 
1282 	ReleaseSysCache(tup);
1283 
1284 	if (stmt->oldVal)
1285 	{
1286 		/* Rename an existing label */
1287 		RenameEnumLabel(enum_type_oid, stmt->oldVal, stmt->newVal);
1288 	}
1289 	else
1290 	{
1291 		/* Add a new label */
1292 		AddEnumLabel(enum_type_oid, stmt->newVal,
1293 					 stmt->newValNeighbor, stmt->newValIsAfter,
1294 					 stmt->skipIfNewValExists);
1295 	}
1296 
1297 	InvokeObjectPostAlterHook(TypeRelationId, enum_type_oid, 0);
1298 
1299 	ObjectAddressSet(address, TypeRelationId, enum_type_oid);
1300 
1301 	return address;
1302 }
1303 
1304 
1305 /*
1306  * checkEnumOwner
1307  *
1308  * Check that the type is actually an enum and that the current user
1309  * has permission to do ALTER TYPE on it.  Throw an error if not.
1310  */
1311 static void
checkEnumOwner(HeapTuple tup)1312 checkEnumOwner(HeapTuple tup)
1313 {
1314 	Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
1315 
1316 	/* Check that this is actually an enum */
1317 	if (typTup->typtype != TYPTYPE_ENUM)
1318 		ereport(ERROR,
1319 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
1320 				 errmsg("%s is not an enum",
1321 						format_type_be(typTup->oid))));
1322 
1323 	/* Permission check: must own type */
1324 	if (!pg_type_ownercheck(typTup->oid, GetUserId()))
1325 		aclcheck_error_type(ACLCHECK_NOT_OWNER, typTup->oid);
1326 }
1327 
1328 
1329 /*
1330  * DefineRange
1331  *		Registers a new range type.
1332  *
1333  * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
1334  * and likewise on multiranges to set it to the range type. But having a
1335  * non-zero typelem is treated elsewhere as a synonym for being an array,
1336  * and users might have queries with that same assumption.
1337  */
1338 ObjectAddress
DefineRange(CreateRangeStmt * stmt)1339 DefineRange(CreateRangeStmt *stmt)
1340 {
1341 	char	   *typeName;
1342 	Oid			typeNamespace;
1343 	Oid			typoid;
1344 	char	   *rangeArrayName;
1345 	char	   *multirangeTypeName = NULL;
1346 	char	   *multirangeArrayName;
1347 	Oid			multirangeNamespace = InvalidOid;
1348 	Oid			rangeArrayOid;
1349 	Oid			multirangeOid;
1350 	Oid			multirangeArrayOid;
1351 	Oid			rangeSubtype = InvalidOid;
1352 	List	   *rangeSubOpclassName = NIL;
1353 	List	   *rangeCollationName = NIL;
1354 	List	   *rangeCanonicalName = NIL;
1355 	List	   *rangeSubtypeDiffName = NIL;
1356 	Oid			rangeSubOpclass;
1357 	Oid			rangeCollation;
1358 	regproc		rangeCanonical;
1359 	regproc		rangeSubtypeDiff;
1360 	int16		subtyplen;
1361 	bool		subtypbyval;
1362 	char		subtypalign;
1363 	char		alignment;
1364 	AclResult	aclresult;
1365 	ListCell   *lc;
1366 	ObjectAddress address;
1367 	ObjectAddress mltrngaddress PG_USED_FOR_ASSERTS_ONLY;
1368 	Oid			castFuncOid;
1369 
1370 	/* Convert list of names to a name and namespace */
1371 	typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
1372 													  &typeName);
1373 
1374 	/* Check we have creation rights in target namespace */
1375 	aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
1376 	if (aclresult != ACLCHECK_OK)
1377 		aclcheck_error(aclresult, OBJECT_SCHEMA,
1378 					   get_namespace_name(typeNamespace));
1379 
1380 	/*
1381 	 * Look to see if type already exists.
1382 	 */
1383 	typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
1384 							 CStringGetDatum(typeName),
1385 							 ObjectIdGetDatum(typeNamespace));
1386 
1387 	/*
1388 	 * If it's not a shell, see if it's an autogenerated array type, and if so
1389 	 * rename it out of the way.
1390 	 */
1391 	if (OidIsValid(typoid) && get_typisdefined(typoid))
1392 	{
1393 		if (moveArrayTypeName(typoid, typeName, typeNamespace))
1394 			typoid = InvalidOid;
1395 		else
1396 			ereport(ERROR,
1397 					(errcode(ERRCODE_DUPLICATE_OBJECT),
1398 					 errmsg("type \"%s\" already exists", typeName)));
1399 	}
1400 
1401 	/*
1402 	 * Unlike DefineType(), we don't insist on a shell type existing first, as
1403 	 * it's only needed if the user wants to specify a canonical function.
1404 	 */
1405 
1406 	/* Extract the parameters from the parameter list */
1407 	foreach(lc, stmt->params)
1408 	{
1409 		DefElem    *defel = (DefElem *) lfirst(lc);
1410 
1411 		if (strcmp(defel->defname, "subtype") == 0)
1412 		{
1413 			if (OidIsValid(rangeSubtype))
1414 				ereport(ERROR,
1415 						(errcode(ERRCODE_SYNTAX_ERROR),
1416 						 errmsg("conflicting or redundant options")));
1417 			/* we can look up the subtype name immediately */
1418 			rangeSubtype = typenameTypeId(NULL, defGetTypeName(defel));
1419 		}
1420 		else if (strcmp(defel->defname, "subtype_opclass") == 0)
1421 		{
1422 			if (rangeSubOpclassName != NIL)
1423 				ereport(ERROR,
1424 						(errcode(ERRCODE_SYNTAX_ERROR),
1425 						 errmsg("conflicting or redundant options")));
1426 			rangeSubOpclassName = defGetQualifiedName(defel);
1427 		}
1428 		else if (strcmp(defel->defname, "collation") == 0)
1429 		{
1430 			if (rangeCollationName != NIL)
1431 				ereport(ERROR,
1432 						(errcode(ERRCODE_SYNTAX_ERROR),
1433 						 errmsg("conflicting or redundant options")));
1434 			rangeCollationName = defGetQualifiedName(defel);
1435 		}
1436 		else if (strcmp(defel->defname, "canonical") == 0)
1437 		{
1438 			if (rangeCanonicalName != NIL)
1439 				ereport(ERROR,
1440 						(errcode(ERRCODE_SYNTAX_ERROR),
1441 						 errmsg("conflicting or redundant options")));
1442 			rangeCanonicalName = defGetQualifiedName(defel);
1443 		}
1444 		else if (strcmp(defel->defname, "subtype_diff") == 0)
1445 		{
1446 			if (rangeSubtypeDiffName != NIL)
1447 				ereport(ERROR,
1448 						(errcode(ERRCODE_SYNTAX_ERROR),
1449 						 errmsg("conflicting or redundant options")));
1450 			rangeSubtypeDiffName = defGetQualifiedName(defel);
1451 		}
1452 		else if (strcmp(defel->defname, "multirange_type_name") == 0)
1453 		{
1454 			if (multirangeTypeName != NULL)
1455 				ereport(ERROR,
1456 						(errcode(ERRCODE_SYNTAX_ERROR),
1457 						 errmsg("conflicting or redundant options")));
1458 			/* we can look up the subtype name immediately */
1459 			multirangeNamespace = QualifiedNameGetCreationNamespace(defGetQualifiedName(defel),
1460 																	&multirangeTypeName);
1461 		}
1462 		else
1463 			ereport(ERROR,
1464 					(errcode(ERRCODE_SYNTAX_ERROR),
1465 					 errmsg("type attribute \"%s\" not recognized",
1466 							defel->defname)));
1467 	}
1468 
1469 	/* Must have a subtype */
1470 	if (!OidIsValid(rangeSubtype))
1471 		ereport(ERROR,
1472 				(errcode(ERRCODE_SYNTAX_ERROR),
1473 				 errmsg("type attribute \"subtype\" is required")));
1474 	/* disallow ranges of pseudotypes */
1475 	if (get_typtype(rangeSubtype) == TYPTYPE_PSEUDO)
1476 		ereport(ERROR,
1477 				(errcode(ERRCODE_DATATYPE_MISMATCH),
1478 				 errmsg("range subtype cannot be %s",
1479 						format_type_be(rangeSubtype))));
1480 
1481 	/* Identify subopclass */
1482 	rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype);
1483 
1484 	/* Identify collation to use, if any */
1485 	if (type_is_collatable(rangeSubtype))
1486 	{
1487 		if (rangeCollationName != NIL)
1488 			rangeCollation = get_collation_oid(rangeCollationName, false);
1489 		else
1490 			rangeCollation = get_typcollation(rangeSubtype);
1491 	}
1492 	else
1493 	{
1494 		if (rangeCollationName != NIL)
1495 			ereport(ERROR,
1496 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
1497 					 errmsg("range collation specified but subtype does not support collation")));
1498 		rangeCollation = InvalidOid;
1499 	}
1500 
1501 	/* Identify support functions, if provided */
1502 	if (rangeCanonicalName != NIL)
1503 	{
1504 		if (!OidIsValid(typoid))
1505 			ereport(ERROR,
1506 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1507 					 errmsg("cannot specify a canonical function without a pre-created shell type"),
1508 					 errhint("Create the type as a shell type, then create its canonicalization function, then do a full CREATE TYPE.")));
1509 		rangeCanonical = findRangeCanonicalFunction(rangeCanonicalName,
1510 													typoid);
1511 	}
1512 	else
1513 		rangeCanonical = InvalidOid;
1514 
1515 	if (rangeSubtypeDiffName != NIL)
1516 		rangeSubtypeDiff = findRangeSubtypeDiffFunction(rangeSubtypeDiffName,
1517 														rangeSubtype);
1518 	else
1519 		rangeSubtypeDiff = InvalidOid;
1520 
1521 	get_typlenbyvalalign(rangeSubtype,
1522 						 &subtyplen, &subtypbyval, &subtypalign);
1523 
1524 	/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
1525 	alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
1526 
1527 	/* Allocate OID for array type, its multirange, and its multirange array */
1528 	rangeArrayOid = AssignTypeArrayOid();
1529 	multirangeOid = AssignTypeMultirangeOid();
1530 	multirangeArrayOid = AssignTypeMultirangeArrayOid();
1531 
1532 	/* Create the pg_type entry */
1533 	address =
1534 		TypeCreate(InvalidOid,	/* no predetermined type OID */
1535 				   typeName,	/* type name */
1536 				   typeNamespace,	/* namespace */
1537 				   InvalidOid,	/* relation oid (n/a here) */
1538 				   0,			/* relation kind (ditto) */
1539 				   GetUserId(), /* owner's ID */
1540 				   -1,			/* internal size (always varlena) */
1541 				   TYPTYPE_RANGE,	/* type-type (range type) */
1542 				   TYPCATEGORY_RANGE,	/* type-category (range type) */
1543 				   false,		/* range types are never preferred */
1544 				   DEFAULT_TYPDELIM,	/* array element delimiter */
1545 				   F_RANGE_IN,	/* input procedure */
1546 				   F_RANGE_OUT, /* output procedure */
1547 				   F_RANGE_RECV,	/* receive procedure */
1548 				   F_RANGE_SEND,	/* send procedure */
1549 				   InvalidOid,	/* typmodin procedure - none */
1550 				   InvalidOid,	/* typmodout procedure - none */
1551 				   F_RANGE_TYPANALYZE,	/* analyze procedure */
1552 				   InvalidOid,	/* subscript procedure - none */
1553 				   InvalidOid,	/* element type ID - none */
1554 				   false,		/* this is not an array type */
1555 				   rangeArrayOid,	/* array type we are about to create */
1556 				   InvalidOid,	/* base type ID (only for domains) */
1557 				   NULL,		/* never a default type value */
1558 				   NULL,		/* no binary form available either */
1559 				   false,		/* never passed by value */
1560 				   alignment,	/* alignment */
1561 				   TYPSTORAGE_EXTENDED, /* TOAST strategy (always extended) */
1562 				   -1,			/* typMod (Domains only) */
1563 				   0,			/* Array dimensions of typbasetype */
1564 				   false,		/* Type NOT NULL */
1565 				   InvalidOid); /* type's collation (ranges never have one) */
1566 	Assert(typoid == InvalidOid || typoid == address.objectId);
1567 	typoid = address.objectId;
1568 
1569 	/* Create the multirange that goes with it */
1570 	if (multirangeTypeName)
1571 	{
1572 		Oid			old_typoid;
1573 
1574 		/*
1575 		 * Look to see if multirange type already exists.
1576 		 */
1577 		old_typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
1578 									 CStringGetDatum(multirangeTypeName),
1579 									 ObjectIdGetDatum(multirangeNamespace));
1580 
1581 		/*
1582 		 * If it's not a shell, see if it's an autogenerated array type, and
1583 		 * if so rename it out of the way.
1584 		 */
1585 		if (OidIsValid(old_typoid) && get_typisdefined(old_typoid))
1586 		{
1587 			if (!moveArrayTypeName(old_typoid, multirangeTypeName, multirangeNamespace))
1588 				ereport(ERROR,
1589 						(errcode(ERRCODE_DUPLICATE_OBJECT),
1590 						 errmsg("type \"%s\" already exists", multirangeTypeName)));
1591 		}
1592 	}
1593 	else
1594 	{
1595 		/* Generate multirange name automatically */
1596 		multirangeNamespace = typeNamespace;
1597 		multirangeTypeName = makeMultirangeTypeName(typeName, multirangeNamespace);
1598 	}
1599 
1600 	mltrngaddress =
1601 		TypeCreate(multirangeOid,	/* force assignment of this type OID */
1602 				   multirangeTypeName,	/* type name */
1603 				   multirangeNamespace, /* namespace */
1604 				   InvalidOid,	/* relation oid (n/a here) */
1605 				   0,			/* relation kind (ditto) */
1606 				   GetUserId(), /* owner's ID */
1607 				   -1,			/* internal size (always varlena) */
1608 				   TYPTYPE_MULTIRANGE,	/* type-type (multirange type) */
1609 				   TYPCATEGORY_RANGE,	/* type-category (range type) */
1610 				   false,		/* multirange types are never preferred */
1611 				   DEFAULT_TYPDELIM,	/* array element delimiter */
1612 				   F_MULTIRANGE_IN, /* input procedure */
1613 				   F_MULTIRANGE_OUT,	/* output procedure */
1614 				   F_MULTIRANGE_RECV,	/* receive procedure */
1615 				   F_MULTIRANGE_SEND,	/* send procedure */
1616 				   InvalidOid,	/* typmodin procedure - none */
1617 				   InvalidOid,	/* typmodout procedure - none */
1618 				   F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
1619 				   InvalidOid,	/* subscript procedure - none */
1620 				   InvalidOid,	/* element type ID - none */
1621 				   false,		/* this is not an array type */
1622 				   multirangeArrayOid,	/* array type we are about to create */
1623 				   InvalidOid,	/* base type ID (only for domains) */
1624 				   NULL,		/* never a default type value */
1625 				   NULL,		/* no binary form available either */
1626 				   false,		/* never passed by value */
1627 				   alignment,	/* alignment */
1628 				   'x',			/* TOAST strategy (always extended) */
1629 				   -1,			/* typMod (Domains only) */
1630 				   0,			/* Array dimensions of typbasetype */
1631 				   false,		/* Type NOT NULL */
1632 				   InvalidOid); /* type's collation (ranges never have one) */
1633 	Assert(multirangeOid == mltrngaddress.objectId);
1634 
1635 	/* Create the entry in pg_range */
1636 	RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
1637 				rangeCanonical, rangeSubtypeDiff, multirangeOid);
1638 
1639 	/*
1640 	 * Create the array type that goes with it.
1641 	 */
1642 	rangeArrayName = makeArrayTypeName(typeName, typeNamespace);
1643 
1644 	TypeCreate(rangeArrayOid,	/* force assignment of this type OID */
1645 			   rangeArrayName,	/* type name */
1646 			   typeNamespace,	/* namespace */
1647 			   InvalidOid,		/* relation oid (n/a here) */
1648 			   0,				/* relation kind (ditto) */
1649 			   GetUserId(),		/* owner's ID */
1650 			   -1,				/* internal size (always varlena) */
1651 			   TYPTYPE_BASE,	/* type-type (base type) */
1652 			   TYPCATEGORY_ARRAY,	/* type-category (array) */
1653 			   false,			/* array types are never preferred */
1654 			   DEFAULT_TYPDELIM,	/* array element delimiter */
1655 			   F_ARRAY_IN,		/* input procedure */
1656 			   F_ARRAY_OUT,		/* output procedure */
1657 			   F_ARRAY_RECV,	/* receive procedure */
1658 			   F_ARRAY_SEND,	/* send procedure */
1659 			   InvalidOid,		/* typmodin procedure - none */
1660 			   InvalidOid,		/* typmodout procedure - none */
1661 			   F_ARRAY_TYPANALYZE,	/* analyze procedure */
1662 			   F_ARRAY_SUBSCRIPT_HANDLER,	/* array subscript procedure */
1663 			   typoid,			/* element type ID */
1664 			   true,			/* yes this is an array type */
1665 			   InvalidOid,		/* no further array type */
1666 			   InvalidOid,		/* base type ID */
1667 			   NULL,			/* never a default type value */
1668 			   NULL,			/* binary default isn't sent either */
1669 			   false,			/* never passed by value */
1670 			   alignment,		/* alignment - same as range's */
1671 			   TYPSTORAGE_EXTENDED, /* ARRAY is always toastable */
1672 			   -1,				/* typMod (Domains only) */
1673 			   0,				/* Array dimensions of typbasetype */
1674 			   false,			/* Type NOT NULL */
1675 			   InvalidOid);		/* typcollation */
1676 
1677 	pfree(rangeArrayName);
1678 
1679 	/* Create the multirange's array type */
1680 
1681 	multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
1682 
1683 	TypeCreate(multirangeArrayOid,	/* force assignment of this type OID */
1684 			   multirangeArrayName, /* type name */
1685 			   multirangeNamespace, /* namespace */
1686 			   InvalidOid,		/* relation oid (n/a here) */
1687 			   0,				/* relation kind (ditto) */
1688 			   GetUserId(),		/* owner's ID */
1689 			   -1,				/* internal size (always varlena) */
1690 			   TYPTYPE_BASE,	/* type-type (base type) */
1691 			   TYPCATEGORY_ARRAY,	/* type-category (array) */
1692 			   false,			/* array types are never preferred */
1693 			   DEFAULT_TYPDELIM,	/* array element delimiter */
1694 			   F_ARRAY_IN,		/* input procedure */
1695 			   F_ARRAY_OUT,		/* output procedure */
1696 			   F_ARRAY_RECV,	/* receive procedure */
1697 			   F_ARRAY_SEND,	/* send procedure */
1698 			   InvalidOid,		/* typmodin procedure - none */
1699 			   InvalidOid,		/* typmodout procedure - none */
1700 			   F_ARRAY_TYPANALYZE,	/* analyze procedure */
1701 			   F_ARRAY_SUBSCRIPT_HANDLER,	/* array subscript procedure */
1702 			   multirangeOid,	/* element type ID */
1703 			   true,			/* yes this is an array type */
1704 			   InvalidOid,		/* no further array type */
1705 			   InvalidOid,		/* base type ID */
1706 			   NULL,			/* never a default type value */
1707 			   NULL,			/* binary default isn't sent either */
1708 			   false,			/* never passed by value */
1709 			   alignment,		/* alignment - same as range's */
1710 			   'x',				/* ARRAY is always toastable */
1711 			   -1,				/* typMod (Domains only) */
1712 			   0,				/* Array dimensions of typbasetype */
1713 			   false,			/* Type NOT NULL */
1714 			   InvalidOid);		/* typcollation */
1715 
1716 	/* And create the constructor functions for this range type */
1717 	makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
1718 	makeMultirangeConstructors(multirangeTypeName, typeNamespace,
1719 							   multirangeOid, typoid, rangeArrayOid,
1720 							   &castFuncOid);
1721 
1722 	/* Create cast from the range type to its multirange type */
1723 	CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
1724 
1725 	pfree(multirangeArrayName);
1726 
1727 	return address;
1728 }
1729 
1730 /*
1731  * Because there may exist several range types over the same subtype, the
1732  * range type can't be uniquely determined from the subtype.  So it's
1733  * impossible to define a polymorphic constructor; we have to generate new
1734  * constructor functions explicitly for each range type.
1735  *
1736  * We actually define 4 functions, with 0 through 3 arguments.  This is just
1737  * to offer more convenience for the user.
1738  */
1739 static void
makeRangeConstructors(const char * name,Oid namespace,Oid rangeOid,Oid subtype)1740 makeRangeConstructors(const char *name, Oid namespace,
1741 					  Oid rangeOid, Oid subtype)
1742 {
1743 	static const char *const prosrc[2] = {"range_constructor2",
1744 	"range_constructor3"};
1745 	static const int pronargs[2] = {2, 3};
1746 
1747 	Oid			constructorArgTypes[3];
1748 	ObjectAddress myself,
1749 				referenced;
1750 	int			i;
1751 
1752 	constructorArgTypes[0] = subtype;
1753 	constructorArgTypes[1] = subtype;
1754 	constructorArgTypes[2] = TEXTOID;
1755 
1756 	referenced.classId = TypeRelationId;
1757 	referenced.objectId = rangeOid;
1758 	referenced.objectSubId = 0;
1759 
1760 	for (i = 0; i < lengthof(prosrc); i++)
1761 	{
1762 		oidvector  *constructorArgTypesVector;
1763 
1764 		constructorArgTypesVector = buildoidvector(constructorArgTypes,
1765 												   pronargs[i]);
1766 
1767 		myself = ProcedureCreate(name,	/* name: same as range type */
1768 								 namespace, /* namespace */
1769 								 false, /* replace */
1770 								 false, /* returns set */
1771 								 rangeOid,	/* return type */
1772 								 BOOTSTRAP_SUPERUSERID, /* proowner */
1773 								 INTERNALlanguageId,	/* language */
1774 								 F_FMGR_INTERNAL_VALIDATOR, /* language validator */
1775 								 prosrc[i], /* prosrc */
1776 								 NULL,	/* probin */
1777 								 NULL,	/* prosqlbody */
1778 								 PROKIND_FUNCTION,
1779 								 false, /* security_definer */
1780 								 false, /* leakproof */
1781 								 false, /* isStrict */
1782 								 PROVOLATILE_IMMUTABLE, /* volatility */
1783 								 PROPARALLEL_SAFE,	/* parallel safety */
1784 								 constructorArgTypesVector, /* parameterTypes */
1785 								 PointerGetDatum(NULL), /* allParameterTypes */
1786 								 PointerGetDatum(NULL), /* parameterModes */
1787 								 PointerGetDatum(NULL), /* parameterNames */
1788 								 NIL,	/* parameterDefaults */
1789 								 PointerGetDatum(NULL), /* trftypes */
1790 								 PointerGetDatum(NULL), /* proconfig */
1791 								 InvalidOid,	/* prosupport */
1792 								 1.0,	/* procost */
1793 								 0.0);	/* prorows */
1794 
1795 		/*
1796 		 * Make the constructors internally-dependent on the range type so
1797 		 * that they go away silently when the type is dropped.  Note that
1798 		 * pg_dump depends on this choice to avoid dumping the constructors.
1799 		 */
1800 		recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1801 	}
1802 }
1803 
1804 /*
1805  * We make a separate multirange constructor for each range type
1806  * so its name can include the base type, like range constructors do.
1807  * If we had an anyrangearray polymorphic type we could use it here,
1808  * but since each type has its own constructor name there's no need.
1809  *
1810  * Sets castFuncOid to the oid of the new constructor that can be used
1811  * to cast from a range to a multirange.
1812  */
1813 static void
makeMultirangeConstructors(const char * name,Oid namespace,Oid multirangeOid,Oid rangeOid,Oid rangeArrayOid,Oid * castFuncOid)1814 makeMultirangeConstructors(const char *name, Oid namespace,
1815 						   Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
1816 						   Oid *castFuncOid)
1817 {
1818 	ObjectAddress myself,
1819 				referenced;
1820 	oidvector  *argtypes;
1821 	Datum		allParamTypes;
1822 	ArrayType  *allParameterTypes;
1823 	Datum		paramModes;
1824 	ArrayType  *parameterModes;
1825 
1826 	referenced.classId = TypeRelationId;
1827 	referenced.objectId = multirangeOid;
1828 	referenced.objectSubId = 0;
1829 
1830 	/* 0-arg constructor - for empty multiranges */
1831 	argtypes = buildoidvector(NULL, 0);
1832 	myself = ProcedureCreate(name,	/* name: same as multirange type */
1833 							 namespace,
1834 							 false, /* replace */
1835 							 false, /* returns set */
1836 							 multirangeOid, /* return type */
1837 							 BOOTSTRAP_SUPERUSERID, /* proowner */
1838 							 INTERNALlanguageId,	/* language */
1839 							 F_FMGR_INTERNAL_VALIDATOR,
1840 							 "multirange_constructor0", /* prosrc */
1841 							 NULL,	/* probin */
1842 							 NULL,	/* prosqlbody */
1843 							 PROKIND_FUNCTION,
1844 							 false, /* security_definer */
1845 							 false, /* leakproof */
1846 							 true,	/* isStrict */
1847 							 PROVOLATILE_IMMUTABLE, /* volatility */
1848 							 PROPARALLEL_SAFE,	/* parallel safety */
1849 							 argtypes,	/* parameterTypes */
1850 							 PointerGetDatum(NULL), /* allParameterTypes */
1851 							 PointerGetDatum(NULL), /* parameterModes */
1852 							 PointerGetDatum(NULL), /* parameterNames */
1853 							 NIL,	/* parameterDefaults */
1854 							 PointerGetDatum(NULL), /* trftypes */
1855 							 PointerGetDatum(NULL), /* proconfig */
1856 							 InvalidOid,	/* prosupport */
1857 							 1.0,	/* procost */
1858 							 0.0);	/* prorows */
1859 
1860 	/*
1861 	 * Make the constructor internally-dependent on the multirange type so
1862 	 * that they go away silently when the type is dropped.  Note that pg_dump
1863 	 * depends on this choice to avoid dumping the constructors.
1864 	 */
1865 	recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1866 	pfree(argtypes);
1867 
1868 	/*
1869 	 * 1-arg constructor - for casts
1870 	 *
1871 	 * In theory we shouldn't need both this and the vararg (n-arg)
1872 	 * constructor, but having a separate 1-arg function lets us define casts
1873 	 * against it.
1874 	 */
1875 	argtypes = buildoidvector(&rangeOid, 1);
1876 	myself = ProcedureCreate(name,	/* name: same as multirange type */
1877 							 namespace,
1878 							 false, /* replace */
1879 							 false, /* returns set */
1880 							 multirangeOid, /* return type */
1881 							 BOOTSTRAP_SUPERUSERID, /* proowner */
1882 							 INTERNALlanguageId,	/* language */
1883 							 F_FMGR_INTERNAL_VALIDATOR,
1884 							 "multirange_constructor1", /* prosrc */
1885 							 NULL,	/* probin */
1886 							 NULL,	/* prosqlbody */
1887 							 PROKIND_FUNCTION,
1888 							 false, /* security_definer */
1889 							 false, /* leakproof */
1890 							 true,	/* isStrict */
1891 							 PROVOLATILE_IMMUTABLE, /* volatility */
1892 							 PROPARALLEL_SAFE,	/* parallel safety */
1893 							 argtypes,	/* parameterTypes */
1894 							 PointerGetDatum(NULL), /* allParameterTypes */
1895 							 PointerGetDatum(NULL), /* parameterModes */
1896 							 PointerGetDatum(NULL), /* parameterNames */
1897 							 NIL,	/* parameterDefaults */
1898 							 PointerGetDatum(NULL), /* trftypes */
1899 							 PointerGetDatum(NULL), /* proconfig */
1900 							 InvalidOid,	/* prosupport */
1901 							 1.0,	/* procost */
1902 							 0.0);	/* prorows */
1903 	/* ditto */
1904 	recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1905 	pfree(argtypes);
1906 	*castFuncOid = myself.objectId;
1907 
1908 	/* n-arg constructor - vararg */
1909 	argtypes = buildoidvector(&rangeArrayOid, 1);
1910 	allParamTypes = ObjectIdGetDatum(rangeArrayOid);
1911 	allParameterTypes = construct_array(&allParamTypes,
1912 										1, OIDOID,
1913 										sizeof(Oid), true, 'i');
1914 	paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
1915 	parameterModes = construct_array(&paramModes, 1, CHAROID,
1916 									 1, true, 'c');
1917 	myself = ProcedureCreate(name,	/* name: same as multirange type */
1918 							 namespace,
1919 							 false, /* replace */
1920 							 false, /* returns set */
1921 							 multirangeOid, /* return type */
1922 							 BOOTSTRAP_SUPERUSERID, /* proowner */
1923 							 INTERNALlanguageId,	/* language */
1924 							 F_FMGR_INTERNAL_VALIDATOR,
1925 							 "multirange_constructor2", /* prosrc */
1926 							 NULL,	/* probin */
1927 							 NULL,	/* prosqlbody */
1928 							 PROKIND_FUNCTION,
1929 							 false, /* security_definer */
1930 							 false, /* leakproof */
1931 							 true,	/* isStrict */
1932 							 PROVOLATILE_IMMUTABLE, /* volatility */
1933 							 PROPARALLEL_SAFE,	/* parallel safety */
1934 							 argtypes,	/* parameterTypes */
1935 							 PointerGetDatum(allParameterTypes),	/* allParameterTypes */
1936 							 PointerGetDatum(parameterModes),	/* parameterModes */
1937 							 PointerGetDatum(NULL), /* parameterNames */
1938 							 NIL,	/* parameterDefaults */
1939 							 PointerGetDatum(NULL), /* trftypes */
1940 							 PointerGetDatum(NULL), /* proconfig */
1941 							 InvalidOid,	/* prosupport */
1942 							 1.0,	/* procost */
1943 							 0.0);	/* prorows */
1944 	/* ditto */
1945 	recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
1946 	pfree(argtypes);
1947 	pfree(allParameterTypes);
1948 	pfree(parameterModes);
1949 }
1950 
1951 /*
1952  * Find suitable I/O and other support functions for a type.
1953  *
1954  * typeOid is the type's OID (which will already exist, if only as a shell
1955  * type).
1956  */
1957 
1958 static Oid
findTypeInputFunction(List * procname,Oid typeOid)1959 findTypeInputFunction(List *procname, Oid typeOid)
1960 {
1961 	Oid			argList[3];
1962 	Oid			procOid;
1963 	Oid			procOid2;
1964 
1965 	/*
1966 	 * Input functions can take a single argument of type CSTRING, or three
1967 	 * arguments (string, typioparam OID, typmod).  Whine about ambiguity if
1968 	 * both forms exist.
1969 	 */
1970 	argList[0] = CSTRINGOID;
1971 	argList[1] = OIDOID;
1972 	argList[2] = INT4OID;
1973 
1974 	procOid = LookupFuncName(procname, 1, argList, true);
1975 	procOid2 = LookupFuncName(procname, 3, argList, true);
1976 	if (OidIsValid(procOid))
1977 	{
1978 		if (OidIsValid(procOid2))
1979 			ereport(ERROR,
1980 					(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
1981 					 errmsg("type input function %s has multiple matches",
1982 							NameListToString(procname))));
1983 	}
1984 	else
1985 	{
1986 		procOid = procOid2;
1987 		/* If not found, reference the 1-argument signature in error msg */
1988 		if (!OidIsValid(procOid))
1989 			ereport(ERROR,
1990 					(errcode(ERRCODE_UNDEFINED_FUNCTION),
1991 					 errmsg("function %s does not exist",
1992 							func_signature_string(procname, 1, NIL, argList))));
1993 	}
1994 
1995 	/* Input functions must return the target type. */
1996 	if (get_func_rettype(procOid) != typeOid)
1997 		ereport(ERROR,
1998 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1999 				 errmsg("type input function %s must return type %s",
2000 						NameListToString(procname), format_type_be(typeOid))));
2001 
2002 	/*
2003 	 * Print warnings if any of the type's I/O functions are marked volatile.
2004 	 * There is a general assumption that I/O functions are stable or
2005 	 * immutable; this allows us for example to mark record_in/record_out
2006 	 * stable rather than volatile.  Ideally we would throw errors not just
2007 	 * warnings here; but since this check is new as of 9.5, and since the
2008 	 * volatility marking might be just an error-of-omission and not a true
2009 	 * indication of how the function behaves, we'll let it pass as a warning
2010 	 * for now.
2011 	 */
2012 	if (func_volatile(procOid) == PROVOLATILE_VOLATILE)
2013 		ereport(WARNING,
2014 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2015 				 errmsg("type input function %s should not be volatile",
2016 						NameListToString(procname))));
2017 
2018 	return procOid;
2019 }
2020 
2021 static Oid
findTypeOutputFunction(List * procname,Oid typeOid)2022 findTypeOutputFunction(List *procname, Oid typeOid)
2023 {
2024 	Oid			argList[1];
2025 	Oid			procOid;
2026 
2027 	/*
2028 	 * Output functions always take a single argument of the type and return
2029 	 * cstring.
2030 	 */
2031 	argList[0] = typeOid;
2032 
2033 	procOid = LookupFuncName(procname, 1, argList, true);
2034 	if (!OidIsValid(procOid))
2035 		ereport(ERROR,
2036 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
2037 				 errmsg("function %s does not exist",
2038 						func_signature_string(procname, 1, NIL, argList))));
2039 
2040 	if (get_func_rettype(procOid) != CSTRINGOID)
2041 		ereport(ERROR,
2042 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2043 				 errmsg("type output function %s must return type %s",
2044 						NameListToString(procname), "cstring")));
2045 
2046 	/* Just a warning for now, per comments in findTypeInputFunction */
2047 	if (func_volatile(procOid) == PROVOLATILE_VOLATILE)
2048 		ereport(WARNING,
2049 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2050 				 errmsg("type output function %s should not be volatile",
2051 						NameListToString(procname))));
2052 
2053 	return procOid;
2054 }
2055 
2056 static Oid
findTypeReceiveFunction(List * procname,Oid typeOid)2057 findTypeReceiveFunction(List *procname, Oid typeOid)
2058 {
2059 	Oid			argList[3];
2060 	Oid			procOid;
2061 	Oid			procOid2;
2062 
2063 	/*
2064 	 * Receive functions can take a single argument of type INTERNAL, or three
2065 	 * arguments (internal, typioparam OID, typmod).  Whine about ambiguity if
2066 	 * both forms exist.
2067 	 */
2068 	argList[0] = INTERNALOID;
2069 	argList[1] = OIDOID;
2070 	argList[2] = INT4OID;
2071 
2072 	procOid = LookupFuncName(procname, 1, argList, true);
2073 	procOid2 = LookupFuncName(procname, 3, argList, true);
2074 	if (OidIsValid(procOid))
2075 	{
2076 		if (OidIsValid(procOid2))
2077 			ereport(ERROR,
2078 					(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
2079 					 errmsg("type receive function %s has multiple matches",
2080 							NameListToString(procname))));
2081 	}
2082 	else
2083 	{
2084 		procOid = procOid2;
2085 		/* If not found, reference the 1-argument signature in error msg */
2086 		if (!OidIsValid(procOid))
2087 			ereport(ERROR,
2088 					(errcode(ERRCODE_UNDEFINED_FUNCTION),
2089 					 errmsg("function %s does not exist",
2090 							func_signature_string(procname, 1, NIL, argList))));
2091 	}
2092 
2093 	/* Receive functions must return the target type. */
2094 	if (get_func_rettype(procOid) != typeOid)
2095 		ereport(ERROR,
2096 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2097 				 errmsg("type receive function %s must return type %s",
2098 						NameListToString(procname), format_type_be(typeOid))));
2099 
2100 	/* Just a warning for now, per comments in findTypeInputFunction */
2101 	if (func_volatile(procOid) == PROVOLATILE_VOLATILE)
2102 		ereport(WARNING,
2103 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2104 				 errmsg("type receive function %s should not be volatile",
2105 						NameListToString(procname))));
2106 
2107 	return procOid;
2108 }
2109 
2110 static Oid
findTypeSendFunction(List * procname,Oid typeOid)2111 findTypeSendFunction(List *procname, Oid typeOid)
2112 {
2113 	Oid			argList[1];
2114 	Oid			procOid;
2115 
2116 	/*
2117 	 * Send functions always take a single argument of the type and return
2118 	 * bytea.
2119 	 */
2120 	argList[0] = typeOid;
2121 
2122 	procOid = LookupFuncName(procname, 1, argList, true);
2123 	if (!OidIsValid(procOid))
2124 		ereport(ERROR,
2125 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
2126 				 errmsg("function %s does not exist",
2127 						func_signature_string(procname, 1, NIL, argList))));
2128 
2129 	if (get_func_rettype(procOid) != BYTEAOID)
2130 		ereport(ERROR,
2131 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2132 				 errmsg("type send function %s must return type %s",
2133 						NameListToString(procname), "bytea")));
2134 
2135 	/* Just a warning for now, per comments in findTypeInputFunction */
2136 	if (func_volatile(procOid) == PROVOLATILE_VOLATILE)
2137 		ereport(WARNING,
2138 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2139 				 errmsg("type send function %s should not be volatile",
2140 						NameListToString(procname))));
2141 
2142 	return procOid;
2143 }
2144 
2145 static Oid
findTypeTypmodinFunction(List * procname)2146 findTypeTypmodinFunction(List *procname)
2147 {
2148 	Oid			argList[1];
2149 	Oid			procOid;
2150 
2151 	/*
2152 	 * typmodin functions always take one cstring[] argument and return int4.
2153 	 */
2154 	argList[0] = CSTRINGARRAYOID;
2155 
2156 	procOid = LookupFuncName(procname, 1, argList, true);
2157 	if (!OidIsValid(procOid))
2158 		ereport(ERROR,
2159 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
2160 				 errmsg("function %s does not exist",
2161 						func_signature_string(procname, 1, NIL, argList))));
2162 
2163 	if (get_func_rettype(procOid) != INT4OID)
2164 		ereport(ERROR,
2165 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2166 				 errmsg("typmod_in function %s must return type %s",
2167 						NameListToString(procname), "integer")));
2168 
2169 	/* Just a warning for now, per comments in findTypeInputFunction */
2170 	if (func_volatile(procOid) == PROVOLATILE_VOLATILE)
2171 		ereport(WARNING,
2172 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2173 				 errmsg("type modifier input function %s should not be volatile",
2174 						NameListToString(procname))));
2175 
2176 	return procOid;
2177 }
2178 
2179 static Oid
findTypeTypmodoutFunction(List * procname)2180 findTypeTypmodoutFunction(List *procname)
2181 {
2182 	Oid			argList[1];
2183 	Oid			procOid;
2184 
2185 	/*
2186 	 * typmodout functions always take one int4 argument and return cstring.
2187 	 */
2188 	argList[0] = INT4OID;
2189 
2190 	procOid = LookupFuncName(procname, 1, argList, true);
2191 	if (!OidIsValid(procOid))
2192 		ereport(ERROR,
2193 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
2194 				 errmsg("function %s does not exist",
2195 						func_signature_string(procname, 1, NIL, argList))));
2196 
2197 	if (get_func_rettype(procOid) != CSTRINGOID)
2198 		ereport(ERROR,
2199 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2200 				 errmsg("typmod_out function %s must return type %s",
2201 						NameListToString(procname), "cstring")));
2202 
2203 	/* Just a warning for now, per comments in findTypeInputFunction */
2204 	if (func_volatile(procOid) == PROVOLATILE_VOLATILE)
2205 		ereport(WARNING,
2206 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2207 				 errmsg("type modifier output function %s should not be volatile",
2208 						NameListToString(procname))));
2209 
2210 	return procOid;
2211 }
2212 
2213 static Oid
findTypeAnalyzeFunction(List * procname,Oid typeOid)2214 findTypeAnalyzeFunction(List *procname, Oid typeOid)
2215 {
2216 	Oid			argList[1];
2217 	Oid			procOid;
2218 
2219 	/*
2220 	 * Analyze functions always take one INTERNAL argument and return bool.
2221 	 */
2222 	argList[0] = INTERNALOID;
2223 
2224 	procOid = LookupFuncName(procname, 1, argList, true);
2225 	if (!OidIsValid(procOid))
2226 		ereport(ERROR,
2227 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
2228 				 errmsg("function %s does not exist",
2229 						func_signature_string(procname, 1, NIL, argList))));
2230 
2231 	if (get_func_rettype(procOid) != BOOLOID)
2232 		ereport(ERROR,
2233 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2234 				 errmsg("type analyze function %s must return type %s",
2235 						NameListToString(procname), "boolean")));
2236 
2237 	return procOid;
2238 }
2239 
2240 static Oid
findTypeSubscriptingFunction(List * procname,Oid typeOid)2241 findTypeSubscriptingFunction(List *procname, Oid typeOid)
2242 {
2243 	Oid			argList[1];
2244 	Oid			procOid;
2245 
2246 	/*
2247 	 * Subscripting support functions always take one INTERNAL argument and
2248 	 * return INTERNAL.  (The argument is not used, but we must have it to
2249 	 * maintain type safety.)
2250 	 */
2251 	argList[0] = INTERNALOID;
2252 
2253 	procOid = LookupFuncName(procname, 1, argList, true);
2254 	if (!OidIsValid(procOid))
2255 		ereport(ERROR,
2256 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
2257 				 errmsg("function %s does not exist",
2258 						func_signature_string(procname, 1, NIL, argList))));
2259 
2260 	if (get_func_rettype(procOid) != INTERNALOID)
2261 		ereport(ERROR,
2262 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2263 				 errmsg("type subscripting function %s must return type %s",
2264 						NameListToString(procname), "internal")));
2265 
2266 	/*
2267 	 * We disallow array_subscript_handler() from being selected explicitly,
2268 	 * since that must only be applied to autogenerated array types.
2269 	 */
2270 	if (procOid == F_ARRAY_SUBSCRIPT_HANDLER)
2271 		ereport(ERROR,
2272 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2273 				 errmsg("user-defined types cannot use subscripting function %s",
2274 						NameListToString(procname))));
2275 
2276 	return procOid;
2277 }
2278 
2279 /*
2280  * Find suitable support functions and opclasses for a range type.
2281  */
2282 
2283 /*
2284  * Find named btree opclass for subtype, or default btree opclass if
2285  * opcname is NIL.
2286  */
2287 static Oid
findRangeSubOpclass(List * opcname,Oid subtype)2288 findRangeSubOpclass(List *opcname, Oid subtype)
2289 {
2290 	Oid			opcid;
2291 	Oid			opInputType;
2292 
2293 	if (opcname != NIL)
2294 	{
2295 		opcid = get_opclass_oid(BTREE_AM_OID, opcname, false);
2296 
2297 		/*
2298 		 * Verify that the operator class accepts this datatype. Note we will
2299 		 * accept binary compatibility.
2300 		 */
2301 		opInputType = get_opclass_input_type(opcid);
2302 		if (!IsBinaryCoercible(subtype, opInputType))
2303 			ereport(ERROR,
2304 					(errcode(ERRCODE_DATATYPE_MISMATCH),
2305 					 errmsg("operator class \"%s\" does not accept data type %s",
2306 							NameListToString(opcname),
2307 							format_type_be(subtype))));
2308 	}
2309 	else
2310 	{
2311 		opcid = GetDefaultOpClass(subtype, BTREE_AM_OID);
2312 		if (!OidIsValid(opcid))
2313 		{
2314 			/* We spell the error message identically to ResolveOpClass */
2315 			ereport(ERROR,
2316 					(errcode(ERRCODE_UNDEFINED_OBJECT),
2317 					 errmsg("data type %s has no default operator class for access method \"%s\"",
2318 							format_type_be(subtype), "btree"),
2319 					 errhint("You must specify an operator class for the range type or define a default operator class for the subtype.")));
2320 		}
2321 	}
2322 
2323 	return opcid;
2324 }
2325 
2326 static Oid
findRangeCanonicalFunction(List * procname,Oid typeOid)2327 findRangeCanonicalFunction(List *procname, Oid typeOid)
2328 {
2329 	Oid			argList[1];
2330 	Oid			procOid;
2331 	AclResult	aclresult;
2332 
2333 	/*
2334 	 * Range canonical functions must take and return the range type, and must
2335 	 * be immutable.
2336 	 */
2337 	argList[0] = typeOid;
2338 
2339 	procOid = LookupFuncName(procname, 1, argList, true);
2340 
2341 	if (!OidIsValid(procOid))
2342 		ereport(ERROR,
2343 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
2344 				 errmsg("function %s does not exist",
2345 						func_signature_string(procname, 1, NIL, argList))));
2346 
2347 	if (get_func_rettype(procOid) != typeOid)
2348 		ereport(ERROR,
2349 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2350 				 errmsg("range canonical function %s must return range type",
2351 						func_signature_string(procname, 1, NIL, argList))));
2352 
2353 	if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
2354 		ereport(ERROR,
2355 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2356 				 errmsg("range canonical function %s must be immutable",
2357 						func_signature_string(procname, 1, NIL, argList))));
2358 
2359 	/* Also, range type's creator must have permission to call function */
2360 	aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
2361 	if (aclresult != ACLCHECK_OK)
2362 		aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(procOid));
2363 
2364 	return procOid;
2365 }
2366 
2367 static Oid
findRangeSubtypeDiffFunction(List * procname,Oid subtype)2368 findRangeSubtypeDiffFunction(List *procname, Oid subtype)
2369 {
2370 	Oid			argList[2];
2371 	Oid			procOid;
2372 	AclResult	aclresult;
2373 
2374 	/*
2375 	 * Range subtype diff functions must take two arguments of the subtype,
2376 	 * must return float8, and must be immutable.
2377 	 */
2378 	argList[0] = subtype;
2379 	argList[1] = subtype;
2380 
2381 	procOid = LookupFuncName(procname, 2, argList, true);
2382 
2383 	if (!OidIsValid(procOid))
2384 		ereport(ERROR,
2385 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
2386 				 errmsg("function %s does not exist",
2387 						func_signature_string(procname, 2, NIL, argList))));
2388 
2389 	if (get_func_rettype(procOid) != FLOAT8OID)
2390 		ereport(ERROR,
2391 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2392 				 errmsg("range subtype diff function %s must return type %s",
2393 						func_signature_string(procname, 2, NIL, argList),
2394 						"double precision")));
2395 
2396 	if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
2397 		ereport(ERROR,
2398 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2399 				 errmsg("range subtype diff function %s must be immutable",
2400 						func_signature_string(procname, 2, NIL, argList))));
2401 
2402 	/* Also, range type's creator must have permission to call function */
2403 	aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
2404 	if (aclresult != ACLCHECK_OK)
2405 		aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(procOid));
2406 
2407 	return procOid;
2408 }
2409 
2410 /*
2411  *	AssignTypeArrayOid
2412  *
2413  *	Pre-assign the type's array OID for use in pg_type.typarray
2414  */
2415 Oid
AssignTypeArrayOid(void)2416 AssignTypeArrayOid(void)
2417 {
2418 	Oid			type_array_oid;
2419 
2420 	/* Use binary-upgrade override for pg_type.typarray? */
2421 	if (IsBinaryUpgrade)
2422 	{
2423 		if (!OidIsValid(binary_upgrade_next_array_pg_type_oid))
2424 			ereport(ERROR,
2425 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2426 					 errmsg("pg_type array OID value not set when in binary upgrade mode")));
2427 
2428 		type_array_oid = binary_upgrade_next_array_pg_type_oid;
2429 		binary_upgrade_next_array_pg_type_oid = InvalidOid;
2430 	}
2431 	else
2432 	{
2433 		Relation	pg_type = table_open(TypeRelationId, AccessShareLock);
2434 
2435 		type_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
2436 											Anum_pg_type_oid);
2437 		table_close(pg_type, AccessShareLock);
2438 	}
2439 
2440 	return type_array_oid;
2441 }
2442 
2443 /*
2444  *	AssignTypeMultirangeOid
2445  *
2446  *	Pre-assign the range type's multirange OID for use in pg_type.oid
2447  */
2448 Oid
AssignTypeMultirangeOid(void)2449 AssignTypeMultirangeOid(void)
2450 {
2451 	Oid			type_multirange_oid;
2452 
2453 	/* Use binary-upgrade override for pg_type.oid? */
2454 	if (IsBinaryUpgrade)
2455 	{
2456 		if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
2457 			ereport(ERROR,
2458 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2459 					 errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
2460 
2461 		type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
2462 		binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
2463 	}
2464 	else
2465 	{
2466 		Relation	pg_type = table_open(TypeRelationId, AccessShareLock);
2467 
2468 		type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
2469 												 Anum_pg_type_oid);
2470 		table_close(pg_type, AccessShareLock);
2471 	}
2472 
2473 	return type_multirange_oid;
2474 }
2475 
2476 /*
2477  *	AssignTypeMultirangeArrayOid
2478  *
2479  *	Pre-assign the range type's multirange array OID for use in pg_type.typarray
2480  */
2481 Oid
AssignTypeMultirangeArrayOid(void)2482 AssignTypeMultirangeArrayOid(void)
2483 {
2484 	Oid			type_multirange_array_oid;
2485 
2486 	/* Use binary-upgrade override for pg_type.oid? */
2487 	if (IsBinaryUpgrade)
2488 	{
2489 		if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
2490 			ereport(ERROR,
2491 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2492 					 errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
2493 
2494 		type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
2495 		binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
2496 	}
2497 	else
2498 	{
2499 		Relation	pg_type = table_open(TypeRelationId, AccessShareLock);
2500 
2501 		type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
2502 													   Anum_pg_type_oid);
2503 		table_close(pg_type, AccessShareLock);
2504 	}
2505 
2506 	return type_multirange_array_oid;
2507 }
2508 
2509 
2510 /*-------------------------------------------------------------------
2511  * DefineCompositeType
2512  *
2513  * Create a Composite Type relation.
2514  * `DefineRelation' does all the work, we just provide the correct
2515  * arguments!
2516  *
2517  * If the relation already exists, then 'DefineRelation' will abort
2518  * the xact...
2519  *
2520  * Return type is the new type's object address.
2521  *-------------------------------------------------------------------
2522  */
2523 ObjectAddress
DefineCompositeType(RangeVar * typevar,List * coldeflist)2524 DefineCompositeType(RangeVar *typevar, List *coldeflist)
2525 {
2526 	CreateStmt *createStmt = makeNode(CreateStmt);
2527 	Oid			old_type_oid;
2528 	Oid			typeNamespace;
2529 	ObjectAddress address;
2530 
2531 	/*
2532 	 * now set the parameters for keys/inheritance etc. All of these are
2533 	 * uninteresting for composite types...
2534 	 */
2535 	createStmt->relation = typevar;
2536 	createStmt->tableElts = coldeflist;
2537 	createStmt->inhRelations = NIL;
2538 	createStmt->constraints = NIL;
2539 	createStmt->options = NIL;
2540 	createStmt->oncommit = ONCOMMIT_NOOP;
2541 	createStmt->tablespacename = NULL;
2542 	createStmt->if_not_exists = false;
2543 
2544 	/*
2545 	 * Check for collision with an existing type name. If there is one and
2546 	 * it's an autogenerated array, we can rename it out of the way.  This
2547 	 * check is here mainly to get a better error message about a "type"
2548 	 * instead of below about a "relation".
2549 	 */
2550 	typeNamespace = RangeVarGetAndCheckCreationNamespace(createStmt->relation,
2551 														 NoLock, NULL);
2552 	RangeVarAdjustRelationPersistence(createStmt->relation, typeNamespace);
2553 	old_type_oid =
2554 		GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
2555 						CStringGetDatum(createStmt->relation->relname),
2556 						ObjectIdGetDatum(typeNamespace));
2557 	if (OidIsValid(old_type_oid))
2558 	{
2559 		if (!moveArrayTypeName(old_type_oid, createStmt->relation->relname, typeNamespace))
2560 			ereport(ERROR,
2561 					(errcode(ERRCODE_DUPLICATE_OBJECT),
2562 					 errmsg("type \"%s\" already exists", createStmt->relation->relname)));
2563 	}
2564 
2565 	/*
2566 	 * Finally create the relation.  This also creates the type.
2567 	 */
2568 	DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE, InvalidOid, &address,
2569 				   NULL);
2570 
2571 	return address;
2572 }
2573 
2574 /*
2575  * AlterDomainDefault
2576  *
2577  * Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
2578  *
2579  * Returns ObjectAddress of the modified domain.
2580  */
2581 ObjectAddress
AlterDomainDefault(List * names,Node * defaultRaw)2582 AlterDomainDefault(List *names, Node *defaultRaw)
2583 {
2584 	TypeName   *typename;
2585 	Oid			domainoid;
2586 	HeapTuple	tup;
2587 	ParseState *pstate;
2588 	Relation	rel;
2589 	char	   *defaultValue;
2590 	Node	   *defaultExpr = NULL; /* NULL if no default specified */
2591 	Datum		new_record[Natts_pg_type];
2592 	bool		new_record_nulls[Natts_pg_type];
2593 	bool		new_record_repl[Natts_pg_type];
2594 	HeapTuple	newtuple;
2595 	Form_pg_type typTup;
2596 	ObjectAddress address;
2597 
2598 	/* Make a TypeName so we can use standard type lookup machinery */
2599 	typename = makeTypeNameFromNameList(names);
2600 	domainoid = typenameTypeId(NULL, typename);
2601 
2602 	/* Look up the domain in the type table */
2603 	rel = table_open(TypeRelationId, RowExclusiveLock);
2604 
2605 	tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2606 	if (!HeapTupleIsValid(tup))
2607 		elog(ERROR, "cache lookup failed for type %u", domainoid);
2608 	typTup = (Form_pg_type) GETSTRUCT(tup);
2609 
2610 	/* Check it's a domain and check user has permission for ALTER DOMAIN */
2611 	checkDomainOwner(tup);
2612 
2613 	/* Setup new tuple */
2614 	MemSet(new_record, (Datum) 0, sizeof(new_record));
2615 	MemSet(new_record_nulls, false, sizeof(new_record_nulls));
2616 	MemSet(new_record_repl, false, sizeof(new_record_repl));
2617 
2618 	/* Store the new default into the tuple */
2619 	if (defaultRaw)
2620 	{
2621 		/* Create a dummy ParseState for transformExpr */
2622 		pstate = make_parsestate(NULL);
2623 
2624 		/*
2625 		 * Cook the colDef->raw_expr into an expression. Note: Name is
2626 		 * strictly for error message
2627 		 */
2628 		defaultExpr = cookDefault(pstate, defaultRaw,
2629 								  typTup->typbasetype,
2630 								  typTup->typtypmod,
2631 								  NameStr(typTup->typname),
2632 								  0);
2633 
2634 		/*
2635 		 * If the expression is just a NULL constant, we treat the command
2636 		 * like ALTER ... DROP DEFAULT.  (But see note for same test in
2637 		 * DefineDomain.)
2638 		 */
2639 		if (defaultExpr == NULL ||
2640 			(IsA(defaultExpr, Const) && ((Const *) defaultExpr)->constisnull))
2641 		{
2642 			/* Default is NULL, drop it */
2643 			defaultExpr = NULL;
2644 			new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
2645 			new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
2646 			new_record_nulls[Anum_pg_type_typdefault - 1] = true;
2647 			new_record_repl[Anum_pg_type_typdefault - 1] = true;
2648 		}
2649 		else
2650 		{
2651 			/*
2652 			 * Expression must be stored as a nodeToString result, but we also
2653 			 * require a valid textual representation (mainly to make life
2654 			 * easier for pg_dump).
2655 			 */
2656 			defaultValue = deparse_expression(defaultExpr,
2657 											  NIL, false, false);
2658 
2659 			/*
2660 			 * Form an updated tuple with the new default and write it back.
2661 			 */
2662 			new_record[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(nodeToString(defaultExpr));
2663 
2664 			new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
2665 			new_record[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(defaultValue);
2666 			new_record_repl[Anum_pg_type_typdefault - 1] = true;
2667 		}
2668 	}
2669 	else
2670 	{
2671 		/* ALTER ... DROP DEFAULT */
2672 		new_record_nulls[Anum_pg_type_typdefaultbin - 1] = true;
2673 		new_record_repl[Anum_pg_type_typdefaultbin - 1] = true;
2674 		new_record_nulls[Anum_pg_type_typdefault - 1] = true;
2675 		new_record_repl[Anum_pg_type_typdefault - 1] = true;
2676 	}
2677 
2678 	newtuple = heap_modify_tuple(tup, RelationGetDescr(rel),
2679 								 new_record, new_record_nulls,
2680 								 new_record_repl);
2681 
2682 	CatalogTupleUpdate(rel, &tup->t_self, newtuple);
2683 
2684 	/* Rebuild dependencies */
2685 	GenerateTypeDependencies(newtuple,
2686 							 rel,
2687 							 defaultExpr,
2688 							 NULL,	/* don't have typacl handy */
2689 							 0, /* relation kind is n/a */
2690 							 false, /* a domain isn't an implicit array */
2691 							 false, /* nor is it any kind of dependent type */
2692 							 false, /* don't touch extension membership */
2693 							 true); /* We do need to rebuild dependencies */
2694 
2695 	InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0);
2696 
2697 	ObjectAddressSet(address, TypeRelationId, domainoid);
2698 
2699 	/* Clean up */
2700 	table_close(rel, RowExclusiveLock);
2701 	heap_freetuple(newtuple);
2702 
2703 	return address;
2704 }
2705 
2706 /*
2707  * AlterDomainNotNull
2708  *
2709  * Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
2710  *
2711  * Returns ObjectAddress of the modified domain.
2712  */
2713 ObjectAddress
AlterDomainNotNull(List * names,bool notNull)2714 AlterDomainNotNull(List *names, bool notNull)
2715 {
2716 	TypeName   *typename;
2717 	Oid			domainoid;
2718 	Relation	typrel;
2719 	HeapTuple	tup;
2720 	Form_pg_type typTup;
2721 	ObjectAddress address = InvalidObjectAddress;
2722 
2723 	/* Make a TypeName so we can use standard type lookup machinery */
2724 	typename = makeTypeNameFromNameList(names);
2725 	domainoid = typenameTypeId(NULL, typename);
2726 
2727 	/* Look up the domain in the type table */
2728 	typrel = table_open(TypeRelationId, RowExclusiveLock);
2729 
2730 	tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2731 	if (!HeapTupleIsValid(tup))
2732 		elog(ERROR, "cache lookup failed for type %u", domainoid);
2733 	typTup = (Form_pg_type) GETSTRUCT(tup);
2734 
2735 	/* Check it's a domain and check user has permission for ALTER DOMAIN */
2736 	checkDomainOwner(tup);
2737 
2738 	/* Is the domain already set to the desired constraint? */
2739 	if (typTup->typnotnull == notNull)
2740 	{
2741 		table_close(typrel, RowExclusiveLock);
2742 		return address;
2743 	}
2744 
2745 	/* Adding a NOT NULL constraint requires checking existing columns */
2746 	if (notNull)
2747 	{
2748 		List	   *rels;
2749 		ListCell   *rt;
2750 
2751 		/* Fetch relation list with attributes based on this domain */
2752 		/* ShareLock is sufficient to prevent concurrent data changes */
2753 
2754 		rels = get_rels_with_domain(domainoid, ShareLock);
2755 
2756 		foreach(rt, rels)
2757 		{
2758 			RelToCheck *rtc = (RelToCheck *) lfirst(rt);
2759 			Relation	testrel = rtc->rel;
2760 			TupleDesc	tupdesc = RelationGetDescr(testrel);
2761 			TupleTableSlot *slot;
2762 			TableScanDesc scan;
2763 			Snapshot	snapshot;
2764 
2765 			/* Scan all tuples in this relation */
2766 			snapshot = RegisterSnapshot(GetLatestSnapshot());
2767 			scan = table_beginscan(testrel, snapshot, 0, NULL);
2768 			slot = table_slot_create(testrel, NULL);
2769 			while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
2770 			{
2771 				int			i;
2772 
2773 				/* Test attributes that are of the domain */
2774 				for (i = 0; i < rtc->natts; i++)
2775 				{
2776 					int			attnum = rtc->atts[i];
2777 					Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
2778 
2779 					if (slot_attisnull(slot, attnum))
2780 					{
2781 						/*
2782 						 * In principle the auxiliary information for this
2783 						 * error should be errdatatype(), but errtablecol()
2784 						 * seems considerably more useful in practice.  Since
2785 						 * this code only executes in an ALTER DOMAIN command,
2786 						 * the client should already know which domain is in
2787 						 * question.
2788 						 */
2789 						ereport(ERROR,
2790 								(errcode(ERRCODE_NOT_NULL_VIOLATION),
2791 								 errmsg("column \"%s\" of table \"%s\" contains null values",
2792 										NameStr(attr->attname),
2793 										RelationGetRelationName(testrel)),
2794 								 errtablecol(testrel, attnum)));
2795 					}
2796 				}
2797 			}
2798 			ExecDropSingleTupleTableSlot(slot);
2799 			table_endscan(scan);
2800 			UnregisterSnapshot(snapshot);
2801 
2802 			/* Close each rel after processing, but keep lock */
2803 			table_close(testrel, NoLock);
2804 		}
2805 	}
2806 
2807 	/*
2808 	 * Okay to update pg_type row.  We can scribble on typTup because it's a
2809 	 * copy.
2810 	 */
2811 	typTup->typnotnull = notNull;
2812 
2813 	CatalogTupleUpdate(typrel, &tup->t_self, tup);
2814 
2815 	InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0);
2816 
2817 	ObjectAddressSet(address, TypeRelationId, domainoid);
2818 
2819 	/* Clean up */
2820 	heap_freetuple(tup);
2821 	table_close(typrel, RowExclusiveLock);
2822 
2823 	return address;
2824 }
2825 
2826 /*
2827  * AlterDomainDropConstraint
2828  *
2829  * Implements the ALTER DOMAIN DROP CONSTRAINT statement
2830  *
2831  * Returns ObjectAddress of the modified domain.
2832  */
2833 ObjectAddress
AlterDomainDropConstraint(List * names,const char * constrName,DropBehavior behavior,bool missing_ok)2834 AlterDomainDropConstraint(List *names, const char *constrName,
2835 						  DropBehavior behavior, bool missing_ok)
2836 {
2837 	TypeName   *typename;
2838 	Oid			domainoid;
2839 	HeapTuple	tup;
2840 	Relation	rel;
2841 	Relation	conrel;
2842 	SysScanDesc conscan;
2843 	ScanKeyData skey[3];
2844 	HeapTuple	contup;
2845 	bool		found = false;
2846 	ObjectAddress address;
2847 
2848 	/* Make a TypeName so we can use standard type lookup machinery */
2849 	typename = makeTypeNameFromNameList(names);
2850 	domainoid = typenameTypeId(NULL, typename);
2851 
2852 	/* Look up the domain in the type table */
2853 	rel = table_open(TypeRelationId, RowExclusiveLock);
2854 
2855 	tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2856 	if (!HeapTupleIsValid(tup))
2857 		elog(ERROR, "cache lookup failed for type %u", domainoid);
2858 
2859 	/* Check it's a domain and check user has permission for ALTER DOMAIN */
2860 	checkDomainOwner(tup);
2861 
2862 	/* Grab an appropriate lock on the pg_constraint relation */
2863 	conrel = table_open(ConstraintRelationId, RowExclusiveLock);
2864 
2865 	/* Find and remove the target constraint */
2866 	ScanKeyInit(&skey[0],
2867 				Anum_pg_constraint_conrelid,
2868 				BTEqualStrategyNumber, F_OIDEQ,
2869 				ObjectIdGetDatum(InvalidOid));
2870 	ScanKeyInit(&skey[1],
2871 				Anum_pg_constraint_contypid,
2872 				BTEqualStrategyNumber, F_OIDEQ,
2873 				ObjectIdGetDatum(domainoid));
2874 	ScanKeyInit(&skey[2],
2875 				Anum_pg_constraint_conname,
2876 				BTEqualStrategyNumber, F_NAMEEQ,
2877 				CStringGetDatum(constrName));
2878 
2879 	conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
2880 								 NULL, 3, skey);
2881 
2882 	/* There can be at most one matching row */
2883 	if ((contup = systable_getnext(conscan)) != NULL)
2884 	{
2885 		ObjectAddress conobj;
2886 
2887 		conobj.classId = ConstraintRelationId;
2888 		conobj.objectId = ((Form_pg_constraint) GETSTRUCT(contup))->oid;
2889 		conobj.objectSubId = 0;
2890 
2891 		performDeletion(&conobj, behavior, 0);
2892 		found = true;
2893 	}
2894 
2895 	/* Clean up after the scan */
2896 	systable_endscan(conscan);
2897 	table_close(conrel, RowExclusiveLock);
2898 
2899 	if (!found)
2900 	{
2901 		if (!missing_ok)
2902 			ereport(ERROR,
2903 					(errcode(ERRCODE_UNDEFINED_OBJECT),
2904 					 errmsg("constraint \"%s\" of domain \"%s\" does not exist",
2905 							constrName, TypeNameToString(typename))));
2906 		else
2907 			ereport(NOTICE,
2908 					(errmsg("constraint \"%s\" of domain \"%s\" does not exist, skipping",
2909 							constrName, TypeNameToString(typename))));
2910 	}
2911 
2912 	/*
2913 	 * We must send out an sinval message for the domain, to ensure that any
2914 	 * dependent plans get rebuilt.  Since this command doesn't change the
2915 	 * domain's pg_type row, that won't happen automatically; do it manually.
2916 	 */
2917 	CacheInvalidateHeapTuple(rel, tup, NULL);
2918 
2919 	ObjectAddressSet(address, TypeRelationId, domainoid);
2920 
2921 	/* Clean up */
2922 	table_close(rel, RowExclusiveLock);
2923 
2924 	return address;
2925 }
2926 
2927 /*
2928  * AlterDomainAddConstraint
2929  *
2930  * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
2931  */
2932 ObjectAddress
AlterDomainAddConstraint(List * names,Node * newConstraint,ObjectAddress * constrAddr)2933 AlterDomainAddConstraint(List *names, Node *newConstraint,
2934 						 ObjectAddress *constrAddr)
2935 {
2936 	TypeName   *typename;
2937 	Oid			domainoid;
2938 	Relation	typrel;
2939 	HeapTuple	tup;
2940 	Form_pg_type typTup;
2941 	Constraint *constr;
2942 	char	   *ccbin;
2943 	ObjectAddress address;
2944 
2945 	/* Make a TypeName so we can use standard type lookup machinery */
2946 	typename = makeTypeNameFromNameList(names);
2947 	domainoid = typenameTypeId(NULL, typename);
2948 
2949 	/* Look up the domain in the type table */
2950 	typrel = table_open(TypeRelationId, RowExclusiveLock);
2951 
2952 	tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(domainoid));
2953 	if (!HeapTupleIsValid(tup))
2954 		elog(ERROR, "cache lookup failed for type %u", domainoid);
2955 	typTup = (Form_pg_type) GETSTRUCT(tup);
2956 
2957 	/* Check it's a domain and check user has permission for ALTER DOMAIN */
2958 	checkDomainOwner(tup);
2959 
2960 	if (!IsA(newConstraint, Constraint))
2961 		elog(ERROR, "unrecognized node type: %d",
2962 			 (int) nodeTag(newConstraint));
2963 
2964 	constr = (Constraint *) newConstraint;
2965 
2966 	switch (constr->contype)
2967 	{
2968 		case CONSTR_CHECK:
2969 			/* processed below */
2970 			break;
2971 
2972 		case CONSTR_UNIQUE:
2973 			ereport(ERROR,
2974 					(errcode(ERRCODE_SYNTAX_ERROR),
2975 					 errmsg("unique constraints not possible for domains")));
2976 			break;
2977 
2978 		case CONSTR_PRIMARY:
2979 			ereport(ERROR,
2980 					(errcode(ERRCODE_SYNTAX_ERROR),
2981 					 errmsg("primary key constraints not possible for domains")));
2982 			break;
2983 
2984 		case CONSTR_EXCLUSION:
2985 			ereport(ERROR,
2986 					(errcode(ERRCODE_SYNTAX_ERROR),
2987 					 errmsg("exclusion constraints not possible for domains")));
2988 			break;
2989 
2990 		case CONSTR_FOREIGN:
2991 			ereport(ERROR,
2992 					(errcode(ERRCODE_SYNTAX_ERROR),
2993 					 errmsg("foreign key constraints not possible for domains")));
2994 			break;
2995 
2996 		case CONSTR_ATTR_DEFERRABLE:
2997 		case CONSTR_ATTR_NOT_DEFERRABLE:
2998 		case CONSTR_ATTR_DEFERRED:
2999 		case CONSTR_ATTR_IMMEDIATE:
3000 			ereport(ERROR,
3001 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3002 					 errmsg("specifying constraint deferrability not supported for domains")));
3003 			break;
3004 
3005 		default:
3006 			elog(ERROR, "unrecognized constraint subtype: %d",
3007 				 (int) constr->contype);
3008 			break;
3009 	}
3010 
3011 	/*
3012 	 * Since all other constraint types throw errors, this must be a check
3013 	 * constraint.  First, process the constraint expression and add an entry
3014 	 * to pg_constraint.
3015 	 */
3016 
3017 	ccbin = domainAddConstraint(domainoid, typTup->typnamespace,
3018 								typTup->typbasetype, typTup->typtypmod,
3019 								constr, NameStr(typTup->typname), constrAddr);
3020 
3021 	/*
3022 	 * If requested to validate the constraint, test all values stored in the
3023 	 * attributes based on the domain the constraint is being added to.
3024 	 */
3025 	if (!constr->skip_validation)
3026 		validateDomainConstraint(domainoid, ccbin);
3027 
3028 	/*
3029 	 * We must send out an sinval message for the domain, to ensure that any
3030 	 * dependent plans get rebuilt.  Since this command doesn't change the
3031 	 * domain's pg_type row, that won't happen automatically; do it manually.
3032 	 */
3033 	CacheInvalidateHeapTuple(typrel, tup, NULL);
3034 
3035 	ObjectAddressSet(address, TypeRelationId, domainoid);
3036 
3037 	/* Clean up */
3038 	table_close(typrel, RowExclusiveLock);
3039 
3040 	return address;
3041 }
3042 
3043 /*
3044  * AlterDomainValidateConstraint
3045  *
3046  * Implements the ALTER DOMAIN .. VALIDATE CONSTRAINT statement.
3047  */
3048 ObjectAddress
AlterDomainValidateConstraint(List * names,const char * constrName)3049 AlterDomainValidateConstraint(List *names, const char *constrName)
3050 {
3051 	TypeName   *typename;
3052 	Oid			domainoid;
3053 	Relation	typrel;
3054 	Relation	conrel;
3055 	HeapTuple	tup;
3056 	Form_pg_constraint con;
3057 	Form_pg_constraint copy_con;
3058 	char	   *conbin;
3059 	SysScanDesc scan;
3060 	Datum		val;
3061 	bool		isnull;
3062 	HeapTuple	tuple;
3063 	HeapTuple	copyTuple;
3064 	ScanKeyData skey[3];
3065 	ObjectAddress address;
3066 
3067 	/* Make a TypeName so we can use standard type lookup machinery */
3068 	typename = makeTypeNameFromNameList(names);
3069 	domainoid = typenameTypeId(NULL, typename);
3070 
3071 	/* Look up the domain in the type table */
3072 	typrel = table_open(TypeRelationId, AccessShareLock);
3073 
3074 	tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(domainoid));
3075 	if (!HeapTupleIsValid(tup))
3076 		elog(ERROR, "cache lookup failed for type %u", domainoid);
3077 
3078 	/* Check it's a domain and check user has permission for ALTER DOMAIN */
3079 	checkDomainOwner(tup);
3080 
3081 	/*
3082 	 * Find and check the target constraint
3083 	 */
3084 	conrel = table_open(ConstraintRelationId, RowExclusiveLock);
3085 
3086 	ScanKeyInit(&skey[0],
3087 				Anum_pg_constraint_conrelid,
3088 				BTEqualStrategyNumber, F_OIDEQ,
3089 				ObjectIdGetDatum(InvalidOid));
3090 	ScanKeyInit(&skey[1],
3091 				Anum_pg_constraint_contypid,
3092 				BTEqualStrategyNumber, F_OIDEQ,
3093 				ObjectIdGetDatum(domainoid));
3094 	ScanKeyInit(&skey[2],
3095 				Anum_pg_constraint_conname,
3096 				BTEqualStrategyNumber, F_NAMEEQ,
3097 				CStringGetDatum(constrName));
3098 
3099 	scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
3100 							  NULL, 3, skey);
3101 
3102 	/* There can be at most one matching row */
3103 	if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
3104 		ereport(ERROR,
3105 				(errcode(ERRCODE_UNDEFINED_OBJECT),
3106 				 errmsg("constraint \"%s\" of domain \"%s\" does not exist",
3107 						constrName, TypeNameToString(typename))));
3108 
3109 	con = (Form_pg_constraint) GETSTRUCT(tuple);
3110 	if (con->contype != CONSTRAINT_CHECK)
3111 		ereport(ERROR,
3112 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3113 				 errmsg("constraint \"%s\" of domain \"%s\" is not a check constraint",
3114 						constrName, TypeNameToString(typename))));
3115 
3116 	val = SysCacheGetAttr(CONSTROID, tuple,
3117 						  Anum_pg_constraint_conbin,
3118 						  &isnull);
3119 	if (isnull)
3120 		elog(ERROR, "null conbin for constraint %u",
3121 			 con->oid);
3122 	conbin = TextDatumGetCString(val);
3123 
3124 	validateDomainConstraint(domainoid, conbin);
3125 
3126 	/*
3127 	 * Now update the catalog, while we have the door open.
3128 	 */
3129 	copyTuple = heap_copytuple(tuple);
3130 	copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
3131 	copy_con->convalidated = true;
3132 	CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
3133 
3134 	InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
3135 
3136 	ObjectAddressSet(address, TypeRelationId, domainoid);
3137 
3138 	heap_freetuple(copyTuple);
3139 
3140 	systable_endscan(scan);
3141 
3142 	table_close(typrel, AccessShareLock);
3143 	table_close(conrel, RowExclusiveLock);
3144 
3145 	ReleaseSysCache(tup);
3146 
3147 	return address;
3148 }
3149 
3150 static void
validateDomainConstraint(Oid domainoid,char * ccbin)3151 validateDomainConstraint(Oid domainoid, char *ccbin)
3152 {
3153 	Expr	   *expr = (Expr *) stringToNode(ccbin);
3154 	List	   *rels;
3155 	ListCell   *rt;
3156 	EState	   *estate;
3157 	ExprContext *econtext;
3158 	ExprState  *exprstate;
3159 
3160 	/* Need an EState to run ExecEvalExpr */
3161 	estate = CreateExecutorState();
3162 	econtext = GetPerTupleExprContext(estate);
3163 
3164 	/* build execution state for expr */
3165 	exprstate = ExecPrepareExpr(expr, estate);
3166 
3167 	/* Fetch relation list with attributes based on this domain */
3168 	/* ShareLock is sufficient to prevent concurrent data changes */
3169 
3170 	rels = get_rels_with_domain(domainoid, ShareLock);
3171 
3172 	foreach(rt, rels)
3173 	{
3174 		RelToCheck *rtc = (RelToCheck *) lfirst(rt);
3175 		Relation	testrel = rtc->rel;
3176 		TupleDesc	tupdesc = RelationGetDescr(testrel);
3177 		TupleTableSlot *slot;
3178 		TableScanDesc scan;
3179 		Snapshot	snapshot;
3180 
3181 		/* Scan all tuples in this relation */
3182 		snapshot = RegisterSnapshot(GetLatestSnapshot());
3183 		scan = table_beginscan(testrel, snapshot, 0, NULL);
3184 		slot = table_slot_create(testrel, NULL);
3185 		while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
3186 		{
3187 			int			i;
3188 
3189 			/* Test attributes that are of the domain */
3190 			for (i = 0; i < rtc->natts; i++)
3191 			{
3192 				int			attnum = rtc->atts[i];
3193 				Datum		d;
3194 				bool		isNull;
3195 				Datum		conResult;
3196 				Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
3197 
3198 				d = slot_getattr(slot, attnum, &isNull);
3199 
3200 				econtext->domainValue_datum = d;
3201 				econtext->domainValue_isNull = isNull;
3202 
3203 				conResult = ExecEvalExprSwitchContext(exprstate,
3204 													  econtext,
3205 													  &isNull);
3206 
3207 				if (!isNull && !DatumGetBool(conResult))
3208 				{
3209 					/*
3210 					 * In principle the auxiliary information for this error
3211 					 * should be errdomainconstraint(), but errtablecol()
3212 					 * seems considerably more useful in practice.  Since this
3213 					 * code only executes in an ALTER DOMAIN command, the
3214 					 * client should already know which domain is in question,
3215 					 * and which constraint too.
3216 					 */
3217 					ereport(ERROR,
3218 							(errcode(ERRCODE_CHECK_VIOLATION),
3219 							 errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
3220 									NameStr(attr->attname),
3221 									RelationGetRelationName(testrel)),
3222 							 errtablecol(testrel, attnum)));
3223 				}
3224 			}
3225 
3226 			ResetExprContext(econtext);
3227 		}
3228 		ExecDropSingleTupleTableSlot(slot);
3229 		table_endscan(scan);
3230 		UnregisterSnapshot(snapshot);
3231 
3232 		/* Hold relation lock till commit (XXX bad for concurrency) */
3233 		table_close(testrel, NoLock);
3234 	}
3235 
3236 	FreeExecutorState(estate);
3237 }
3238 
3239 /*
3240  * get_rels_with_domain
3241  *
3242  * Fetch all relations / attributes which are using the domain
3243  *
3244  * The result is a list of RelToCheck structs, one for each distinct
3245  * relation, each containing one or more attribute numbers that are of
3246  * the domain type.  We have opened each rel and acquired the specified lock
3247  * type on it.
3248  *
3249  * We support nested domains by including attributes that are of derived
3250  * domain types.  Current callers do not need to distinguish between attributes
3251  * that are of exactly the given domain and those that are of derived domains.
3252  *
3253  * XXX this is completely broken because there is no way to lock the domain
3254  * to prevent columns from being added or dropped while our command runs.
3255  * We can partially protect against column drops by locking relations as we
3256  * come across them, but there is still a race condition (the window between
3257  * seeing a pg_depend entry and acquiring lock on the relation it references).
3258  * Also, holding locks on all these relations simultaneously creates a non-
3259  * trivial risk of deadlock.  We can minimize but not eliminate the deadlock
3260  * risk by using the weakest suitable lock (ShareLock for most callers).
3261  *
3262  * XXX the API for this is not sufficient to support checking domain values
3263  * that are inside container types, such as composite types, arrays, or
3264  * ranges.  Currently we just error out if a container type containing the
3265  * target domain is stored anywhere.
3266  *
3267  * Generally used for retrieving a list of tests when adding
3268  * new constraints to a domain.
3269  */
3270 static List *
get_rels_with_domain(Oid domainOid,LOCKMODE lockmode)3271 get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
3272 {
3273 	List	   *result = NIL;
3274 	char	   *domainTypeName = format_type_be(domainOid);
3275 	Relation	depRel;
3276 	ScanKeyData key[2];
3277 	SysScanDesc depScan;
3278 	HeapTuple	depTup;
3279 
3280 	Assert(lockmode != NoLock);
3281 
3282 	/* since this function recurses, it could be driven to stack overflow */
3283 	check_stack_depth();
3284 
3285 	/*
3286 	 * We scan pg_depend to find those things that depend on the domain. (We
3287 	 * assume we can ignore refobjsubid for a domain.)
3288 	 */
3289 	depRel = table_open(DependRelationId, AccessShareLock);
3290 
3291 	ScanKeyInit(&key[0],
3292 				Anum_pg_depend_refclassid,
3293 				BTEqualStrategyNumber, F_OIDEQ,
3294 				ObjectIdGetDatum(TypeRelationId));
3295 	ScanKeyInit(&key[1],
3296 				Anum_pg_depend_refobjid,
3297 				BTEqualStrategyNumber, F_OIDEQ,
3298 				ObjectIdGetDatum(domainOid));
3299 
3300 	depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
3301 								 NULL, 2, key);
3302 
3303 	while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
3304 	{
3305 		Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
3306 		RelToCheck *rtc = NULL;
3307 		ListCell   *rellist;
3308 		Form_pg_attribute pg_att;
3309 		int			ptr;
3310 
3311 		/* Check for directly dependent types */
3312 		if (pg_depend->classid == TypeRelationId)
3313 		{
3314 			if (get_typtype(pg_depend->objid) == TYPTYPE_DOMAIN)
3315 			{
3316 				/*
3317 				 * This is a sub-domain, so recursively add dependent columns
3318 				 * to the output list.  This is a bit inefficient since we may
3319 				 * fail to combine RelToCheck entries when attributes of the
3320 				 * same rel have different derived domain types, but it's
3321 				 * probably not worth improving.
3322 				 */
3323 				result = list_concat(result,
3324 									 get_rels_with_domain(pg_depend->objid,
3325 														  lockmode));
3326 			}
3327 			else
3328 			{
3329 				/*
3330 				 * Otherwise, it is some container type using the domain, so
3331 				 * fail if there are any columns of this type.
3332 				 */
3333 				find_composite_type_dependencies(pg_depend->objid,
3334 												 NULL,
3335 												 domainTypeName);
3336 			}
3337 			continue;
3338 		}
3339 
3340 		/* Else, ignore dependees that aren't user columns of relations */
3341 		/* (we assume system columns are never of domain types) */
3342 		if (pg_depend->classid != RelationRelationId ||
3343 			pg_depend->objsubid <= 0)
3344 			continue;
3345 
3346 		/* See if we already have an entry for this relation */
3347 		foreach(rellist, result)
3348 		{
3349 			RelToCheck *rt = (RelToCheck *) lfirst(rellist);
3350 
3351 			if (RelationGetRelid(rt->rel) == pg_depend->objid)
3352 			{
3353 				rtc = rt;
3354 				break;
3355 			}
3356 		}
3357 
3358 		if (rtc == NULL)
3359 		{
3360 			/* First attribute found for this relation */
3361 			Relation	rel;
3362 
3363 			/* Acquire requested lock on relation */
3364 			rel = relation_open(pg_depend->objid, lockmode);
3365 
3366 			/*
3367 			 * Check to see if rowtype is stored anyplace as a composite-type
3368 			 * column; if so we have to fail, for now anyway.
3369 			 */
3370 			if (OidIsValid(rel->rd_rel->reltype))
3371 				find_composite_type_dependencies(rel->rd_rel->reltype,
3372 												 NULL,
3373 												 domainTypeName);
3374 
3375 			/*
3376 			 * Otherwise, we can ignore relations except those with both
3377 			 * storage and user-chosen column types.
3378 			 *
3379 			 * XXX If an index-only scan could satisfy "col::some_domain" from
3380 			 * a suitable expression index, this should also check expression
3381 			 * index columns.
3382 			 */
3383 			if (rel->rd_rel->relkind != RELKIND_RELATION &&
3384 				rel->rd_rel->relkind != RELKIND_MATVIEW)
3385 			{
3386 				relation_close(rel, lockmode);
3387 				continue;
3388 			}
3389 
3390 			/* Build the RelToCheck entry with enough space for all atts */
3391 			rtc = (RelToCheck *) palloc(sizeof(RelToCheck));
3392 			rtc->rel = rel;
3393 			rtc->natts = 0;
3394 			rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));
3395 			result = lappend(result, rtc);
3396 		}
3397 
3398 		/*
3399 		 * Confirm column has not been dropped, and is of the expected type.
3400 		 * This defends against an ALTER DROP COLUMN occurring just before we
3401 		 * acquired lock ... but if the whole table were dropped, we'd still
3402 		 * have a problem.
3403 		 */
3404 		if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
3405 			continue;
3406 		pg_att = TupleDescAttr(rtc->rel->rd_att, pg_depend->objsubid - 1);
3407 		if (pg_att->attisdropped || pg_att->atttypid != domainOid)
3408 			continue;
3409 
3410 		/*
3411 		 * Okay, add column to result.  We store the columns in column-number
3412 		 * order; this is just a hack to improve predictability of regression
3413 		 * test output ...
3414 		 */
3415 		Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
3416 
3417 		ptr = rtc->natts++;
3418 		while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)
3419 		{
3420 			rtc->atts[ptr] = rtc->atts[ptr - 1];
3421 			ptr--;
3422 		}
3423 		rtc->atts[ptr] = pg_depend->objsubid;
3424 	}
3425 
3426 	systable_endscan(depScan);
3427 
3428 	relation_close(depRel, AccessShareLock);
3429 
3430 	return result;
3431 }
3432 
3433 /*
3434  * checkDomainOwner
3435  *
3436  * Check that the type is actually a domain and that the current user
3437  * has permission to do ALTER DOMAIN on it.  Throw an error if not.
3438  */
3439 void
checkDomainOwner(HeapTuple tup)3440 checkDomainOwner(HeapTuple tup)
3441 {
3442 	Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
3443 
3444 	/* Check that this is actually a domain */
3445 	if (typTup->typtype != TYPTYPE_DOMAIN)
3446 		ereport(ERROR,
3447 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3448 				 errmsg("%s is not a domain",
3449 						format_type_be(typTup->oid))));
3450 
3451 	/* Permission check: must own type */
3452 	if (!pg_type_ownercheck(typTup->oid, GetUserId()))
3453 		aclcheck_error_type(ACLCHECK_NOT_OWNER, typTup->oid);
3454 }
3455 
3456 /*
3457  * domainAddConstraint - code shared between CREATE and ALTER DOMAIN
3458  */
3459 static char *
domainAddConstraint(Oid domainOid,Oid domainNamespace,Oid baseTypeOid,int typMod,Constraint * constr,const char * domainName,ObjectAddress * constrAddr)3460 domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
3461 					int typMod, Constraint *constr,
3462 					const char *domainName, ObjectAddress *constrAddr)
3463 {
3464 	Node	   *expr;
3465 	char	   *ccbin;
3466 	ParseState *pstate;
3467 	CoerceToDomainValue *domVal;
3468 	Oid			ccoid;
3469 
3470 	/*
3471 	 * Assign or validate constraint name
3472 	 */
3473 	if (constr->conname)
3474 	{
3475 		if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
3476 								 domainOid,
3477 								 constr->conname))
3478 			ereport(ERROR,
3479 					(errcode(ERRCODE_DUPLICATE_OBJECT),
3480 					 errmsg("constraint \"%s\" for domain \"%s\" already exists",
3481 							constr->conname, domainName)));
3482 	}
3483 	else
3484 		constr->conname = ChooseConstraintName(domainName,
3485 											   NULL,
3486 											   "check",
3487 											   domainNamespace,
3488 											   NIL);
3489 
3490 	/*
3491 	 * Convert the A_EXPR in raw_expr into an EXPR
3492 	 */
3493 	pstate = make_parsestate(NULL);
3494 
3495 	/*
3496 	 * Set up a CoerceToDomainValue to represent the occurrence of VALUE in
3497 	 * the expression.  Note that it will appear to have the type of the base
3498 	 * type, not the domain.  This seems correct since within the check
3499 	 * expression, we should not assume the input value can be considered a
3500 	 * member of the domain.
3501 	 */
3502 	domVal = makeNode(CoerceToDomainValue);
3503 	domVal->typeId = baseTypeOid;
3504 	domVal->typeMod = typMod;
3505 	domVal->collation = get_typcollation(baseTypeOid);
3506 	domVal->location = -1;		/* will be set when/if used */
3507 
3508 	pstate->p_pre_columnref_hook = replace_domain_constraint_value;
3509 	pstate->p_ref_hook_state = (void *) domVal;
3510 
3511 	expr = transformExpr(pstate, constr->raw_expr, EXPR_KIND_DOMAIN_CHECK);
3512 
3513 	/*
3514 	 * Make sure it yields a boolean result.
3515 	 */
3516 	expr = coerce_to_boolean(pstate, expr, "CHECK");
3517 
3518 	/*
3519 	 * Fix up collation information.
3520 	 */
3521 	assign_expr_collations(pstate, expr);
3522 
3523 	/*
3524 	 * Domains don't allow variables (this is probably dead code now that
3525 	 * add_missing_from is history, but let's be sure).
3526 	 */
3527 	if (list_length(pstate->p_rtable) != 0 ||
3528 		contain_var_clause(expr))
3529 		ereport(ERROR,
3530 				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
3531 				 errmsg("cannot use table references in domain check constraint")));
3532 
3533 	/*
3534 	 * Convert to string form for storage.
3535 	 */
3536 	ccbin = nodeToString(expr);
3537 
3538 	/*
3539 	 * Store the constraint in pg_constraint
3540 	 */
3541 	ccoid =
3542 		CreateConstraintEntry(constr->conname,	/* Constraint Name */
3543 							  domainNamespace,	/* namespace */
3544 							  CONSTRAINT_CHECK, /* Constraint Type */
3545 							  false,	/* Is Deferrable */
3546 							  false,	/* Is Deferred */
3547 							  !constr->skip_validation, /* Is Validated */
3548 							  InvalidOid,	/* no parent constraint */
3549 							  InvalidOid,	/* not a relation constraint */
3550 							  NULL,
3551 							  0,
3552 							  0,
3553 							  domainOid,	/* domain constraint */
3554 							  InvalidOid,	/* no associated index */
3555 							  InvalidOid,	/* Foreign key fields */
3556 							  NULL,
3557 							  NULL,
3558 							  NULL,
3559 							  NULL,
3560 							  0,
3561 							  ' ',
3562 							  ' ',
3563 							  ' ',
3564 							  NULL, /* not an exclusion constraint */
3565 							  expr, /* Tree form of check constraint */
3566 							  ccbin,	/* Binary form of check constraint */
3567 							  true, /* is local */
3568 							  0,	/* inhcount */
3569 							  false,	/* connoinherit */
3570 							  false);	/* is_internal */
3571 	if (constrAddr)
3572 		ObjectAddressSet(*constrAddr, ConstraintRelationId, ccoid);
3573 
3574 	/*
3575 	 * Return the compiled constraint expression so the calling routine can
3576 	 * perform any additional required tests.
3577 	 */
3578 	return ccbin;
3579 }
3580 
3581 /* Parser pre_columnref_hook for domain CHECK constraint parsing */
3582 static Node *
replace_domain_constraint_value(ParseState * pstate,ColumnRef * cref)3583 replace_domain_constraint_value(ParseState *pstate, ColumnRef *cref)
3584 {
3585 	/*
3586 	 * Check for a reference to "value", and if that's what it is, replace
3587 	 * with a CoerceToDomainValue as prepared for us by domainAddConstraint.
3588 	 * (We handle VALUE as a name, not a keyword, to avoid breaking a lot of
3589 	 * applications that have used VALUE as a column name in the past.)
3590 	 */
3591 	if (list_length(cref->fields) == 1)
3592 	{
3593 		Node	   *field1 = (Node *) linitial(cref->fields);
3594 		char	   *colname;
3595 
3596 		Assert(IsA(field1, String));
3597 		colname = strVal(field1);
3598 		if (strcmp(colname, "value") == 0)
3599 		{
3600 			CoerceToDomainValue *domVal = copyObject(pstate->p_ref_hook_state);
3601 
3602 			/* Propagate location knowledge, if any */
3603 			domVal->location = cref->location;
3604 			return (Node *) domVal;
3605 		}
3606 	}
3607 	return NULL;
3608 }
3609 
3610 
3611 /*
3612  * Execute ALTER TYPE RENAME
3613  */
3614 ObjectAddress
RenameType(RenameStmt * stmt)3615 RenameType(RenameStmt *stmt)
3616 {
3617 	List	   *names = castNode(List, stmt->object);
3618 	const char *newTypeName = stmt->newname;
3619 	TypeName   *typename;
3620 	Oid			typeOid;
3621 	Relation	rel;
3622 	HeapTuple	tup;
3623 	Form_pg_type typTup;
3624 	ObjectAddress address;
3625 
3626 	/* Make a TypeName so we can use standard type lookup machinery */
3627 	typename = makeTypeNameFromNameList(names);
3628 	typeOid = typenameTypeId(NULL, typename);
3629 
3630 	/* Look up the type in the type table */
3631 	rel = table_open(TypeRelationId, RowExclusiveLock);
3632 
3633 	tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
3634 	if (!HeapTupleIsValid(tup))
3635 		elog(ERROR, "cache lookup failed for type %u", typeOid);
3636 	typTup = (Form_pg_type) GETSTRUCT(tup);
3637 
3638 	/* check permissions on type */
3639 	if (!pg_type_ownercheck(typeOid, GetUserId()))
3640 		aclcheck_error_type(ACLCHECK_NOT_OWNER, typeOid);
3641 
3642 	/* ALTER DOMAIN used on a non-domain? */
3643 	if (stmt->renameType == OBJECT_DOMAIN && typTup->typtype != TYPTYPE_DOMAIN)
3644 		ereport(ERROR,
3645 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3646 				 errmsg("%s is not a domain",
3647 						format_type_be(typeOid))));
3648 
3649 	/*
3650 	 * If it's a composite type, we need to check that it really is a
3651 	 * free-standing composite type, and not a table's rowtype. We want people
3652 	 * to use ALTER TABLE not ALTER TYPE for that case.
3653 	 */
3654 	if (typTup->typtype == TYPTYPE_COMPOSITE &&
3655 		get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
3656 		ereport(ERROR,
3657 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3658 				 errmsg("%s is a table's row type",
3659 						format_type_be(typeOid)),
3660 				 errhint("Use ALTER TABLE instead.")));
3661 
3662 	/* don't allow direct alteration of array types, either */
3663 	if (IsTrueArrayType(typTup))
3664 		ereport(ERROR,
3665 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3666 				 errmsg("cannot alter array type %s",
3667 						format_type_be(typeOid)),
3668 				 errhint("You can alter type %s, which will alter the array type as well.",
3669 						 format_type_be(typTup->typelem))));
3670 
3671 	/*
3672 	 * If type is composite we need to rename associated pg_class entry too.
3673 	 * RenameRelationInternal will call RenameTypeInternal automatically.
3674 	 */
3675 	if (typTup->typtype == TYPTYPE_COMPOSITE)
3676 		RenameRelationInternal(typTup->typrelid, newTypeName, false, false);
3677 	else
3678 		RenameTypeInternal(typeOid, newTypeName,
3679 						   typTup->typnamespace);
3680 
3681 	ObjectAddressSet(address, TypeRelationId, typeOid);
3682 	/* Clean up */
3683 	table_close(rel, RowExclusiveLock);
3684 
3685 	return address;
3686 }
3687 
3688 /*
3689  * Change the owner of a type.
3690  */
3691 ObjectAddress
AlterTypeOwner(List * names,Oid newOwnerId,ObjectType objecttype)3692 AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype)
3693 {
3694 	TypeName   *typename;
3695 	Oid			typeOid;
3696 	Relation	rel;
3697 	HeapTuple	tup;
3698 	HeapTuple	newtup;
3699 	Form_pg_type typTup;
3700 	AclResult	aclresult;
3701 	ObjectAddress address;
3702 
3703 	rel = table_open(TypeRelationId, RowExclusiveLock);
3704 
3705 	/* Make a TypeName so we can use standard type lookup machinery */
3706 	typename = makeTypeNameFromNameList(names);
3707 
3708 	/* Use LookupTypeName here so that shell types can be processed */
3709 	tup = LookupTypeName(NULL, typename, NULL, false);
3710 	if (tup == NULL)
3711 		ereport(ERROR,
3712 				(errcode(ERRCODE_UNDEFINED_OBJECT),
3713 				 errmsg("type \"%s\" does not exist",
3714 						TypeNameToString(typename))));
3715 	typeOid = typeTypeId(tup);
3716 
3717 	/* Copy the syscache entry so we can scribble on it below */
3718 	newtup = heap_copytuple(tup);
3719 	ReleaseSysCache(tup);
3720 	tup = newtup;
3721 	typTup = (Form_pg_type) GETSTRUCT(tup);
3722 
3723 	/* Don't allow ALTER DOMAIN on a type */
3724 	if (objecttype == OBJECT_DOMAIN && typTup->typtype != TYPTYPE_DOMAIN)
3725 		ereport(ERROR,
3726 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3727 				 errmsg("%s is not a domain",
3728 						format_type_be(typeOid))));
3729 
3730 	/*
3731 	 * If it's a composite type, we need to check that it really is a
3732 	 * free-standing composite type, and not a table's rowtype. We want people
3733 	 * to use ALTER TABLE not ALTER TYPE for that case.
3734 	 */
3735 	if (typTup->typtype == TYPTYPE_COMPOSITE &&
3736 		get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
3737 		ereport(ERROR,
3738 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3739 				 errmsg("%s is a table's row type",
3740 						format_type_be(typeOid)),
3741 				 errhint("Use ALTER TABLE instead.")));
3742 
3743 	/* don't allow direct alteration of array types, either */
3744 	if (IsTrueArrayType(typTup))
3745 		ereport(ERROR,
3746 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3747 				 errmsg("cannot alter array type %s",
3748 						format_type_be(typeOid)),
3749 				 errhint("You can alter type %s, which will alter the array type as well.",
3750 						 format_type_be(typTup->typelem))));
3751 
3752 	/*
3753 	 * If the new owner is the same as the existing owner, consider the
3754 	 * command to have succeeded.  This is for dump restoration purposes.
3755 	 */
3756 	if (typTup->typowner != newOwnerId)
3757 	{
3758 		/* Superusers can always do it */
3759 		if (!superuser())
3760 		{
3761 			/* Otherwise, must be owner of the existing object */
3762 			if (!pg_type_ownercheck(typTup->oid, GetUserId()))
3763 				aclcheck_error_type(ACLCHECK_NOT_OWNER, typTup->oid);
3764 
3765 			/* Must be able to become new owner */
3766 			check_is_member_of_role(GetUserId(), newOwnerId);
3767 
3768 			/* New owner must have CREATE privilege on namespace */
3769 			aclresult = pg_namespace_aclcheck(typTup->typnamespace,
3770 											  newOwnerId,
3771 											  ACL_CREATE);
3772 			if (aclresult != ACLCHECK_OK)
3773 				aclcheck_error(aclresult, OBJECT_SCHEMA,
3774 							   get_namespace_name(typTup->typnamespace));
3775 		}
3776 
3777 		AlterTypeOwner_oid(typeOid, newOwnerId, true);
3778 	}
3779 
3780 	ObjectAddressSet(address, TypeRelationId, typeOid);
3781 
3782 	/* Clean up */
3783 	table_close(rel, RowExclusiveLock);
3784 
3785 	return address;
3786 }
3787 
3788 /*
3789  * AlterTypeOwner_oid - change type owner unconditionally
3790  *
3791  * This function recurses to handle a pg_class entry, if necessary.  It
3792  * invokes any necessary access object hooks.  If hasDependEntry is true, this
3793  * function modifies the pg_shdepend entry appropriately (this should be
3794  * passed as false only for table rowtypes and array types).
3795  *
3796  * This is used by ALTER TABLE/TYPE OWNER commands, as well as by REASSIGN
3797  * OWNED BY.  It assumes the caller has done all needed check.
3798  */
3799 void
AlterTypeOwner_oid(Oid typeOid,Oid newOwnerId,bool hasDependEntry)3800 AlterTypeOwner_oid(Oid typeOid, Oid newOwnerId, bool hasDependEntry)
3801 {
3802 	Relation	rel;
3803 	HeapTuple	tup;
3804 	Form_pg_type typTup;
3805 
3806 	rel = table_open(TypeRelationId, RowExclusiveLock);
3807 
3808 	tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
3809 	if (!HeapTupleIsValid(tup))
3810 		elog(ERROR, "cache lookup failed for type %u", typeOid);
3811 	typTup = (Form_pg_type) GETSTRUCT(tup);
3812 
3813 	/*
3814 	 * If it's a composite type, invoke ATExecChangeOwner so that we fix up
3815 	 * the pg_class entry properly.  That will call back to
3816 	 * AlterTypeOwnerInternal to take care of the pg_type entry(s).
3817 	 */
3818 	if (typTup->typtype == TYPTYPE_COMPOSITE)
3819 		ATExecChangeOwner(typTup->typrelid, newOwnerId, true, AccessExclusiveLock);
3820 	else
3821 		AlterTypeOwnerInternal(typeOid, newOwnerId);
3822 
3823 	/* Update owner dependency reference */
3824 	if (hasDependEntry)
3825 		changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
3826 
3827 	InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
3828 
3829 	ReleaseSysCache(tup);
3830 	table_close(rel, RowExclusiveLock);
3831 }
3832 
3833 /*
3834  * AlterTypeOwnerInternal - bare-bones type owner change.
3835  *
3836  * This routine simply modifies the owner of a pg_type entry, and recurses
3837  * to handle a possible array type.
3838  */
3839 void
AlterTypeOwnerInternal(Oid typeOid,Oid newOwnerId)3840 AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
3841 {
3842 	Relation	rel;
3843 	HeapTuple	tup;
3844 	Form_pg_type typTup;
3845 	Datum		repl_val[Natts_pg_type];
3846 	bool		repl_null[Natts_pg_type];
3847 	bool		repl_repl[Natts_pg_type];
3848 	Acl		   *newAcl;
3849 	Datum		aclDatum;
3850 	bool		isNull;
3851 
3852 	rel = table_open(TypeRelationId, RowExclusiveLock);
3853 
3854 	tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
3855 	if (!HeapTupleIsValid(tup))
3856 		elog(ERROR, "cache lookup failed for type %u", typeOid);
3857 	typTup = (Form_pg_type) GETSTRUCT(tup);
3858 
3859 	memset(repl_null, false, sizeof(repl_null));
3860 	memset(repl_repl, false, sizeof(repl_repl));
3861 
3862 	repl_repl[Anum_pg_type_typowner - 1] = true;
3863 	repl_val[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(newOwnerId);
3864 
3865 	aclDatum = heap_getattr(tup,
3866 							Anum_pg_type_typacl,
3867 							RelationGetDescr(rel),
3868 							&isNull);
3869 	/* Null ACLs do not require changes */
3870 	if (!isNull)
3871 	{
3872 		newAcl = aclnewowner(DatumGetAclP(aclDatum),
3873 							 typTup->typowner, newOwnerId);
3874 		repl_repl[Anum_pg_type_typacl - 1] = true;
3875 		repl_val[Anum_pg_type_typacl - 1] = PointerGetDatum(newAcl);
3876 	}
3877 
3878 	tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
3879 							repl_repl);
3880 
3881 	CatalogTupleUpdate(rel, &tup->t_self, tup);
3882 
3883 	/* If it has an array type, update that too */
3884 	if (OidIsValid(typTup->typarray))
3885 		AlterTypeOwnerInternal(typTup->typarray, newOwnerId);
3886 
3887 	/* Clean up */
3888 	table_close(rel, RowExclusiveLock);
3889 }
3890 
3891 /*
3892  * Execute ALTER TYPE SET SCHEMA
3893  */
3894 ObjectAddress
AlterTypeNamespace(List * names,const char * newschema,ObjectType objecttype,Oid * oldschema)3895 AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype,
3896 				   Oid *oldschema)
3897 {
3898 	TypeName   *typename;
3899 	Oid			typeOid;
3900 	Oid			nspOid;
3901 	Oid			oldNspOid;
3902 	ObjectAddresses *objsMoved;
3903 	ObjectAddress myself;
3904 
3905 	/* Make a TypeName so we can use standard type lookup machinery */
3906 	typename = makeTypeNameFromNameList(names);
3907 	typeOid = typenameTypeId(NULL, typename);
3908 
3909 	/* Don't allow ALTER DOMAIN on a type */
3910 	if (objecttype == OBJECT_DOMAIN && get_typtype(typeOid) != TYPTYPE_DOMAIN)
3911 		ereport(ERROR,
3912 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3913 				 errmsg("%s is not a domain",
3914 						format_type_be(typeOid))));
3915 
3916 	/* get schema OID and check its permissions */
3917 	nspOid = LookupCreationNamespace(newschema);
3918 
3919 	objsMoved = new_object_addresses();
3920 	oldNspOid = AlterTypeNamespace_oid(typeOid, nspOid, objsMoved);
3921 	free_object_addresses(objsMoved);
3922 
3923 	if (oldschema)
3924 		*oldschema = oldNspOid;
3925 
3926 	ObjectAddressSet(myself, TypeRelationId, typeOid);
3927 
3928 	return myself;
3929 }
3930 
3931 Oid
AlterTypeNamespace_oid(Oid typeOid,Oid nspOid,ObjectAddresses * objsMoved)3932 AlterTypeNamespace_oid(Oid typeOid, Oid nspOid, ObjectAddresses *objsMoved)
3933 {
3934 	Oid			elemOid;
3935 
3936 	/* check permissions on type */
3937 	if (!pg_type_ownercheck(typeOid, GetUserId()))
3938 		aclcheck_error_type(ACLCHECK_NOT_OWNER, typeOid);
3939 
3940 	/* don't allow direct alteration of array types */
3941 	elemOid = get_element_type(typeOid);
3942 	if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
3943 		ereport(ERROR,
3944 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3945 				 errmsg("cannot alter array type %s",
3946 						format_type_be(typeOid)),
3947 				 errhint("You can alter type %s, which will alter the array type as well.",
3948 						 format_type_be(elemOid))));
3949 
3950 	/* and do the work */
3951 	return AlterTypeNamespaceInternal(typeOid, nspOid, false, true, objsMoved);
3952 }
3953 
3954 /*
3955  * Move specified type to new namespace.
3956  *
3957  * Caller must have already checked privileges.
3958  *
3959  * The function automatically recurses to process the type's array type,
3960  * if any.  isImplicitArray should be true only when doing this internal
3961  * recursion (outside callers must never try to move an array type directly).
3962  *
3963  * If errorOnTableType is true, the function errors out if the type is
3964  * a table type.  ALTER TABLE has to be used to move a table to a new
3965  * namespace.
3966  *
3967  * Returns the type's old namespace OID.
3968  */
3969 Oid
AlterTypeNamespaceInternal(Oid typeOid,Oid nspOid,bool isImplicitArray,bool errorOnTableType,ObjectAddresses * objsMoved)3970 AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
3971 						   bool isImplicitArray,
3972 						   bool errorOnTableType,
3973 						   ObjectAddresses *objsMoved)
3974 {
3975 	Relation	rel;
3976 	HeapTuple	tup;
3977 	Form_pg_type typform;
3978 	Oid			oldNspOid;
3979 	Oid			arrayOid;
3980 	bool		isCompositeType;
3981 	ObjectAddress thisobj;
3982 
3983 	/*
3984 	 * Make sure we haven't moved this object previously.
3985 	 */
3986 	thisobj.classId = TypeRelationId;
3987 	thisobj.objectId = typeOid;
3988 	thisobj.objectSubId = 0;
3989 
3990 	if (object_address_present(&thisobj, objsMoved))
3991 		return InvalidOid;
3992 
3993 	rel = table_open(TypeRelationId, RowExclusiveLock);
3994 
3995 	tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
3996 	if (!HeapTupleIsValid(tup))
3997 		elog(ERROR, "cache lookup failed for type %u", typeOid);
3998 	typform = (Form_pg_type) GETSTRUCT(tup);
3999 
4000 	oldNspOid = typform->typnamespace;
4001 	arrayOid = typform->typarray;
4002 
4003 	/* If the type is already there, we scan skip these next few checks. */
4004 	if (oldNspOid != nspOid)
4005 	{
4006 		/* common checks on switching namespaces */
4007 		CheckSetNamespace(oldNspOid, nspOid);
4008 
4009 		/* check for duplicate name (more friendly than unique-index failure) */
4010 		if (SearchSysCacheExists2(TYPENAMENSP,
4011 								  NameGetDatum(&typform->typname),
4012 								  ObjectIdGetDatum(nspOid)))
4013 			ereport(ERROR,
4014 					(errcode(ERRCODE_DUPLICATE_OBJECT),
4015 					 errmsg("type \"%s\" already exists in schema \"%s\"",
4016 							NameStr(typform->typname),
4017 							get_namespace_name(nspOid))));
4018 	}
4019 
4020 	/* Detect whether type is a composite type (but not a table rowtype) */
4021 	isCompositeType =
4022 		(typform->typtype == TYPTYPE_COMPOSITE &&
4023 		 get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE);
4024 
4025 	/* Enforce not-table-type if requested */
4026 	if (typform->typtype == TYPTYPE_COMPOSITE && !isCompositeType &&
4027 		errorOnTableType)
4028 		ereport(ERROR,
4029 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
4030 				 errmsg("%s is a table's row type",
4031 						format_type_be(typeOid)),
4032 				 errhint("Use ALTER TABLE instead.")));
4033 
4034 	if (oldNspOid != nspOid)
4035 	{
4036 		/* OK, modify the pg_type row */
4037 
4038 		/* tup is a copy, so we can scribble directly on it */
4039 		typform->typnamespace = nspOid;
4040 
4041 		CatalogTupleUpdate(rel, &tup->t_self, tup);
4042 	}
4043 
4044 	/*
4045 	 * Composite types have pg_class entries.
4046 	 *
4047 	 * We need to modify the pg_class tuple as well to reflect the change of
4048 	 * schema.
4049 	 */
4050 	if (isCompositeType)
4051 	{
4052 		Relation	classRel;
4053 
4054 		classRel = table_open(RelationRelationId, RowExclusiveLock);
4055 
4056 		AlterRelationNamespaceInternal(classRel, typform->typrelid,
4057 									   oldNspOid, nspOid,
4058 									   false, objsMoved);
4059 
4060 		table_close(classRel, RowExclusiveLock);
4061 
4062 		/*
4063 		 * Check for constraints associated with the composite type (we don't
4064 		 * currently support this, but probably will someday).
4065 		 */
4066 		AlterConstraintNamespaces(typform->typrelid, oldNspOid,
4067 								  nspOid, false, objsMoved);
4068 	}
4069 	else
4070 	{
4071 		/* If it's a domain, it might have constraints */
4072 		if (typform->typtype == TYPTYPE_DOMAIN)
4073 			AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true,
4074 									  objsMoved);
4075 	}
4076 
4077 	/*
4078 	 * Update dependency on schema, if any --- a table rowtype has not got
4079 	 * one, and neither does an implicit array.
4080 	 */
4081 	if (oldNspOid != nspOid &&
4082 		(isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
4083 		!isImplicitArray)
4084 		if (changeDependencyFor(TypeRelationId, typeOid,
4085 								NamespaceRelationId, oldNspOid, nspOid) != 1)
4086 			elog(ERROR, "failed to change schema dependency for type %s",
4087 				 format_type_be(typeOid));
4088 
4089 	InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
4090 
4091 	heap_freetuple(tup);
4092 
4093 	table_close(rel, RowExclusiveLock);
4094 
4095 	add_exact_object_address(&thisobj, objsMoved);
4096 
4097 	/* Recursively alter the associated array type, if any */
4098 	if (OidIsValid(arrayOid))
4099 		AlterTypeNamespaceInternal(arrayOid, nspOid, true, true, objsMoved);
4100 
4101 	return oldNspOid;
4102 }
4103 
4104 /*
4105  * AlterType
4106  *		ALTER TYPE <type> SET (option = ...)
4107  *
4108  * NOTE: the set of changes that can be allowed here is constrained by many
4109  * non-obvious implementation restrictions.  Tread carefully when considering
4110  * adding new flexibility.
4111  */
4112 ObjectAddress
AlterType(AlterTypeStmt * stmt)4113 AlterType(AlterTypeStmt *stmt)
4114 {
4115 	ObjectAddress address;
4116 	Relation	catalog;
4117 	TypeName   *typename;
4118 	HeapTuple	tup;
4119 	Oid			typeOid;
4120 	Form_pg_type typForm;
4121 	bool		requireSuper = false;
4122 	AlterTypeRecurseParams atparams;
4123 	ListCell   *pl;
4124 
4125 	catalog = table_open(TypeRelationId, RowExclusiveLock);
4126 
4127 	/* Make a TypeName so we can use standard type lookup machinery */
4128 	typename = makeTypeNameFromNameList(stmt->typeName);
4129 	tup = typenameType(NULL, typename, NULL);
4130 
4131 	typeOid = typeTypeId(tup);
4132 	typForm = (Form_pg_type) GETSTRUCT(tup);
4133 
4134 	/* Process options */
4135 	memset(&atparams, 0, sizeof(atparams));
4136 	foreach(pl, stmt->options)
4137 	{
4138 		DefElem    *defel = (DefElem *) lfirst(pl);
4139 
4140 		if (strcmp(defel->defname, "storage") == 0)
4141 		{
4142 			char	   *a = defGetString(defel);
4143 
4144 			if (pg_strcasecmp(a, "plain") == 0)
4145 				atparams.storage = TYPSTORAGE_PLAIN;
4146 			else if (pg_strcasecmp(a, "external") == 0)
4147 				atparams.storage = TYPSTORAGE_EXTERNAL;
4148 			else if (pg_strcasecmp(a, "extended") == 0)
4149 				atparams.storage = TYPSTORAGE_EXTENDED;
4150 			else if (pg_strcasecmp(a, "main") == 0)
4151 				atparams.storage = TYPSTORAGE_MAIN;
4152 			else
4153 				ereport(ERROR,
4154 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4155 						 errmsg("storage \"%s\" not recognized", a)));
4156 
4157 			/*
4158 			 * Validate the storage request.  If the type isn't varlena, it
4159 			 * certainly doesn't support non-PLAIN storage.
4160 			 */
4161 			if (atparams.storage != TYPSTORAGE_PLAIN && typForm->typlen != -1)
4162 				ereport(ERROR,
4163 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
4164 						 errmsg("fixed-size types must have storage PLAIN")));
4165 
4166 			/*
4167 			 * Switching from PLAIN to non-PLAIN is allowed, but it requires
4168 			 * superuser, since we can't validate that the type's C functions
4169 			 * will support it.  Switching from non-PLAIN to PLAIN is
4170 			 * disallowed outright, because it's not practical to ensure that
4171 			 * no tables have toasted values of the type.  Switching among
4172 			 * different non-PLAIN settings is OK, since it just constitutes a
4173 			 * change in the strategy requested for columns created in the
4174 			 * future.
4175 			 */
4176 			if (atparams.storage != TYPSTORAGE_PLAIN &&
4177 				typForm->typstorage == TYPSTORAGE_PLAIN)
4178 				requireSuper = true;
4179 			else if (atparams.storage == TYPSTORAGE_PLAIN &&
4180 					 typForm->typstorage != TYPSTORAGE_PLAIN)
4181 				ereport(ERROR,
4182 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
4183 						 errmsg("cannot change type's storage to PLAIN")));
4184 
4185 			atparams.updateStorage = true;
4186 		}
4187 		else if (strcmp(defel->defname, "receive") == 0)
4188 		{
4189 			if (defel->arg != NULL)
4190 				atparams.receiveOid =
4191 					findTypeReceiveFunction(defGetQualifiedName(defel),
4192 											typeOid);
4193 			else
4194 				atparams.receiveOid = InvalidOid;	/* NONE, remove function */
4195 			atparams.updateReceive = true;
4196 			/* Replacing an I/O function requires superuser. */
4197 			requireSuper = true;
4198 		}
4199 		else if (strcmp(defel->defname, "send") == 0)
4200 		{
4201 			if (defel->arg != NULL)
4202 				atparams.sendOid =
4203 					findTypeSendFunction(defGetQualifiedName(defel),
4204 										 typeOid);
4205 			else
4206 				atparams.sendOid = InvalidOid;	/* NONE, remove function */
4207 			atparams.updateSend = true;
4208 			/* Replacing an I/O function requires superuser. */
4209 			requireSuper = true;
4210 		}
4211 		else if (strcmp(defel->defname, "typmod_in") == 0)
4212 		{
4213 			if (defel->arg != NULL)
4214 				atparams.typmodinOid =
4215 					findTypeTypmodinFunction(defGetQualifiedName(defel));
4216 			else
4217 				atparams.typmodinOid = InvalidOid;	/* NONE, remove function */
4218 			atparams.updateTypmodin = true;
4219 			/* Replacing an I/O function requires superuser. */
4220 			requireSuper = true;
4221 		}
4222 		else if (strcmp(defel->defname, "typmod_out") == 0)
4223 		{
4224 			if (defel->arg != NULL)
4225 				atparams.typmodoutOid =
4226 					findTypeTypmodoutFunction(defGetQualifiedName(defel));
4227 			else
4228 				atparams.typmodoutOid = InvalidOid; /* NONE, remove function */
4229 			atparams.updateTypmodout = true;
4230 			/* Replacing an I/O function requires superuser. */
4231 			requireSuper = true;
4232 		}
4233 		else if (strcmp(defel->defname, "analyze") == 0)
4234 		{
4235 			if (defel->arg != NULL)
4236 				atparams.analyzeOid =
4237 					findTypeAnalyzeFunction(defGetQualifiedName(defel),
4238 											typeOid);
4239 			else
4240 				atparams.analyzeOid = InvalidOid;	/* NONE, remove function */
4241 			atparams.updateAnalyze = true;
4242 			/* Replacing an analyze function requires superuser. */
4243 			requireSuper = true;
4244 		}
4245 		else if (strcmp(defel->defname, "subscript") == 0)
4246 		{
4247 			if (defel->arg != NULL)
4248 				atparams.subscriptOid =
4249 					findTypeSubscriptingFunction(defGetQualifiedName(defel),
4250 												 typeOid);
4251 			else
4252 				atparams.subscriptOid = InvalidOid; /* NONE, remove function */
4253 			atparams.updateSubscript = true;
4254 			/* Replacing a subscript function requires superuser. */
4255 			requireSuper = true;
4256 		}
4257 
4258 		/*
4259 		 * The rest of the options that CREATE accepts cannot be changed.
4260 		 * Check for them so that we can give a meaningful error message.
4261 		 */
4262 		else if (strcmp(defel->defname, "input") == 0 ||
4263 				 strcmp(defel->defname, "output") == 0 ||
4264 				 strcmp(defel->defname, "internallength") == 0 ||
4265 				 strcmp(defel->defname, "passedbyvalue") == 0 ||
4266 				 strcmp(defel->defname, "alignment") == 0 ||
4267 				 strcmp(defel->defname, "like") == 0 ||
4268 				 strcmp(defel->defname, "category") == 0 ||
4269 				 strcmp(defel->defname, "preferred") == 0 ||
4270 				 strcmp(defel->defname, "default") == 0 ||
4271 				 strcmp(defel->defname, "element") == 0 ||
4272 				 strcmp(defel->defname, "delimiter") == 0 ||
4273 				 strcmp(defel->defname, "collatable") == 0)
4274 			ereport(ERROR,
4275 					(errcode(ERRCODE_SYNTAX_ERROR),
4276 					 errmsg("type attribute \"%s\" cannot be changed",
4277 							defel->defname)));
4278 		else
4279 			ereport(ERROR,
4280 					(errcode(ERRCODE_SYNTAX_ERROR),
4281 					 errmsg("type attribute \"%s\" not recognized",
4282 							defel->defname)));
4283 	}
4284 
4285 	/*
4286 	 * Permissions check.  Require superuser if we decided the command
4287 	 * requires that, else must own the type.
4288 	 */
4289 	if (requireSuper)
4290 	{
4291 		if (!superuser())
4292 			ereport(ERROR,
4293 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4294 					 errmsg("must be superuser to alter a type")));
4295 	}
4296 	else
4297 	{
4298 		if (!pg_type_ownercheck(typeOid, GetUserId()))
4299 			aclcheck_error_type(ACLCHECK_NOT_OWNER, typeOid);
4300 	}
4301 
4302 	/*
4303 	 * We disallow all forms of ALTER TYPE SET on types that aren't plain base
4304 	 * types.  It would for example be highly unsafe, not to mention
4305 	 * pointless, to change the send/receive functions for a composite type.
4306 	 * Moreover, pg_dump has no support for changing these properties on
4307 	 * non-base types.  We might weaken this someday, but not now.
4308 	 *
4309 	 * Note: if you weaken this enough to allow composite types, be sure to
4310 	 * adjust the GenerateTypeDependencies call in AlterTypeRecurse.
4311 	 */
4312 	if (typForm->typtype != TYPTYPE_BASE)
4313 		ereport(ERROR,
4314 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
4315 				 errmsg("%s is not a base type",
4316 						format_type_be(typeOid))));
4317 
4318 	/*
4319 	 * For the same reasons, don't allow direct alteration of array types.
4320 	 */
4321 	if (IsTrueArrayType(typForm))
4322 		ereport(ERROR,
4323 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
4324 				 errmsg("%s is not a base type",
4325 						format_type_be(typeOid))));
4326 
4327 	/* OK, recursively update this type and any arrays/domains over it */
4328 	AlterTypeRecurse(typeOid, false, tup, catalog, &atparams);
4329 
4330 	/* Clean up */
4331 	ReleaseSysCache(tup);
4332 
4333 	table_close(catalog, RowExclusiveLock);
4334 
4335 	ObjectAddressSet(address, TypeRelationId, typeOid);
4336 
4337 	return address;
4338 }
4339 
4340 /*
4341  * AlterTypeRecurse: one recursion step for AlterType()
4342  *
4343  * Apply the changes specified by "atparams" to the type identified by
4344  * "typeOid", whose existing pg_type tuple is "tup".  If necessary,
4345  * recursively update its array type as well.  Then search for any domains
4346  * over this type, and recursively apply (most of) the same changes to those
4347  * domains.
4348  *
4349  * We need this because the system generally assumes that a domain inherits
4350  * many properties from its base type.  See DefineDomain() above for details
4351  * of what is inherited.  Arrays inherit a smaller number of properties,
4352  * but not none.
4353  *
4354  * There's a race condition here, in that some other transaction could
4355  * concurrently add another domain atop this base type; we'd miss updating
4356  * that one.  Hence, be wary of allowing ALTER TYPE to change properties for
4357  * which it'd be really fatal for a domain to be out of sync with its base
4358  * type (typlen, for example).  In practice, races seem unlikely to be an
4359  * issue for plausible use-cases for ALTER TYPE.  If one does happen, it could
4360  * be fixed by re-doing the same ALTER TYPE once all prior transactions have
4361  * committed.
4362  */
4363 static void
AlterTypeRecurse(Oid typeOid,bool isImplicitArray,HeapTuple tup,Relation catalog,AlterTypeRecurseParams * atparams)4364 AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
4365 				 HeapTuple tup, Relation catalog,
4366 				 AlterTypeRecurseParams *atparams)
4367 {
4368 	Datum		values[Natts_pg_type];
4369 	bool		nulls[Natts_pg_type];
4370 	bool		replaces[Natts_pg_type];
4371 	HeapTuple	newtup;
4372 	SysScanDesc scan;
4373 	ScanKeyData key[1];
4374 	HeapTuple	domainTup;
4375 
4376 	/* Since this function recurses, it could be driven to stack overflow */
4377 	check_stack_depth();
4378 
4379 	/* Update the current type's tuple */
4380 	memset(values, 0, sizeof(values));
4381 	memset(nulls, 0, sizeof(nulls));
4382 	memset(replaces, 0, sizeof(replaces));
4383 
4384 	if (atparams->updateStorage)
4385 	{
4386 		replaces[Anum_pg_type_typstorage - 1] = true;
4387 		values[Anum_pg_type_typstorage - 1] = CharGetDatum(atparams->storage);
4388 	}
4389 	if (atparams->updateReceive)
4390 	{
4391 		replaces[Anum_pg_type_typreceive - 1] = true;
4392 		values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(atparams->receiveOid);
4393 	}
4394 	if (atparams->updateSend)
4395 	{
4396 		replaces[Anum_pg_type_typsend - 1] = true;
4397 		values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(atparams->sendOid);
4398 	}
4399 	if (atparams->updateTypmodin)
4400 	{
4401 		replaces[Anum_pg_type_typmodin - 1] = true;
4402 		values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(atparams->typmodinOid);
4403 	}
4404 	if (atparams->updateTypmodout)
4405 	{
4406 		replaces[Anum_pg_type_typmodout - 1] = true;
4407 		values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(atparams->typmodoutOid);
4408 	}
4409 	if (atparams->updateAnalyze)
4410 	{
4411 		replaces[Anum_pg_type_typanalyze - 1] = true;
4412 		values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(atparams->analyzeOid);
4413 	}
4414 	if (atparams->updateSubscript)
4415 	{
4416 		replaces[Anum_pg_type_typsubscript - 1] = true;
4417 		values[Anum_pg_type_typsubscript - 1] = ObjectIdGetDatum(atparams->subscriptOid);
4418 	}
4419 
4420 	newtup = heap_modify_tuple(tup, RelationGetDescr(catalog),
4421 							   values, nulls, replaces);
4422 
4423 	CatalogTupleUpdate(catalog, &newtup->t_self, newtup);
4424 
4425 	/* Rebuild dependencies for this type */
4426 	GenerateTypeDependencies(newtup,
4427 							 catalog,
4428 							 NULL,	/* don't have defaultExpr handy */
4429 							 NULL,	/* don't have typacl handy */
4430 							 0, /* we rejected composite types above */
4431 							 isImplicitArray,	/* it might be an array */
4432 							 isImplicitArray,	/* dependent iff it's array */
4433 							 false, /* don't touch extension membership */
4434 							 true);
4435 
4436 	InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
4437 
4438 	/*
4439 	 * Arrays inherit their base type's typmodin and typmodout, but none of
4440 	 * the other properties we're concerned with here.  Recurse to the array
4441 	 * type if needed.
4442 	 */
4443 	if (!isImplicitArray &&
4444 		(atparams->updateTypmodin || atparams->updateTypmodout))
4445 	{
4446 		Oid			arrtypoid = ((Form_pg_type) GETSTRUCT(newtup))->typarray;
4447 
4448 		if (OidIsValid(arrtypoid))
4449 		{
4450 			HeapTuple	arrtup;
4451 			AlterTypeRecurseParams arrparams;
4452 
4453 			arrtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrtypoid));
4454 			if (!HeapTupleIsValid(arrtup))
4455 				elog(ERROR, "cache lookup failed for type %u", arrtypoid);
4456 
4457 			memset(&arrparams, 0, sizeof(arrparams));
4458 			arrparams.updateTypmodin = atparams->updateTypmodin;
4459 			arrparams.updateTypmodout = atparams->updateTypmodout;
4460 			arrparams.typmodinOid = atparams->typmodinOid;
4461 			arrparams.typmodoutOid = atparams->typmodoutOid;
4462 
4463 			AlterTypeRecurse(arrtypoid, true, arrtup, catalog, &arrparams);
4464 
4465 			ReleaseSysCache(arrtup);
4466 		}
4467 	}
4468 
4469 	/*
4470 	 * Now we need to recurse to domains.  However, some properties are not
4471 	 * inherited by domains, so clear the update flags for those.
4472 	 */
4473 	atparams->updateReceive = false;	/* domains use F_DOMAIN_RECV */
4474 	atparams->updateTypmodin = false;	/* domains don't have typmods */
4475 	atparams->updateTypmodout = false;
4476 	atparams->updateSubscript = false;	/* domains don't have subscriptors */
4477 
4478 	/* Skip the scan if nothing remains to be done */
4479 	if (!(atparams->updateStorage ||
4480 		  atparams->updateSend ||
4481 		  atparams->updateAnalyze))
4482 		return;
4483 
4484 	/* Search pg_type for possible domains over this type */
4485 	ScanKeyInit(&key[0],
4486 				Anum_pg_type_typbasetype,
4487 				BTEqualStrategyNumber, F_OIDEQ,
4488 				ObjectIdGetDatum(typeOid));
4489 
4490 	scan = systable_beginscan(catalog, InvalidOid, false,
4491 							  NULL, 1, key);
4492 
4493 	while ((domainTup = systable_getnext(scan)) != NULL)
4494 	{
4495 		Form_pg_type domainForm = (Form_pg_type) GETSTRUCT(domainTup);
4496 
4497 		/*
4498 		 * Shouldn't have a nonzero typbasetype in a non-domain, but let's
4499 		 * check
4500 		 */
4501 		if (domainForm->typtype != TYPTYPE_DOMAIN)
4502 			continue;
4503 
4504 		AlterTypeRecurse(domainForm->oid, false, domainTup, catalog, atparams);
4505 	}
4506 
4507 	systable_endscan(scan);
4508 }
4509