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