1 /*-------------------------------------------------------------------------
2  *
3  * pg_constraint.c
4  *	  routines to support manipulation of the pg_constraint relation
5  *
6  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *	  src/backend/catalog/pg_constraint.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "access/genam.h"
18 #include "access/heapam.h"
19 #include "access/htup_details.h"
20 #include "access/sysattr.h"
21 #include "catalog/dependency.h"
22 #include "catalog/indexing.h"
23 #include "catalog/objectaccess.h"
24 #include "catalog/pg_constraint.h"
25 #include "catalog/pg_constraint_fn.h"
26 #include "catalog/pg_operator.h"
27 #include "catalog/pg_type.h"
28 #include "commands/defrem.h"
29 #include "utils/array.h"
30 #include "utils/builtins.h"
31 #include "utils/fmgroids.h"
32 #include "utils/lsyscache.h"
33 #include "utils/rel.h"
34 #include "utils/syscache.h"
35 #include "utils/tqual.h"
36 
37 
38 /*
39  * CreateConstraintEntry
40  *	Create a constraint table entry.
41  *
42  * Subsidiary records (such as triggers or indexes to implement the
43  * constraint) are *not* created here.  But we do make dependency links
44  * from the constraint to the things it depends on.
45  *
46  * The new constraint's OID is returned.
47  */
48 Oid
CreateConstraintEntry(const char * constraintName,Oid constraintNamespace,char constraintType,bool isDeferrable,bool isDeferred,bool isValidated,Oid relId,const int16 * constraintKey,int constraintNKeys,Oid domainId,Oid indexRelId,Oid foreignRelId,const int16 * foreignKey,const Oid * pfEqOp,const Oid * ppEqOp,const Oid * ffEqOp,int foreignNKeys,char foreignUpdateType,char foreignDeleteType,char foreignMatchType,const Oid * exclOp,Node * conExpr,const char * conBin,const char * conSrc,bool conIsLocal,int conInhCount,bool conNoInherit,bool is_internal)49 CreateConstraintEntry(const char *constraintName,
50 					  Oid constraintNamespace,
51 					  char constraintType,
52 					  bool isDeferrable,
53 					  bool isDeferred,
54 					  bool isValidated,
55 					  Oid relId,
56 					  const int16 *constraintKey,
57 					  int constraintNKeys,
58 					  Oid domainId,
59 					  Oid indexRelId,
60 					  Oid foreignRelId,
61 					  const int16 *foreignKey,
62 					  const Oid *pfEqOp,
63 					  const Oid *ppEqOp,
64 					  const Oid *ffEqOp,
65 					  int foreignNKeys,
66 					  char foreignUpdateType,
67 					  char foreignDeleteType,
68 					  char foreignMatchType,
69 					  const Oid *exclOp,
70 					  Node *conExpr,
71 					  const char *conBin,
72 					  const char *conSrc,
73 					  bool conIsLocal,
74 					  int conInhCount,
75 					  bool conNoInherit,
76 					  bool is_internal)
77 {
78 	Relation	conDesc;
79 	Oid			conOid;
80 	HeapTuple	tup;
81 	bool		nulls[Natts_pg_constraint];
82 	Datum		values[Natts_pg_constraint];
83 	ArrayType  *conkeyArray;
84 	ArrayType  *confkeyArray;
85 	ArrayType  *conpfeqopArray;
86 	ArrayType  *conppeqopArray;
87 	ArrayType  *conffeqopArray;
88 	ArrayType  *conexclopArray;
89 	NameData	cname;
90 	int			i;
91 	ObjectAddress conobject;
92 
93 	conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
94 
95 	Assert(constraintName);
96 	namestrcpy(&cname, constraintName);
97 
98 	/*
99 	 * Convert C arrays into Postgres arrays.
100 	 */
101 	if (constraintNKeys > 0)
102 	{
103 		Datum	   *conkey;
104 
105 		conkey = (Datum *) palloc(constraintNKeys * sizeof(Datum));
106 		for (i = 0; i < constraintNKeys; i++)
107 			conkey[i] = Int16GetDatum(constraintKey[i]);
108 		conkeyArray = construct_array(conkey, constraintNKeys,
109 									  INT2OID, 2, true, 's');
110 	}
111 	else
112 		conkeyArray = NULL;
113 
114 	if (foreignNKeys > 0)
115 	{
116 		Datum	   *fkdatums;
117 
118 		fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
119 		for (i = 0; i < foreignNKeys; i++)
120 			fkdatums[i] = Int16GetDatum(foreignKey[i]);
121 		confkeyArray = construct_array(fkdatums, foreignNKeys,
122 									   INT2OID, 2, true, 's');
123 		for (i = 0; i < foreignNKeys; i++)
124 			fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
125 		conpfeqopArray = construct_array(fkdatums, foreignNKeys,
126 										 OIDOID, sizeof(Oid), true, 'i');
127 		for (i = 0; i < foreignNKeys; i++)
128 			fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
129 		conppeqopArray = construct_array(fkdatums, foreignNKeys,
130 										 OIDOID, sizeof(Oid), true, 'i');
131 		for (i = 0; i < foreignNKeys; i++)
132 			fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
133 		conffeqopArray = construct_array(fkdatums, foreignNKeys,
134 										 OIDOID, sizeof(Oid), true, 'i');
135 	}
136 	else
137 	{
138 		confkeyArray = NULL;
139 		conpfeqopArray = NULL;
140 		conppeqopArray = NULL;
141 		conffeqopArray = NULL;
142 	}
143 
144 	if (exclOp != NULL)
145 	{
146 		Datum	   *opdatums;
147 
148 		opdatums = (Datum *) palloc(constraintNKeys * sizeof(Datum));
149 		for (i = 0; i < constraintNKeys; i++)
150 			opdatums[i] = ObjectIdGetDatum(exclOp[i]);
151 		conexclopArray = construct_array(opdatums, constraintNKeys,
152 										 OIDOID, sizeof(Oid), true, 'i');
153 	}
154 	else
155 		conexclopArray = NULL;
156 
157 	/* initialize nulls and values */
158 	for (i = 0; i < Natts_pg_constraint; i++)
159 	{
160 		nulls[i] = false;
161 		values[i] = (Datum) NULL;
162 	}
163 
164 	values[Anum_pg_constraint_conname - 1] = NameGetDatum(&cname);
165 	values[Anum_pg_constraint_connamespace - 1] = ObjectIdGetDatum(constraintNamespace);
166 	values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType);
167 	values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable);
168 	values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred);
169 	values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated);
170 	values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId);
171 	values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId);
172 	values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(indexRelId);
173 	values[Anum_pg_constraint_confrelid - 1] = ObjectIdGetDatum(foreignRelId);
174 	values[Anum_pg_constraint_confupdtype - 1] = CharGetDatum(foreignUpdateType);
175 	values[Anum_pg_constraint_confdeltype - 1] = CharGetDatum(foreignDeleteType);
176 	values[Anum_pg_constraint_confmatchtype - 1] = CharGetDatum(foreignMatchType);
177 	values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
178 	values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
179 	values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
180 
181 	if (conkeyArray)
182 		values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
183 	else
184 		nulls[Anum_pg_constraint_conkey - 1] = true;
185 
186 	if (confkeyArray)
187 		values[Anum_pg_constraint_confkey - 1] = PointerGetDatum(confkeyArray);
188 	else
189 		nulls[Anum_pg_constraint_confkey - 1] = true;
190 
191 	if (conpfeqopArray)
192 		values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
193 	else
194 		nulls[Anum_pg_constraint_conpfeqop - 1] = true;
195 
196 	if (conppeqopArray)
197 		values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray);
198 	else
199 		nulls[Anum_pg_constraint_conppeqop - 1] = true;
200 
201 	if (conffeqopArray)
202 		values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray);
203 	else
204 		nulls[Anum_pg_constraint_conffeqop - 1] = true;
205 
206 	if (conexclopArray)
207 		values[Anum_pg_constraint_conexclop - 1] = PointerGetDatum(conexclopArray);
208 	else
209 		nulls[Anum_pg_constraint_conexclop - 1] = true;
210 
211 	/*
212 	 * initialize the binary form of the check constraint.
213 	 */
214 	if (conBin)
215 		values[Anum_pg_constraint_conbin - 1] = CStringGetTextDatum(conBin);
216 	else
217 		nulls[Anum_pg_constraint_conbin - 1] = true;
218 
219 	/*
220 	 * initialize the text form of the check constraint
221 	 */
222 	if (conSrc)
223 		values[Anum_pg_constraint_consrc - 1] = CStringGetTextDatum(conSrc);
224 	else
225 		nulls[Anum_pg_constraint_consrc - 1] = true;
226 
227 	tup = heap_form_tuple(RelationGetDescr(conDesc), values, nulls);
228 
229 	conOid = CatalogTupleInsert(conDesc, tup);
230 
231 	conobject.classId = ConstraintRelationId;
232 	conobject.objectId = conOid;
233 	conobject.objectSubId = 0;
234 
235 	heap_close(conDesc, RowExclusiveLock);
236 
237 	if (OidIsValid(relId))
238 	{
239 		/*
240 		 * Register auto dependency from constraint to owning relation, or to
241 		 * specific column(s) if any are mentioned.
242 		 */
243 		ObjectAddress relobject;
244 
245 		relobject.classId = RelationRelationId;
246 		relobject.objectId = relId;
247 		if (constraintNKeys > 0)
248 		{
249 			for (i = 0; i < constraintNKeys; i++)
250 			{
251 				relobject.objectSubId = constraintKey[i];
252 
253 				recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
254 			}
255 		}
256 		else
257 		{
258 			relobject.objectSubId = 0;
259 
260 			recordDependencyOn(&conobject, &relobject, DEPENDENCY_AUTO);
261 		}
262 	}
263 
264 	if (OidIsValid(domainId))
265 	{
266 		/*
267 		 * Register auto dependency from constraint to owning domain
268 		 */
269 		ObjectAddress domobject;
270 
271 		domobject.classId = TypeRelationId;
272 		domobject.objectId = domainId;
273 		domobject.objectSubId = 0;
274 
275 		recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
276 	}
277 
278 	if (OidIsValid(foreignRelId))
279 	{
280 		/*
281 		 * Register normal dependency from constraint to foreign relation, or
282 		 * to specific column(s) if any are mentioned.
283 		 */
284 		ObjectAddress relobject;
285 
286 		relobject.classId = RelationRelationId;
287 		relobject.objectId = foreignRelId;
288 		if (foreignNKeys > 0)
289 		{
290 			for (i = 0; i < foreignNKeys; i++)
291 			{
292 				relobject.objectSubId = foreignKey[i];
293 
294 				recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
295 			}
296 		}
297 		else
298 		{
299 			relobject.objectSubId = 0;
300 
301 			recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
302 		}
303 	}
304 
305 	if (OidIsValid(indexRelId) && constraintType == CONSTRAINT_FOREIGN)
306 	{
307 		/*
308 		 * Register normal dependency on the unique index that supports a
309 		 * foreign-key constraint.  (Note: for indexes associated with unique
310 		 * or primary-key constraints, the dependency runs the other way, and
311 		 * is not made here.)
312 		 */
313 		ObjectAddress relobject;
314 
315 		relobject.classId = RelationRelationId;
316 		relobject.objectId = indexRelId;
317 		relobject.objectSubId = 0;
318 
319 		recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
320 	}
321 
322 	if (foreignNKeys > 0)
323 	{
324 		/*
325 		 * Register normal dependencies on the equality operators that support
326 		 * a foreign-key constraint.  If the PK and FK types are the same then
327 		 * all three operators for a column are the same; otherwise they are
328 		 * different.
329 		 */
330 		ObjectAddress oprobject;
331 
332 		oprobject.classId = OperatorRelationId;
333 		oprobject.objectSubId = 0;
334 
335 		for (i = 0; i < foreignNKeys; i++)
336 		{
337 			oprobject.objectId = pfEqOp[i];
338 			recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
339 			if (ppEqOp[i] != pfEqOp[i])
340 			{
341 				oprobject.objectId = ppEqOp[i];
342 				recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
343 			}
344 			if (ffEqOp[i] != pfEqOp[i])
345 			{
346 				oprobject.objectId = ffEqOp[i];
347 				recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
348 			}
349 		}
350 	}
351 
352 	/*
353 	 * We don't bother to register dependencies on the exclusion operators of
354 	 * an exclusion constraint.  We assume they are members of the opclass
355 	 * supporting the index, so there's an indirect dependency via that. (This
356 	 * would be pretty dicey for cross-type operators, but exclusion operators
357 	 * can never be cross-type.)
358 	 */
359 
360 	if (conExpr != NULL)
361 	{
362 		/*
363 		 * Register dependencies from constraint to objects mentioned in CHECK
364 		 * expression.
365 		 */
366 		recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
367 										DEPENDENCY_NORMAL,
368 										DEPENDENCY_NORMAL, false);
369 	}
370 
371 	/* Post creation hook for new constraint */
372 	InvokeObjectPostCreateHookArg(ConstraintRelationId, conOid, 0,
373 								  is_internal);
374 
375 	return conOid;
376 }
377 
378 
379 /*
380  * Test whether given name is currently used as a constraint name
381  * for the given object (relation or domain).
382  *
383  * This is used to decide whether to accept a user-specified constraint name.
384  * It is deliberately not the same test as ChooseConstraintName uses to decide
385  * whether an auto-generated name is OK: here, we will allow it unless there
386  * is an identical constraint name in use *on the same object*.
387  *
388  * NB: Caller should hold exclusive lock on the given object, else
389  * this test can be fooled by concurrent additions.
390  */
391 bool
ConstraintNameIsUsed(ConstraintCategory conCat,Oid objId,Oid objNamespace,const char * conname)392 ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
393 					 Oid objNamespace, const char *conname)
394 {
395 	bool		found;
396 	Relation	conDesc;
397 	SysScanDesc conscan;
398 	ScanKeyData skey[2];
399 	HeapTuple	tup;
400 
401 	conDesc = heap_open(ConstraintRelationId, AccessShareLock);
402 
403 	found = false;
404 
405 	ScanKeyInit(&skey[0],
406 				Anum_pg_constraint_conname,
407 				BTEqualStrategyNumber, F_NAMEEQ,
408 				CStringGetDatum(conname));
409 
410 	ScanKeyInit(&skey[1],
411 				Anum_pg_constraint_connamespace,
412 				BTEqualStrategyNumber, F_OIDEQ,
413 				ObjectIdGetDatum(objNamespace));
414 
415 	conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
416 								 NULL, 2, skey);
417 
418 	while (HeapTupleIsValid(tup = systable_getnext(conscan)))
419 	{
420 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
421 
422 		if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
423 		{
424 			found = true;
425 			break;
426 		}
427 		else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
428 		{
429 			found = true;
430 			break;
431 		}
432 	}
433 
434 	systable_endscan(conscan);
435 	heap_close(conDesc, AccessShareLock);
436 
437 	return found;
438 }
439 
440 /*
441  * Select a nonconflicting name for a new constraint.
442  *
443  * The objective here is to choose a name that is unique within the
444  * specified namespace.  Postgres does not require this, but the SQL
445  * spec does, and some apps depend on it.  Therefore we avoid choosing
446  * default names that so conflict.
447  *
448  * name1, name2, and label are used the same way as for makeObjectName(),
449  * except that the label can't be NULL; digits will be appended to the label
450  * if needed to create a name that is unique within the specified namespace.
451  *
452  * 'others' can be a list of string names already chosen within the current
453  * command (but not yet reflected into the catalogs); we will not choose
454  * a duplicate of one of these either.
455  *
456  * Note: it is theoretically possible to get a collision anyway, if someone
457  * else chooses the same name concurrently.  This is fairly unlikely to be
458  * a problem in practice, especially if one is holding an exclusive lock on
459  * the relation identified by name1.
460  *
461  * Returns a palloc'd string.
462  */
463 char *
ChooseConstraintName(const char * name1,const char * name2,const char * label,Oid namespaceid,List * others)464 ChooseConstraintName(const char *name1, const char *name2,
465 					 const char *label, Oid namespaceid,
466 					 List *others)
467 {
468 	int			pass = 0;
469 	char	   *conname = NULL;
470 	char		modlabel[NAMEDATALEN];
471 	Relation	conDesc;
472 	SysScanDesc conscan;
473 	ScanKeyData skey[2];
474 	bool		found;
475 	ListCell   *l;
476 
477 	conDesc = heap_open(ConstraintRelationId, AccessShareLock);
478 
479 	/* try the unmodified label first */
480 	StrNCpy(modlabel, label, sizeof(modlabel));
481 
482 	for (;;)
483 	{
484 		conname = makeObjectName(name1, name2, modlabel);
485 
486 		found = false;
487 
488 		foreach(l, others)
489 		{
490 			if (strcmp((char *) lfirst(l), conname) == 0)
491 			{
492 				found = true;
493 				break;
494 			}
495 		}
496 
497 		if (!found)
498 		{
499 			ScanKeyInit(&skey[0],
500 						Anum_pg_constraint_conname,
501 						BTEqualStrategyNumber, F_NAMEEQ,
502 						CStringGetDatum(conname));
503 
504 			ScanKeyInit(&skey[1],
505 						Anum_pg_constraint_connamespace,
506 						BTEqualStrategyNumber, F_OIDEQ,
507 						ObjectIdGetDatum(namespaceid));
508 
509 			conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
510 										 NULL, 2, skey);
511 
512 			found = (HeapTupleIsValid(systable_getnext(conscan)));
513 
514 			systable_endscan(conscan);
515 		}
516 
517 		if (!found)
518 			break;
519 
520 		/* found a conflict, so try a new name component */
521 		pfree(conname);
522 		snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
523 	}
524 
525 	heap_close(conDesc, AccessShareLock);
526 
527 	return conname;
528 }
529 
530 /*
531  * Delete a single constraint record.
532  */
533 void
RemoveConstraintById(Oid conId)534 RemoveConstraintById(Oid conId)
535 {
536 	Relation	conDesc;
537 	HeapTuple	tup;
538 	Form_pg_constraint con;
539 
540 	conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
541 
542 	tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conId));
543 	if (!HeapTupleIsValid(tup)) /* should not happen */
544 		elog(ERROR, "cache lookup failed for constraint %u", conId);
545 	con = (Form_pg_constraint) GETSTRUCT(tup);
546 
547 	/*
548 	 * Special processing depending on what the constraint is for.
549 	 */
550 	if (OidIsValid(con->conrelid))
551 	{
552 		Relation	rel;
553 
554 		/*
555 		 * If the constraint is for a relation, open and exclusive-lock the
556 		 * relation it's for.
557 		 */
558 		rel = heap_open(con->conrelid, AccessExclusiveLock);
559 
560 		/*
561 		 * We need to update the relcheck count if it is a check constraint
562 		 * being dropped.  This update will force backends to rebuild relcache
563 		 * entries when we commit.
564 		 */
565 		if (con->contype == CONSTRAINT_CHECK)
566 		{
567 			Relation	pgrel;
568 			HeapTuple	relTup;
569 			Form_pg_class classForm;
570 
571 			pgrel = heap_open(RelationRelationId, RowExclusiveLock);
572 			relTup = SearchSysCacheCopy1(RELOID,
573 										 ObjectIdGetDatum(con->conrelid));
574 			if (!HeapTupleIsValid(relTup))
575 				elog(ERROR, "cache lookup failed for relation %u",
576 					 con->conrelid);
577 			classForm = (Form_pg_class) GETSTRUCT(relTup);
578 
579 			if (classForm->relchecks == 0)	/* should not happen */
580 				elog(ERROR, "relation \"%s\" has relchecks = 0",
581 					 RelationGetRelationName(rel));
582 			classForm->relchecks--;
583 
584 			CatalogTupleUpdate(pgrel, &relTup->t_self, relTup);
585 
586 			heap_freetuple(relTup);
587 
588 			heap_close(pgrel, RowExclusiveLock);
589 		}
590 
591 		/* Keep lock on constraint's rel until end of xact */
592 		heap_close(rel, NoLock);
593 	}
594 	else if (OidIsValid(con->contypid))
595 	{
596 		/*
597 		 * XXX for now, do nothing special when dropping a domain constraint
598 		 *
599 		 * Probably there should be some form of locking on the domain type,
600 		 * but we have no such concept at the moment.
601 		 */
602 	}
603 	else
604 		elog(ERROR, "constraint %u is not of a known type", conId);
605 
606 	/* Fry the constraint itself */
607 	CatalogTupleDelete(conDesc, &tup->t_self);
608 
609 	/* Clean up */
610 	ReleaseSysCache(tup);
611 	heap_close(conDesc, RowExclusiveLock);
612 }
613 
614 /*
615  * RenameConstraintById
616  *		Rename a constraint.
617  *
618  * Note: this isn't intended to be a user-exposed function; it doesn't check
619  * permissions etc.  Currently this is only invoked when renaming an index
620  * that is associated with a constraint, but it's made a little more general
621  * than that with the expectation of someday having ALTER TABLE RENAME
622  * CONSTRAINT.
623  */
624 void
RenameConstraintById(Oid conId,const char * newname)625 RenameConstraintById(Oid conId, const char *newname)
626 {
627 	Relation	conDesc;
628 	HeapTuple	tuple;
629 	Form_pg_constraint con;
630 
631 	conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
632 
633 	tuple = SearchSysCacheCopy1(CONSTROID, ObjectIdGetDatum(conId));
634 	if (!HeapTupleIsValid(tuple))
635 		elog(ERROR, "cache lookup failed for constraint %u", conId);
636 	con = (Form_pg_constraint) GETSTRUCT(tuple);
637 
638 	/*
639 	 * We need to check whether the name is already in use --- note that there
640 	 * currently is not a unique index that would catch this.
641 	 */
642 	if (OidIsValid(con->conrelid) &&
643 		ConstraintNameIsUsed(CONSTRAINT_RELATION,
644 							 con->conrelid,
645 							 con->connamespace,
646 							 newname))
647 		ereport(ERROR,
648 				(errcode(ERRCODE_DUPLICATE_OBJECT),
649 				 errmsg("constraint \"%s\" for relation \"%s\" already exists",
650 						newname, get_rel_name(con->conrelid))));
651 	if (OidIsValid(con->contypid) &&
652 		ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
653 							 con->contypid,
654 							 con->connamespace,
655 							 newname))
656 		ereport(ERROR,
657 				(errcode(ERRCODE_DUPLICATE_OBJECT),
658 				 errmsg("constraint \"%s\" for domain %s already exists",
659 						newname, format_type_be(con->contypid))));
660 
661 	/* OK, do the rename --- tuple is a copy, so OK to scribble on it */
662 	namestrcpy(&(con->conname), newname);
663 
664 	CatalogTupleUpdate(conDesc, &tuple->t_self, tuple);
665 
666 	InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0);
667 
668 	heap_freetuple(tuple);
669 	heap_close(conDesc, RowExclusiveLock);
670 }
671 
672 /*
673  * AlterConstraintNamespaces
674  *		Find any constraints belonging to the specified object,
675  *		and move them to the specified new namespace.
676  *
677  * isType indicates whether the owning object is a type or a relation.
678  */
679 void
AlterConstraintNamespaces(Oid ownerId,Oid oldNspId,Oid newNspId,bool isType,ObjectAddresses * objsMoved)680 AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
681 						  Oid newNspId, bool isType, ObjectAddresses *objsMoved)
682 {
683 	Relation	conRel;
684 	ScanKeyData key[1];
685 	SysScanDesc scan;
686 	HeapTuple	tup;
687 
688 	conRel = heap_open(ConstraintRelationId, RowExclusiveLock);
689 
690 	if (isType)
691 	{
692 		ScanKeyInit(&key[0],
693 					Anum_pg_constraint_contypid,
694 					BTEqualStrategyNumber, F_OIDEQ,
695 					ObjectIdGetDatum(ownerId));
696 
697 		scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
698 								  NULL, 1, key);
699 	}
700 	else
701 	{
702 		ScanKeyInit(&key[0],
703 					Anum_pg_constraint_conrelid,
704 					BTEqualStrategyNumber, F_OIDEQ,
705 					ObjectIdGetDatum(ownerId));
706 
707 		scan = systable_beginscan(conRel, ConstraintRelidIndexId, true,
708 								  NULL, 1, key);
709 	}
710 
711 	while (HeapTupleIsValid((tup = systable_getnext(scan))))
712 	{
713 		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
714 		ObjectAddress thisobj;
715 
716 		thisobj.classId = ConstraintRelationId;
717 		thisobj.objectId = HeapTupleGetOid(tup);
718 		thisobj.objectSubId = 0;
719 
720 		if (object_address_present(&thisobj, objsMoved))
721 			continue;
722 
723 		/* Don't update if the object is already part of the namespace */
724 		if (conform->connamespace == oldNspId && oldNspId != newNspId)
725 		{
726 			tup = heap_copytuple(tup);
727 			conform = (Form_pg_constraint) GETSTRUCT(tup);
728 
729 			conform->connamespace = newNspId;
730 
731 			CatalogTupleUpdate(conRel, &tup->t_self, tup);
732 
733 			/*
734 			 * Note: currently, the constraint will not have its own
735 			 * dependency on the namespace, so we don't need to do
736 			 * changeDependencyFor().
737 			 */
738 		}
739 
740 		InvokeObjectPostAlterHook(ConstraintRelationId, thisobj.objectId, 0);
741 
742 		add_exact_object_address(&thisobj, objsMoved);
743 	}
744 
745 	systable_endscan(scan);
746 
747 	heap_close(conRel, RowExclusiveLock);
748 }
749 
750 /*
751  * get_relation_constraint_oid
752  *		Find a constraint on the specified relation with the specified name.
753  *		Returns constraint's OID.
754  */
755 Oid
get_relation_constraint_oid(Oid relid,const char * conname,bool missing_ok)756 get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
757 {
758 	Relation	pg_constraint;
759 	HeapTuple	tuple;
760 	SysScanDesc scan;
761 	ScanKeyData skey[1];
762 	Oid			conOid = InvalidOid;
763 
764 	/*
765 	 * Fetch the constraint tuple from pg_constraint.  There may be more than
766 	 * one match, because constraints are not required to have unique names;
767 	 * if so, error out.
768 	 */
769 	pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
770 
771 	ScanKeyInit(&skey[0],
772 				Anum_pg_constraint_conrelid,
773 				BTEqualStrategyNumber, F_OIDEQ,
774 				ObjectIdGetDatum(relid));
775 
776 	scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
777 							  NULL, 1, skey);
778 
779 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
780 	{
781 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
782 
783 		if (strcmp(NameStr(con->conname), conname) == 0)
784 		{
785 			if (OidIsValid(conOid))
786 				ereport(ERROR,
787 						(errcode(ERRCODE_DUPLICATE_OBJECT),
788 						 errmsg("table \"%s\" has multiple constraints named \"%s\"",
789 								get_rel_name(relid), conname)));
790 			conOid = HeapTupleGetOid(tuple);
791 		}
792 	}
793 
794 	systable_endscan(scan);
795 
796 	/* If no such constraint exists, complain */
797 	if (!OidIsValid(conOid) && !missing_ok)
798 		ereport(ERROR,
799 				(errcode(ERRCODE_UNDEFINED_OBJECT),
800 				 errmsg("constraint \"%s\" for table \"%s\" does not exist",
801 						conname, get_rel_name(relid))));
802 
803 	heap_close(pg_constraint, AccessShareLock);
804 
805 	return conOid;
806 }
807 
808 /*
809  * get_relation_constraint_attnos
810  *		Find a constraint on the specified relation with the specified name
811  *		and return the constrained columns.
812  *
813  * Returns a Bitmapset of the column attnos of the constrained columns, with
814  * attnos being offset by FirstLowInvalidHeapAttributeNumber so that system
815  * columns can be represented.
816  *
817  * *constraintOid is set to the OID of the constraint, or InvalidOid on
818  * failure.
819  */
820 Bitmapset *
get_relation_constraint_attnos(Oid relid,const char * conname,bool missing_ok,Oid * constraintOid)821 get_relation_constraint_attnos(Oid relid, const char *conname,
822 							   bool missing_ok, Oid *constraintOid)
823 {
824 	Bitmapset  *conattnos = NULL;
825 	Relation	pg_constraint;
826 	HeapTuple	tuple;
827 	SysScanDesc scan;
828 	ScanKeyData skey[1];
829 
830 	/* Set *constraintOid, to avoid complaints about uninitialized vars */
831 	*constraintOid = InvalidOid;
832 
833 	/*
834 	 * Fetch the constraint tuple from pg_constraint.  There may be more than
835 	 * one match, because constraints are not required to have unique names;
836 	 * if so, error out.
837 	 */
838 	pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
839 
840 	ScanKeyInit(&skey[0],
841 				Anum_pg_constraint_conrelid,
842 				BTEqualStrategyNumber, F_OIDEQ,
843 				ObjectIdGetDatum(relid));
844 
845 	scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
846 							  NULL, 1, skey);
847 
848 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
849 	{
850 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
851 		Datum		adatum;
852 		bool		isNull;
853 		ArrayType  *arr;
854 		int16	   *attnums;
855 		int			numcols;
856 		int			i;
857 
858 		/* Check the constraint name */
859 		if (strcmp(NameStr(con->conname), conname) != 0)
860 			continue;
861 		if (OidIsValid(*constraintOid))
862 			ereport(ERROR,
863 					(errcode(ERRCODE_DUPLICATE_OBJECT),
864 					 errmsg("table \"%s\" has multiple constraints named \"%s\"",
865 							get_rel_name(relid), conname)));
866 
867 		*constraintOid = HeapTupleGetOid(tuple);
868 
869 		/* Extract the conkey array, ie, attnums of constrained columns */
870 		adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
871 							  RelationGetDescr(pg_constraint), &isNull);
872 		if (isNull)
873 			continue;			/* no constrained columns */
874 
875 		arr = DatumGetArrayTypeP(adatum);	/* ensure not toasted */
876 		numcols = ARR_DIMS(arr)[0];
877 		if (ARR_NDIM(arr) != 1 ||
878 			numcols < 0 ||
879 			ARR_HASNULL(arr) ||
880 			ARR_ELEMTYPE(arr) != INT2OID)
881 			elog(ERROR, "conkey is not a 1-D smallint array");
882 		attnums = (int16 *) ARR_DATA_PTR(arr);
883 
884 		/* Construct the result value */
885 		for (i = 0; i < numcols; i++)
886 		{
887 			conattnos = bms_add_member(conattnos,
888 									   attnums[i] - FirstLowInvalidHeapAttributeNumber);
889 		}
890 	}
891 
892 	systable_endscan(scan);
893 
894 	/* If no such constraint exists, complain */
895 	if (!OidIsValid(*constraintOid) && !missing_ok)
896 		ereport(ERROR,
897 				(errcode(ERRCODE_UNDEFINED_OBJECT),
898 				 errmsg("constraint \"%s\" for table \"%s\" does not exist",
899 						conname, get_rel_name(relid))));
900 
901 	heap_close(pg_constraint, AccessShareLock);
902 
903 	return conattnos;
904 }
905 
906 /*
907  * get_domain_constraint_oid
908  *		Find a constraint on the specified domain with the specified name.
909  *		Returns constraint's OID.
910  */
911 Oid
get_domain_constraint_oid(Oid typid,const char * conname,bool missing_ok)912 get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
913 {
914 	Relation	pg_constraint;
915 	HeapTuple	tuple;
916 	SysScanDesc scan;
917 	ScanKeyData skey[1];
918 	Oid			conOid = InvalidOid;
919 
920 	/*
921 	 * Fetch the constraint tuple from pg_constraint.  There may be more than
922 	 * one match, because constraints are not required to have unique names;
923 	 * if so, error out.
924 	 */
925 	pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
926 
927 	ScanKeyInit(&skey[0],
928 				Anum_pg_constraint_contypid,
929 				BTEqualStrategyNumber, F_OIDEQ,
930 				ObjectIdGetDatum(typid));
931 
932 	scan = systable_beginscan(pg_constraint, ConstraintTypidIndexId, true,
933 							  NULL, 1, skey);
934 
935 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
936 	{
937 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
938 
939 		if (strcmp(NameStr(con->conname), conname) == 0)
940 		{
941 			if (OidIsValid(conOid))
942 				ereport(ERROR,
943 						(errcode(ERRCODE_DUPLICATE_OBJECT),
944 						 errmsg("domain %s has multiple constraints named \"%s\"",
945 								format_type_be(typid), conname)));
946 			conOid = HeapTupleGetOid(tuple);
947 		}
948 	}
949 
950 	systable_endscan(scan);
951 
952 	/* If no such constraint exists, complain */
953 	if (!OidIsValid(conOid) && !missing_ok)
954 		ereport(ERROR,
955 				(errcode(ERRCODE_UNDEFINED_OBJECT),
956 				 errmsg("constraint \"%s\" for domain %s does not exist",
957 						conname, format_type_be(typid))));
958 
959 	heap_close(pg_constraint, AccessShareLock);
960 
961 	return conOid;
962 }
963 
964 /*
965  * get_primary_key_attnos
966  *		Identify the columns in a relation's primary key, if any.
967  *
968  * Returns a Bitmapset of the column attnos of the primary key's columns,
969  * with attnos being offset by FirstLowInvalidHeapAttributeNumber so that
970  * system columns can be represented.
971  *
972  * If there is no primary key, return NULL.  We also return NULL if the pkey
973  * constraint is deferrable and deferrableOk is false.
974  *
975  * *constraintOid is set to the OID of the pkey constraint, or InvalidOid
976  * on failure.
977  */
978 Bitmapset *
get_primary_key_attnos(Oid relid,bool deferrableOk,Oid * constraintOid)979 get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
980 {
981 	Bitmapset  *pkattnos = NULL;
982 	Relation	pg_constraint;
983 	HeapTuple	tuple;
984 	SysScanDesc scan;
985 	ScanKeyData skey[1];
986 
987 	/* Set *constraintOid, to avoid complaints about uninitialized vars */
988 	*constraintOid = InvalidOid;
989 
990 	/* Scan pg_constraint for constraints of the target rel */
991 	pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
992 
993 	ScanKeyInit(&skey[0],
994 				Anum_pg_constraint_conrelid,
995 				BTEqualStrategyNumber, F_OIDEQ,
996 				ObjectIdGetDatum(relid));
997 
998 	scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
999 							  NULL, 1, skey);
1000 
1001 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
1002 	{
1003 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
1004 		Datum		adatum;
1005 		bool		isNull;
1006 		ArrayType  *arr;
1007 		int16	   *attnums;
1008 		int			numkeys;
1009 		int			i;
1010 
1011 		/* Skip constraints that are not PRIMARY KEYs */
1012 		if (con->contype != CONSTRAINT_PRIMARY)
1013 			continue;
1014 
1015 		/*
1016 		 * If the primary key is deferrable, but we've been instructed to
1017 		 * ignore deferrable constraints, then we might as well give up
1018 		 * searching, since there can only be a single primary key on a table.
1019 		 */
1020 		if (con->condeferrable && !deferrableOk)
1021 			break;
1022 
1023 		/* Extract the conkey array, ie, attnums of PK's columns */
1024 		adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
1025 							  RelationGetDescr(pg_constraint), &isNull);
1026 		if (isNull)
1027 			elog(ERROR, "null conkey for constraint %u",
1028 				 HeapTupleGetOid(tuple));
1029 		arr = DatumGetArrayTypeP(adatum);	/* ensure not toasted */
1030 		numkeys = ARR_DIMS(arr)[0];
1031 		if (ARR_NDIM(arr) != 1 ||
1032 			numkeys < 0 ||
1033 			ARR_HASNULL(arr) ||
1034 			ARR_ELEMTYPE(arr) != INT2OID)
1035 			elog(ERROR, "conkey is not a 1-D smallint array");
1036 		attnums = (int16 *) ARR_DATA_PTR(arr);
1037 
1038 		/* Construct the result value */
1039 		for (i = 0; i < numkeys; i++)
1040 		{
1041 			pkattnos = bms_add_member(pkattnos,
1042 									  attnums[i] - FirstLowInvalidHeapAttributeNumber);
1043 		}
1044 		*constraintOid = HeapTupleGetOid(tuple);
1045 
1046 		/* No need to search further */
1047 		break;
1048 	}
1049 
1050 	systable_endscan(scan);
1051 
1052 	heap_close(pg_constraint, AccessShareLock);
1053 
1054 	return pkattnos;
1055 }
1056 
1057 /*
1058  * Determine whether a relation can be proven functionally dependent on
1059  * a set of grouping columns.  If so, return TRUE and add the pg_constraint
1060  * OIDs of the constraints needed for the proof to the *constraintDeps list.
1061  *
1062  * grouping_columns is a list of grouping expressions, in which columns of
1063  * the rel of interest are Vars with the indicated varno/varlevelsup.
1064  *
1065  * Currently we only check to see if the rel has a primary key that is a
1066  * subset of the grouping_columns.  We could also use plain unique constraints
1067  * if all their columns are known not null, but there's a problem: we need
1068  * to be able to represent the not-null-ness as part of the constraints added
1069  * to *constraintDeps.  FIXME whenever not-null constraints get represented
1070  * in pg_constraint.
1071  */
1072 bool
check_functional_grouping(Oid relid,Index varno,Index varlevelsup,List * grouping_columns,List ** constraintDeps)1073 check_functional_grouping(Oid relid,
1074 						  Index varno, Index varlevelsup,
1075 						  List *grouping_columns,
1076 						  List **constraintDeps)
1077 {
1078 	Bitmapset  *pkattnos;
1079 	Bitmapset  *groupbyattnos;
1080 	Oid			constraintOid;
1081 	ListCell   *gl;
1082 
1083 	/* If the rel has no PK, then we can't prove functional dependency */
1084 	pkattnos = get_primary_key_attnos(relid, false, &constraintOid);
1085 	if (pkattnos == NULL)
1086 		return false;
1087 
1088 	/* Identify all the rel's columns that appear in grouping_columns */
1089 	groupbyattnos = NULL;
1090 	foreach(gl, grouping_columns)
1091 	{
1092 		Var		   *gvar = (Var *) lfirst(gl);
1093 
1094 		if (IsA(gvar, Var) &&
1095 			gvar->varno == varno &&
1096 			gvar->varlevelsup == varlevelsup)
1097 			groupbyattnos = bms_add_member(groupbyattnos,
1098 										   gvar->varattno - FirstLowInvalidHeapAttributeNumber);
1099 	}
1100 
1101 	if (bms_is_subset(pkattnos, groupbyattnos))
1102 	{
1103 		/* The PK is a subset of grouping_columns, so we win */
1104 		*constraintDeps = lappend_oid(*constraintDeps, constraintOid);
1105 		return true;
1106 	}
1107 
1108 	return false;
1109 }
1110