dependency.c 43.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*-------------------------------------------------------------------------
 *
 * dependency.c
 *	  Routines to support inter-object dependencies.
 *
 *
 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
11
 *	  $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.17 2002/12/06 05:00:10 momjian Exp $
12 13 14 15 16 17 18 19 20 21 22 23
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
24
#include "catalog/namespace.h"
25
#include "catalog/pg_attrdef.h"
26
#include "catalog/pg_cast.h"
27
#include "catalog/pg_constraint.h"
Tatsuo Ishii's avatar
Tatsuo Ishii committed
28
#include "catalog/pg_conversion.h"
29 30
#include "catalog/pg_depend.h"
#include "catalog/pg_language.h"
31
#include "catalog/pg_opclass.h"
32 33 34 35 36
#include "catalog/pg_rewrite.h"
#include "catalog/pg_trigger.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/proclang.h"
37
#include "commands/schemacmds.h"
38
#include "commands/trigger.h"
39
#include "commands/typecmds.h"
40 41
#include "lib/stringinfo.h"
#include "miscadmin.h"
42 43
#include "optimizer/clauses.h"
#include "parser/parsetree.h"
44
#include "rewrite/rewriteRemove.h"
45
#include "utils/builtins.h"
46 47 48 49 50 51 52 53 54 55 56
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"


/* This enum covers all system catalogs whose OIDs can appear in classid. */
typedef enum ObjectClasses
{
	OCLASS_CLASS,				/* pg_class */
	OCLASS_PROC,				/* pg_proc */
	OCLASS_TYPE,				/* pg_type */
57
	OCLASS_CAST,				/* pg_cast */
58
	OCLASS_CONSTRAINT,			/* pg_constraint */
Tatsuo Ishii's avatar
Tatsuo Ishii committed
59
	OCLASS_CONVERSION,			/* pg_conversion */
60
	OCLASS_DEFAULT,				/* pg_attrdef */
61 62
	OCLASS_LANGUAGE,			/* pg_language */
	OCLASS_OPERATOR,			/* pg_operator */
63
	OCLASS_OPCLASS,				/* pg_opclass */
64
	OCLASS_REWRITE,				/* pg_rewrite */
65
	OCLASS_TRIGGER,				/* pg_trigger */
66
	OCLASS_SCHEMA,				/* pg_namespace */
67
	MAX_OCLASS					/* MUST BE LAST */
68 69
} ObjectClasses;

70
/* expansible list of ObjectAddresses */
71
typedef struct
72
{
Bruce Momjian's avatar
Bruce Momjian committed
73 74 75
	ObjectAddress *refs;		/* => palloc'd array */
	int			numrefs;		/* current number of references */
	int			maxrefs;		/* current size of palloc'd array */
76 77 78 79 80
} ObjectAddresses;

/* for find_expr_references_walker */
typedef struct
{
Bruce Momjian's avatar
Bruce Momjian committed
81 82
	ObjectAddresses addrs;		/* addresses being accumulated */
	List	   *rtables;		/* list of rangetables to resolve Vars */
83 84 85 86 87
} find_expr_references_context;


/*
 * Because not all system catalogs have predetermined OIDs, we build a table
Bruce Momjian's avatar
Bruce Momjian committed
88
 * mapping between ObjectClasses and OIDs.	This is done at most once per
89 90
 * backend run, to minimize lookup overhead.
 */
Bruce Momjian's avatar
Bruce Momjian committed
91
static bool object_classes_initialized = false;
92 93 94
static Oid	object_classes[MAX_OCLASS];


95 96 97
static void findAutoDeletableObjects(const ObjectAddress *object,
									 ObjectAddresses *oktodelete,
									 Relation depRel);
98
static bool recursiveDeletion(const ObjectAddress *object,
Bruce Momjian's avatar
Bruce Momjian committed
99 100
				  DropBehavior behavior,
				  const ObjectAddress *callingObject,
101
				  ObjectAddresses *oktodelete,
Bruce Momjian's avatar
Bruce Momjian committed
102
				  Relation depRel);
103
static void doDeletion(const ObjectAddress *object);
104
static bool find_expr_references_walker(Node *node,
Bruce Momjian's avatar
Bruce Momjian committed
105
							find_expr_references_context *context);
106 107 108 109
static void eliminate_duplicate_dependencies(ObjectAddresses *addrs);
static int	object_address_comparator(const void *a, const void *b);
static void init_object_addresses(ObjectAddresses *addrs);
static void add_object_address(ObjectClasses oclass, Oid objectId, int32 subId,
Bruce Momjian's avatar
Bruce Momjian committed
110
				   ObjectAddresses *addrs);
111
static void add_exact_object_address(const ObjectAddress *object,
Bruce Momjian's avatar
Bruce Momjian committed
112
						 ObjectAddresses *addrs);
113
static bool object_address_present(const ObjectAddress *object,
Bruce Momjian's avatar
Bruce Momjian committed
114
				   ObjectAddresses *addrs);
115 116
static void term_object_addresses(ObjectAddresses *addrs);
static void init_object_classes(void);
117 118
static ObjectClasses getObjectClass(const ObjectAddress *object);
static char *getObjectDescription(const ObjectAddress *object);
119
static void getRelationDescription(StringInfo buffer, Oid relid);
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135


/*
 * performDeletion: attempt to drop the specified object.  If CASCADE
 * behavior is specified, also drop any dependent objects (recursively).
 * If RESTRICT behavior is specified, error out if there are any dependent
 * objects, except for those that should be implicitly dropped anyway
 * according to the dependency type.
 *
 * This is the outer control routine for all forms of DROP that drop objects
 * that can participate in dependencies.
 */
void
performDeletion(const ObjectAddress *object,
				DropBehavior behavior)
{
Bruce Momjian's avatar
Bruce Momjian committed
136 137
	char	   *objDescription;
	Relation	depRel;
138
	ObjectAddresses oktodelete;
139 140

	/*
Bruce Momjian's avatar
Bruce Momjian committed
141 142
	 * Get object description for possible use in failure message. Must do
	 * this before deleting it ...
143 144 145 146 147 148 149 150 151
	 */
	objDescription = getObjectDescription(object);

	/*
	 * We save some cycles by opening pg_depend just once and passing the
	 * Relation pointer down to all the recursive deletion steps.
	 */
	depRel = heap_openr(DependRelationName, RowExclusiveLock);

152 153 154 155 156 157 158 159 160 161 162
	/*
	 * Construct a list of objects that are reachable by AUTO or INTERNAL
	 * dependencies from the target object.  These should be deleted silently,
	 * even if the actual deletion pass first reaches one of them via a
	 * non-auto dependency.
	 */
	init_object_addresses(&oktodelete);

	findAutoDeletableObjects(object, &oktodelete, depRel);

	if (!recursiveDeletion(object, behavior, NULL, &oktodelete, depRel))
163 164 165 166
		elog(ERROR, "Cannot drop %s because other objects depend on it"
			 "\n\tUse DROP ... CASCADE to drop the dependent objects too",
			 objDescription);

167 168
	term_object_addresses(&oktodelete);

169 170 171 172 173 174 175
	heap_close(depRel, RowExclusiveLock);

	pfree(objDescription);
}


/*
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
 * findAutoDeletableObjects: find all objects that are reachable by AUTO or
 * INTERNAL dependency paths from the given object.  Add them all to the
 * oktodelete list.  Note that the originally given object will also be
 * added to the list.
 *
 * depRel is the already-open pg_depend relation.
 */
static void
findAutoDeletableObjects(const ObjectAddress *object,
						 ObjectAddresses *oktodelete,
						 Relation depRel)
{
	ScanKeyData key[3];
	int			nkeys;
	SysScanDesc scan;
	HeapTuple	tup;
	ObjectAddress otherObject;

	/*
	 * If this object is already in oktodelete, then we already visited it;
	 * don't do so again (this prevents infinite recursion if there's a loop
	 * in pg_depend).  Otherwise, add it.
	 */
	if (object_address_present(object, oktodelete))
		return;
	add_exact_object_address(object, oktodelete);

	/*
	 * Scan pg_depend records that link to this object, showing the things
	 * that depend on it.  For each one that is AUTO or INTERNAL, visit the
	 * referencing object.
	 *
	 * When dropping a whole object (subId = 0), find pg_depend records for
	 * its sub-objects too.
	 */
	ScanKeyEntryInitialize(&key[0], 0x0,
						   Anum_pg_depend_refclassid, F_OIDEQ,
						   ObjectIdGetDatum(object->classId));
	ScanKeyEntryInitialize(&key[1], 0x0,
						   Anum_pg_depend_refobjid, F_OIDEQ,
						   ObjectIdGetDatum(object->objectId));
	if (object->objectSubId != 0)
	{
		ScanKeyEntryInitialize(&key[2], 0x0,
							   Anum_pg_depend_refobjsubid, F_INT4EQ,
							   Int32GetDatum(object->objectSubId));
		nkeys = 3;
	}
	else
		nkeys = 2;

	scan = systable_beginscan(depRel, DependReferenceIndex, true,
							  SnapshotNow, nkeys, key);

	while (HeapTupleIsValid(tup = systable_getnext(scan)))
	{
		Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);

		switch (foundDep->deptype)
		{
			case DEPENDENCY_NORMAL:
				/* ignore */
				break;
			case DEPENDENCY_AUTO:
			case DEPENDENCY_INTERNAL:
				/* recurse */
				otherObject.classId = foundDep->classid;
				otherObject.objectId = foundDep->objid;
				otherObject.objectSubId = foundDep->objsubid;
				findAutoDeletableObjects(&otherObject, oktodelete, depRel);
				break;
			case DEPENDENCY_PIN:
				/*
				 * For a PIN dependency we just elog immediately; there
				 * won't be any others to examine, and we aren't ever
				 * going to let the user delete it.
				 */
				elog(ERROR, "Cannot drop %s because it is required by the database system",
					 getObjectDescription(object));
				break;
			default:
				elog(ERROR, "findAutoDeletableObjects: unknown dependency type '%c' for %s",
					 foundDep->deptype, getObjectDescription(object));
				break;
		}
	}

	systable_endscan(scan);
}


/*
 * recursiveDeletion: delete a single object for performDeletion, plus
 * (recursively) anything that depends on it.
270
 *
271 272 273 274
 * Returns TRUE if successful, FALSE if not.
 *
 * callingObject is NULL at the outer level, else identifies the object that
 * we recursed from (the reference object that someone else needs to delete).
275 276 277 278
 *
 * oktodelete is a list of objects verified deletable (ie, reachable by one
 * or more AUTO or INTERNAL dependencies from the original target).
 *
279
 * depRel is the already-open pg_depend relation.
280
 *
281
 *
282 283 284 285 286
 * In RESTRICT mode, we perform all the deletions anyway, but elog a NOTICE
 * and return FALSE if we find a restriction violation.  performDeletion
 * will then abort the transaction to nullify the deletions.  We have to
 * do it this way to (a) report all the direct and indirect dependencies
 * while (b) not going into infinite recursion if there's a cycle.
287 288
 *
 * This is even more complex than one could wish, because it is possible for
289 290 291 292 293 294 295 296 297 298 299
 * the same pair of objects to be related by both NORMAL and AUTO/INTERNAL
 * dependencies.  Also, we might have a situation where we've been asked to
 * delete object A, and objects B and C both have AUTO dependencies on A,
 * but B also has a NORMAL dependency on C.  (Since any of these paths might
 * be indirect, we can't prevent these scenarios, but must cope instead.)
 * If we visit C before B then we would mistakenly decide that the B->C link
 * should prevent the restricted drop from occurring.  To handle this, we make
 * a pre-scan to find all the objects that are auto-deletable from A.  If we
 * visit C first, but B is present in the oktodelete list, then we make no
 * complaint but recurse to delete B anyway.  (Note that in general we must
 * delete B before deleting C; the drop routine for B may try to access C.)
300
 *
301 302 303 304
 * Note: in the case where the path to B is traversed first, we will not
 * see the NORMAL dependency when we reach C, because of the pg_depend
 * removals done in step 1.  The oktodelete list is necessary just
 * to make the behavior independent of the order in which pg_depend
305
 * entries are visited.
306 307 308 309
 */
static bool
recursiveDeletion(const ObjectAddress *object,
				  DropBehavior behavior,
310
				  const ObjectAddress *callingObject,
311
				  ObjectAddresses *oktodelete,
312 313
				  Relation depRel)
{
Bruce Momjian's avatar
Bruce Momjian committed
314 315 316 317 318 319 320 321 322
	bool		ok = true;
	char	   *objDescription;
	ScanKeyData key[3];
	int			nkeys;
	SysScanDesc scan;
	HeapTuple	tup;
	ObjectAddress otherObject;
	ObjectAddress owningObject;
	bool		amOwned = false;
323 324 325 326 327 328 329 330 331 332 333

	/*
	 * Get object description for possible use in messages.  Must do this
	 * before deleting it ...
	 */
	objDescription = getObjectDescription(object);

	/*
	 * Step 1: find and remove pg_depend records that link from this
	 * object to others.  We have to do this anyway, and doing it first
	 * ensures that we avoid infinite recursion in the case of cycles.
334
	 * Also, some dependency types require extra processing here.
335
	 *
Bruce Momjian's avatar
Bruce Momjian committed
336 337
	 * When dropping a whole object (subId = 0), remove all pg_depend records
	 * for its sub-objects too.
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
	 */
	ScanKeyEntryInitialize(&key[0], 0x0,
						   Anum_pg_depend_classid, F_OIDEQ,
						   ObjectIdGetDatum(object->classId));
	ScanKeyEntryInitialize(&key[1], 0x0,
						   Anum_pg_depend_objid, F_OIDEQ,
						   ObjectIdGetDatum(object->objectId));
	if (object->objectSubId != 0)
	{
		ScanKeyEntryInitialize(&key[2], 0x0,
							   Anum_pg_depend_objsubid, F_INT4EQ,
							   Int32GetDatum(object->objectSubId));
		nkeys = 3;
	}
	else
		nkeys = 2;

	scan = systable_beginscan(depRel, DependDependerIndex, true,
							  SnapshotNow, nkeys, key);

	while (HeapTupleIsValid(tup = systable_getnext(scan)))
	{
Bruce Momjian's avatar
Bruce Momjian committed
360
		Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
361 362 363 364 365 366 367 368 369 370 371 372

		otherObject.classId = foundDep->refclassid;
		otherObject.objectId = foundDep->refobjid;
		otherObject.objectSubId = foundDep->refobjsubid;

		switch (foundDep->deptype)
		{
			case DEPENDENCY_NORMAL:
			case DEPENDENCY_AUTO:
				/* no problem */
				break;
			case DEPENDENCY_INTERNAL:
Bruce Momjian's avatar
Bruce Momjian committed
373

374
				/*
Bruce Momjian's avatar
Bruce Momjian committed
375 376
				 * This object is part of the internal implementation of
				 * another object.	We have three cases:
377 378
				 *
				 * 1. At the outermost recursion level, disallow the DROP.
379 380
				 * (We just elog here, rather than proceeding, since no
				 * other dependencies are likely to be interesting.)
381
				 */
382 383
				if (callingObject == NULL)
				{
Bruce Momjian's avatar
Bruce Momjian committed
384
					char	   *otherObjDesc = getObjectDescription(&otherObject);
385

386
					elog(ERROR, "Cannot drop %s because %s requires it"
387 388 389
						 "\n\tYou may drop %s instead",
						 objDescription, otherObjDesc, otherObjDesc);
				}
Bruce Momjian's avatar
Bruce Momjian committed
390

391
				/*
Bruce Momjian's avatar
Bruce Momjian committed
392 393 394 395
				 * 2. When recursing from the other end of this
				 * dependency, it's okay to continue with the deletion.
				 * This holds when recursing from a whole object that
				 * includes the nominal other end as a component, too.
396 397 398
				 */
				if (callingObject->classId == otherObject.classId &&
					callingObject->objectId == otherObject.objectId &&
Bruce Momjian's avatar
Bruce Momjian committed
399 400
				(callingObject->objectSubId == otherObject.objectSubId ||
				 callingObject->objectSubId == 0))
401
					break;
Bruce Momjian's avatar
Bruce Momjian committed
402

403 404 405
				/*
				 * 3. When recursing from anyplace else, transform this
				 * deletion request into a delete of the other object.
Bruce Momjian's avatar
Bruce Momjian committed
406 407 408 409
				 * (This will be an error condition iff RESTRICT mode.) In
				 * this case we finish deleting my dependencies except for
				 * the INTERNAL link, which will be needed to cause the
				 * owning object to recurse back to me.
410 411 412 413 414 415 416 417
				 */
				if (amOwned)	/* shouldn't happen */
					elog(ERROR, "recursiveDeletion: multiple INTERNAL dependencies for %s",
						 objDescription);
				owningObject = otherObject;
				amOwned = true;
				/* "continue" bypasses the simple_heap_delete call below */
				continue;
418
			case DEPENDENCY_PIN:
Bruce Momjian's avatar
Bruce Momjian committed
419

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
				/*
				 * Should not happen; PIN dependencies should have zeroes
				 * in the depender fields...
				 */
				elog(ERROR, "recursiveDeletion: incorrect use of PIN dependency with %s",
					 objDescription);
				break;
			default:
				elog(ERROR, "recursiveDeletion: unknown dependency type '%c' for %s",
					 foundDep->deptype, objDescription);
				break;
		}

		simple_heap_delete(depRel, &tup->t_self);
	}

	systable_endscan(scan);

	/*
Bruce Momjian's avatar
Bruce Momjian committed
439 440 441 442
	 * CommandCounterIncrement here to ensure that preceding changes are
	 * all visible; in particular, that the above deletions of pg_depend
	 * entries are visible.  That prevents infinite recursion in case of a
	 * dependency loop (which is perfectly legal).
443 444 445
	 */
	CommandCounterIncrement();

446 447
	/*
	 * If we found we are owned by another object, ask it to delete itself
448 449
	 * instead of proceeding.  Complain if RESTRICT mode, unless the other
	 * object is in oktodelete.
450 451 452
	 */
	if (amOwned)
	{
453 454 455 456
		if (object_address_present(&owningObject, oktodelete))
			elog(DEBUG1, "Drop auto-cascades to %s",
				 getObjectDescription(&owningObject));
		else if (behavior == DROP_RESTRICT)
457 458 459 460 461 462 463 464 465 466 467 468
		{
			elog(NOTICE, "%s depends on %s",
				 getObjectDescription(&owningObject),
				 objDescription);
			ok = false;
		}
		else
			elog(NOTICE, "Drop cascades to %s",
				 getObjectDescription(&owningObject));

		if (!recursiveDeletion(&owningObject, behavior,
							   object,
469
							   oktodelete, depRel))
470 471 472 473 474 475 476
			ok = false;

		pfree(objDescription);

		return ok;
	}

477 478
	/*
	 * Step 2: scan pg_depend records that link to this object, showing
Bruce Momjian's avatar
Bruce Momjian committed
479 480 481
	 * the things that depend on it.  Recursively delete those things. (We
	 * don't delete the pg_depend records here, as the recursive call will
	 * do that.)  Note it's important to delete the dependent objects
482
	 * before the referenced one, since the deletion routines might do
Bruce Momjian's avatar
Bruce Momjian committed
483 484
	 * things like try to update the pg_class record when deleting a check
	 * constraint.
485 486 487 488 489
	 *
	 * Again, when dropping a whole object (subId = 0), find pg_depend
	 * records for its sub-objects too.
	 *
	 * NOTE: because we are using SnapshotNow, if a recursive call deletes
Bruce Momjian's avatar
Bruce Momjian committed
490 491 492 493
	 * any pg_depend tuples that our scan hasn't yet visited, we will not
	 * see them as good when we do visit them.	This is essential for
	 * correct behavior if there are multiple dependency paths between two
	 * objects --- else we might try to delete an already-deleted object.
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
	 */
	ScanKeyEntryInitialize(&key[0], 0x0,
						   Anum_pg_depend_refclassid, F_OIDEQ,
						   ObjectIdGetDatum(object->classId));
	ScanKeyEntryInitialize(&key[1], 0x0,
						   Anum_pg_depend_refobjid, F_OIDEQ,
						   ObjectIdGetDatum(object->objectId));
	if (object->objectSubId != 0)
	{
		ScanKeyEntryInitialize(&key[2], 0x0,
							   Anum_pg_depend_refobjsubid, F_INT4EQ,
							   Int32GetDatum(object->objectSubId));
		nkeys = 3;
	}
	else
		nkeys = 2;

	scan = systable_beginscan(depRel, DependReferenceIndex, true,
							  SnapshotNow, nkeys, key);

	while (HeapTupleIsValid(tup = systable_getnext(scan)))
	{
Bruce Momjian's avatar
Bruce Momjian committed
516
		Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
517 518 519 520 521 522 523 524

		otherObject.classId = foundDep->classid;
		otherObject.objectId = foundDep->objid;
		otherObject.objectSubId = foundDep->objsubid;

		switch (foundDep->deptype)
		{
			case DEPENDENCY_NORMAL:
525 526 527 528 529 530 531 532 533 534
				/*
				 * Perhaps there was another dependency path that would
				 * have allowed silent deletion of the otherObject, had
				 * we only taken that path first.
				 * In that case, act like this link is AUTO, too.
				 */
				if (object_address_present(&otherObject, oktodelete))
					elog(DEBUG1, "Drop auto-cascades to %s",
						 getObjectDescription(&otherObject));
				else if (behavior == DROP_RESTRICT)
535
				{
536 537 538 539
					elog(NOTICE, "%s depends on %s",
						 getObjectDescription(&otherObject),
						 objDescription);
					ok = false;
540 541 542 543 544
				}
				else
					elog(NOTICE, "Drop cascades to %s",
						 getObjectDescription(&otherObject));

545 546 547 548
				if (!recursiveDeletion(&otherObject, behavior,
									   object,
									   oktodelete, depRel))
					ok = false;
549 550 551
				break;
			case DEPENDENCY_AUTO:
			case DEPENDENCY_INTERNAL:
Bruce Momjian's avatar
Bruce Momjian committed
552

553 554 555 556 557
				/*
				 * We propagate the DROP without complaint even in the
				 * RESTRICT case.  (However, normal dependencies on the
				 * component object could still cause failure.)
				 */
558
				elog(DEBUG1, "Drop auto-cascades to %s",
559 560 561
					 getObjectDescription(&otherObject));

				if (!recursiveDeletion(&otherObject, behavior,
562
									   object,
563
									   oktodelete, depRel))
564 565 566
					ok = false;
				break;
			case DEPENDENCY_PIN:
Bruce Momjian's avatar
Bruce Momjian committed
567

568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
				/*
				 * For a PIN dependency we just elog immediately; there
				 * won't be any others to report.
				 */
				elog(ERROR, "Cannot drop %s because it is required by the database system",
					 objDescription);
				break;
			default:
				elog(ERROR, "recursiveDeletion: unknown dependency type '%c' for %s",
					 foundDep->deptype, objDescription);
				break;
		}
	}

	systable_endscan(scan);

	/*
	 * We do not need CommandCounterIncrement here, since if step 2 did
	 * anything then each recursive call will have ended with one.
	 */

	/*
	 * Step 3: delete the object itself.
	 */
	doDeletion(object);

	/*
Bruce Momjian's avatar
Bruce Momjian committed
595 596 597
	 * Delete any comments associated with this object.  (This is a
	 * convenient place to do it instead of having every object type know
	 * to do it.)
598 599 600 601
	 */
	DeleteComments(object->objectId, object->classId, object->objectSubId);

	/*
Bruce Momjian's avatar
Bruce Momjian committed
602 603
	 * CommandCounterIncrement here to ensure that preceding changes are
	 * all visible.
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
	 */
	CommandCounterIncrement();

	/*
	 * And we're done!
	 */
	pfree(objDescription);

	return ok;
}


/*
 * doDeletion: actually delete a single object
 */
static void
doDeletion(const ObjectAddress *object)
{
	switch (getObjectClass(object))
	{
		case OCLASS_CLASS:
			{
626
				char		relKind = get_rel_relkind(object->objectId);
Bruce Momjian's avatar
Bruce Momjian committed
627 628 629 630 631 632

				if (relKind == RELKIND_INDEX)
				{
					Assert(object->objectSubId == 0);
					index_drop(object->objectId);
				}
633
				else
Bruce Momjian's avatar
Bruce Momjian committed
634 635 636 637 638 639 640 641
				{
					if (object->objectSubId != 0)
						RemoveAttributeById(object->objectId,
											object->objectSubId);
					else
						heap_drop_with_catalog(object->objectId);
				}
				break;
642 643 644 645 646 647 648 649 650 651
			}

		case OCLASS_PROC:
			RemoveFunctionById(object->objectId);
			break;

		case OCLASS_TYPE:
			RemoveTypeById(object->objectId);
			break;

652 653 654 655
		case OCLASS_CAST:
			DropCastById(object->objectId);
			break;

656 657 658 659
		case OCLASS_CONSTRAINT:
			RemoveConstraintById(object->objectId);
			break;

Tatsuo Ishii's avatar
Tatsuo Ishii committed
660 661 662 663
		case OCLASS_CONVERSION:
			RemoveConversionById(object->objectId);
			break;

664 665 666 667
		case OCLASS_DEFAULT:
			RemoveAttrDefaultById(object->objectId);
			break;

668 669 670 671 672 673 674 675
		case OCLASS_LANGUAGE:
			DropProceduralLanguageById(object->objectId);
			break;

		case OCLASS_OPERATOR:
			RemoveOperatorById(object->objectId);
			break;

676 677 678 679
		case OCLASS_OPCLASS:
			RemoveOpClassById(object->objectId);
			break;

680 681 682 683 684 685 686 687
		case OCLASS_REWRITE:
			RemoveRewriteRuleById(object->objectId);
			break;

		case OCLASS_TRIGGER:
			RemoveTriggerById(object->objectId);
			break;

688 689 690 691
		case OCLASS_SCHEMA:
			RemoveSchemaById(object->objectId);
			break;

692 693 694 695 696 697
		default:
			elog(ERROR, "doDeletion: Unsupported object class %u",
				 object->classId);
	}
}

698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
/*
 * recordDependencyOnExpr - find expression dependencies
 *
 * This is used to find the dependencies of rules, constraint expressions,
 * etc.
 *
 * Given an expression or query in node-tree form, find all the objects
 * it refers to (tables, columns, operators, functions, etc).  Record
 * a dependency of the specified type from the given depender object
 * to each object mentioned in the expression.
 *
 * rtable is the rangetable to be used to interpret Vars with varlevelsup=0.
 * It can be NIL if no such variables are expected.
 *
 * XXX is it important to create dependencies on the datatypes mentioned in
Bruce Momjian's avatar
Bruce Momjian committed
713
 * the expression?	In most cases this would be redundant (eg, a ref to an
714 715 716 717 718 719 720 721
 * operator indirectly references its input and output datatypes), but I'm
 * not quite convinced there are no cases where we need it.
 */
void
recordDependencyOnExpr(const ObjectAddress *depender,
					   Node *expr, List *rtable,
					   DependencyType behavior)
{
Bruce Momjian's avatar
Bruce Momjian committed
722
	find_expr_references_context context;
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744

	init_object_addresses(&context.addrs);

	/* Set up interpretation for Vars at varlevelsup = 0 */
	context.rtables = makeList1(rtable);

	/* Scan the expression tree for referenceable objects */
	find_expr_references_walker(expr, &context);

	/* Remove any duplicates */
	eliminate_duplicate_dependencies(&context.addrs);

	/* And record 'em */
	recordMultipleDependencies(depender,
							   context.addrs.refs, context.addrs.numrefs,
							   behavior);

	term_object_addresses(&context.addrs);
}

/*
 * Recursively search an expression tree for object references.
745 746 747 748 749 750
 *
 * Note: we avoid creating references to columns of tables that participate
 * in an SQL JOIN construct, but are not actually used anywhere in the query.
 * To do so, we do not scan the joinaliasvars list of a join RTE while
 * scanning the query rangetable, but instead scan each individual entry
 * of the alias list when we find a reference to it.
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
 */
static bool
find_expr_references_walker(Node *node,
							find_expr_references_context *context)
{
	if (node == NULL)
		return false;
	if (IsA(node, Var))
	{
		Var		   *var = (Var *) node;
		int			levelsup;
		List	   *rtable,
				   *rtables;
		RangeTblEntry *rte;

		/* Find matching rtable entry, or complain if not found */
		levelsup = var->varlevelsup;
		rtables = context->rtables;
		while (levelsup--)
		{
			if (rtables == NIL)
				break;
			rtables = lnext(rtables);
		}
		if (rtables == NIL)
			elog(ERROR, "find_expr_references_walker: bogus varlevelsup %d",
				 var->varlevelsup);
		rtable = lfirst(rtables);
		if (var->varno <= 0 || var->varno > length(rtable))
			elog(ERROR, "find_expr_references_walker: bogus varno %d",
				 var->varno);
		rte = rt_fetch(var->varno, rtable);
		if (rte->rtekind == RTE_RELATION)
784 785 786
		{
			/* If it's a plain relation, reference this column */
			/* NB: this code works for whole-row Var with attno 0, too */
787 788
			add_object_address(OCLASS_CLASS, rte->relid, var->varattno,
							   &context->addrs);
789 790 791 792
		}
		else if (rte->rtekind == RTE_JOIN)
		{
			/* Scan join output column to add references to join inputs */
793 794 795 796 797
			List   *save_rtables;

			/* We must make the context appropriate for join's level */
			save_rtables = context->rtables;
			context->rtables = rtables;
798 799 800 801 802 803 804
			if (var->varattno <= 0 ||
				var->varattno > length(rte->joinaliasvars))
				elog(ERROR, "find_expr_references_walker: bogus varattno %d",
					 var->varattno);
			find_expr_references_walker((Node *) nth(var->varattno - 1,
													 rte->joinaliasvars),
										context);
805
			context->rtables = save_rtables;
806
		}
807 808 809 810 811 812
		return false;
	}
	if (IsA(node, Expr))
	{
		Expr	   *expr = (Expr *) node;

813 814
		if (expr->opType == OP_EXPR ||
			expr->opType == DISTINCT_EXPR)
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
		{
			Oper	   *oper = (Oper *) expr->oper;

			add_object_address(OCLASS_OPERATOR, oper->opno, 0,
							   &context->addrs);
		}
		else if (expr->opType == FUNC_EXPR)
		{
			Func	   *func = (Func *) expr->oper;

			add_object_address(OCLASS_PROC, func->funcid, 0,
							   &context->addrs);
		}
		/* fall through to examine arguments */
	}
	if (IsA(node, Aggref))
	{
		Aggref	   *aggref = (Aggref *) node;

		add_object_address(OCLASS_PROC, aggref->aggfnoid, 0,
						   &context->addrs);
		/* fall through to examine arguments */
	}
	if (is_subplan(node))
	{
		/* Extra work needed here if we ever need this case */
		elog(ERROR, "find_expr_references_walker: already-planned subqueries not supported");
	}
	if (IsA(node, Query))
	{
		/* Recurse into RTE subquery or not-yet-planned sublink subquery */
		Query	   *query = (Query *) node;
		List	   *rtable;
		bool		result;

		/*
Bruce Momjian's avatar
Bruce Momjian committed
851 852
		 * Add whole-relation refs for each plain relation mentioned in
		 * the subquery's rtable.  (Note: query_tree_walker takes care of
853
		 * recursing into RTE_FUNCTION and RTE_SUBQUERY RTEs, so no need
854
		 * to do that here.  But keep it from looking at join alias lists.)
855 856 857 858 859 860 861 862 863 864 865 866 867 868
		 */
		foreach(rtable, query->rtable)
		{
			RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtable);

			if (rte->rtekind == RTE_RELATION)
				add_object_address(OCLASS_CLASS, rte->relid, 0,
								   &context->addrs);
		}

		/* Examine substructure of query */
		context->rtables = lcons(query->rtable, context->rtables);
		result = query_tree_walker(query,
								   find_expr_references_walker,
869 870
								   (void *) context,
								   QTW_IGNORE_JOINALIASES);
871 872 873 874 875 876 877 878 879 880 881 882 883
		context->rtables = lnext(context->rtables);
		return result;
	}
	return expression_tree_walker(node, find_expr_references_walker,
								  (void *) context);
}

/*
 * Given an array of dependency references, eliminate any duplicates.
 */
static void
eliminate_duplicate_dependencies(ObjectAddresses *addrs)
{
Bruce Momjian's avatar
Bruce Momjian committed
884
	ObjectAddress *priorobj;
885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
	int			oldref,
				newrefs;

	if (addrs->numrefs <= 1)
		return;					/* nothing to do */

	/* Sort the refs so that duplicates are adjacent */
	qsort((void *) addrs->refs, addrs->numrefs, sizeof(ObjectAddress),
		  object_address_comparator);

	/* Remove dups */
	priorobj = addrs->refs;
	newrefs = 1;
	for (oldref = 1; oldref < addrs->numrefs; oldref++)
	{
Bruce Momjian's avatar
Bruce Momjian committed
900
		ObjectAddress *thisobj = addrs->refs + oldref;
901 902 903 904 905 906

		if (priorobj->classId == thisobj->classId &&
			priorobj->objectId == thisobj->objectId)
		{
			if (priorobj->objectSubId == thisobj->objectSubId)
				continue;		/* identical, so drop thisobj */
Bruce Momjian's avatar
Bruce Momjian committed
907

908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949
			/*
			 * If we have a whole-object reference and a reference to a
			 * part of the same object, we don't need the whole-object
			 * reference (for example, we don't need to reference both
			 * table foo and column foo.bar).  The whole-object reference
			 * will always appear first in the sorted list.
			 */
			if (priorobj->objectSubId == 0)
			{
				/* replace whole ref with partial */
				priorobj->objectSubId = thisobj->objectSubId;
				continue;
			}
		}
		/* Not identical, so add thisobj to output set */
		priorobj++;
		priorobj->classId = thisobj->classId;
		priorobj->objectId = thisobj->objectId;
		priorobj->objectSubId = thisobj->objectSubId;
		newrefs++;
	}

	addrs->numrefs = newrefs;
}

/*
 * qsort comparator for ObjectAddress items
 */
static int
object_address_comparator(const void *a, const void *b)
{
	const ObjectAddress *obja = (const ObjectAddress *) a;
	const ObjectAddress *objb = (const ObjectAddress *) b;

	if (obja->classId < objb->classId)
		return -1;
	if (obja->classId > objb->classId)
		return 1;
	if (obja->objectId < objb->objectId)
		return -1;
	if (obja->objectId > objb->objectId)
		return 1;
Bruce Momjian's avatar
Bruce Momjian committed
950

951
	/*
Bruce Momjian's avatar
Bruce Momjian committed
952 953
	 * We sort the subId as an unsigned int so that 0 will come first. See
	 * logic in eliminate_duplicate_dependencies.
954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
	 */
	if ((unsigned int) obja->objectSubId < (unsigned int) objb->objectSubId)
		return -1;
	if ((unsigned int) obja->objectSubId > (unsigned int) objb->objectSubId)
		return 1;
	return 0;
}

/*
 * Routines for handling an expansible array of ObjectAddress items.
 *
 * init_object_addresses: initialize an ObjectAddresses array.
 */
static void
init_object_addresses(ObjectAddresses *addrs)
{
	/* Initialize array to empty */
	addrs->numrefs = 0;
	addrs->maxrefs = 32;		/* arbitrary initial array size */
	addrs->refs = (ObjectAddress *)
		palloc(addrs->maxrefs * sizeof(ObjectAddress));

	/* Initialize object_classes[] if not done yet */
	/* This will be needed by add_object_address() */
	if (!object_classes_initialized)
		init_object_classes();
}

/*
 * Add an entry to an ObjectAddresses array.
 *
 * It is convenient to specify the class by ObjectClass rather than directly
 * by catalog OID.
 */
static void
add_object_address(ObjectClasses oclass, Oid objectId, int32 subId,
				   ObjectAddresses *addrs)
{
Bruce Momjian's avatar
Bruce Momjian committed
992
	ObjectAddress *item;
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017

	/* enlarge array if needed */
	if (addrs->numrefs >= addrs->maxrefs)
	{
		addrs->maxrefs *= 2;
		addrs->refs = (ObjectAddress *)
			repalloc(addrs->refs, addrs->maxrefs * sizeof(ObjectAddress));
	}
	/* record this item */
	item = addrs->refs + addrs->numrefs;
	item->classId = object_classes[oclass];
	item->objectId = objectId;
	item->objectSubId = subId;
	addrs->numrefs++;
}

/*
 * Add an entry to an ObjectAddresses array.
 *
 * As above, but specify entry exactly.
 */
static void
add_exact_object_address(const ObjectAddress *object,
						 ObjectAddresses *addrs)
{
Bruce Momjian's avatar
Bruce Momjian committed
1018
	ObjectAddress *item;
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033

	/* enlarge array if needed */
	if (addrs->numrefs >= addrs->maxrefs)
	{
		addrs->maxrefs *= 2;
		addrs->refs = (ObjectAddress *)
			repalloc(addrs->refs, addrs->maxrefs * sizeof(ObjectAddress));
	}
	/* record this item */
	item = addrs->refs + addrs->numrefs;
	*item = *object;
	addrs->numrefs++;
}

/*
1034 1035 1036
 * Test whether an object is present in an ObjectAddresses array.
 *
 * We return "true" if object is a subobject of something in the array, too.
1037
 */
1038 1039 1040
static bool
object_address_present(const ObjectAddress *object,
					   ObjectAddresses *addrs)
1041
{
1042 1043 1044
	int			i;

	for (i = addrs->numrefs - 1; i >= 0; i--)
1045
	{
1046
		ObjectAddress *thisobj = addrs->refs + i;
1047

1048 1049
		if (object->classId == thisobj->classId &&
			object->objectId == thisobj->objectId)
1050
		{
1051 1052 1053
			if (object->objectSubId == thisobj->objectSubId ||
				thisobj->objectSubId == 0)
				return true;
1054 1055 1056
		}
	}

1057
	return false;
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
}

/*
 * Clean up when done with an ObjectAddresses array.
 */
static void
term_object_addresses(ObjectAddresses *addrs)
{
	pfree(addrs->refs);
}

/*
 * Initialize the object_classes[] table.
 *
 * Although some of these OIDs aren't compile-time constants, they surely
 * shouldn't change during a backend's run.  So, we look them up the
 * first time through and then cache them.
 */
static void
init_object_classes(void)
{
	object_classes[OCLASS_CLASS] = RelOid_pg_class;
	object_classes[OCLASS_PROC] = RelOid_pg_proc;
	object_classes[OCLASS_TYPE] = RelOid_pg_type;
1082
	object_classes[OCLASS_CAST] = get_system_catalog_relid(CastRelationName);
1083
	object_classes[OCLASS_CONSTRAINT] = get_system_catalog_relid(ConstraintRelationName);
Tatsuo Ishii's avatar
Tatsuo Ishii committed
1084
	object_classes[OCLASS_CONVERSION] = get_system_catalog_relid(ConversionRelationName);
1085 1086 1087
	object_classes[OCLASS_DEFAULT] = get_system_catalog_relid(AttrDefaultRelationName);
	object_classes[OCLASS_LANGUAGE] = get_system_catalog_relid(LanguageRelationName);
	object_classes[OCLASS_OPERATOR] = get_system_catalog_relid(OperatorRelationName);
1088
	object_classes[OCLASS_OPCLASS] = get_system_catalog_relid(OperatorClassRelationName);
1089 1090
	object_classes[OCLASS_REWRITE] = get_system_catalog_relid(RewriteRelationName);
	object_classes[OCLASS_TRIGGER] = get_system_catalog_relid(TriggerRelationName);
1091
	object_classes[OCLASS_SCHEMA] = get_system_catalog_relid(NamespaceRelationName);
1092 1093 1094
	object_classes_initialized = true;
}

1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
/*
 * Determine the class of a given object identified by objectAddress.
 *
 * This function is needed just because some of the system catalogs do
 * not have hardwired-at-compile-time OIDs.
 */
static ObjectClasses
getObjectClass(const ObjectAddress *object)
{
	/* Easy for the bootstrapped catalogs... */
	switch (object->classId)
	{
		case RelOid_pg_class:
			/* caller must check objectSubId */
			return OCLASS_CLASS;

		case RelOid_pg_proc:
			Assert(object->objectSubId == 0);
			return OCLASS_PROC;

		case RelOid_pg_type:
			Assert(object->objectSubId == 0);
			return OCLASS_TYPE;
	}

	/*
	 * Handle cases where catalog's OID is not hardwired.
	 */
1123 1124
	if (!object_classes_initialized)
		init_object_classes();
1125

1126 1127 1128 1129 1130
	if (object->classId == object_classes[OCLASS_CAST])
	{
		Assert(object->objectSubId == 0);
		return OCLASS_CAST;
	}
1131
	if (object->classId == object_classes[OCLASS_CONSTRAINT])
1132 1133 1134 1135
	{
		Assert(object->objectSubId == 0);
		return OCLASS_CONSTRAINT;
	}
Tatsuo Ishii's avatar
Tatsuo Ishii committed
1136 1137 1138 1139 1140
	if (object->classId == object_classes[OCLASS_CONVERSION])
	{
		Assert(object->objectSubId == 0);
		return OCLASS_CONVERSION;
	}
1141
	if (object->classId == object_classes[OCLASS_DEFAULT])
1142 1143 1144 1145
	{
		Assert(object->objectSubId == 0);
		return OCLASS_DEFAULT;
	}
1146
	if (object->classId == object_classes[OCLASS_LANGUAGE])
1147 1148 1149 1150
	{
		Assert(object->objectSubId == 0);
		return OCLASS_LANGUAGE;
	}
1151
	if (object->classId == object_classes[OCLASS_OPERATOR])
1152 1153 1154 1155
	{
		Assert(object->objectSubId == 0);
		return OCLASS_OPERATOR;
	}
1156 1157 1158 1159 1160
	if (object->classId == object_classes[OCLASS_OPCLASS])
	{
		Assert(object->objectSubId == 0);
		return OCLASS_OPCLASS;
	}
1161
	if (object->classId == object_classes[OCLASS_REWRITE])
1162 1163 1164 1165
	{
		Assert(object->objectSubId == 0);
		return OCLASS_REWRITE;
	}
1166
	if (object->classId == object_classes[OCLASS_TRIGGER])
1167 1168 1169 1170
	{
		Assert(object->objectSubId == 0);
		return OCLASS_TRIGGER;
	}
1171 1172 1173 1174 1175
	if (object->classId == object_classes[OCLASS_SCHEMA])
	{
		Assert(object->objectSubId == 0);
		return OCLASS_SCHEMA;
	}
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196

	elog(ERROR, "getObjectClass: Unknown object class %u",
		 object->classId);
	return OCLASS_CLASS;		/* keep compiler quiet */
}

/*
 * getObjectDescription: build an object description for messages
 *
 * The result is a palloc'd string.
 */
static char *
getObjectDescription(const ObjectAddress *object)
{
	StringInfoData buffer;

	initStringInfo(&buffer);

	switch (getObjectClass(object))
	{
		case OCLASS_CLASS:
1197
			getRelationDescription(&buffer, object->objectId);
1198 1199 1200 1201 1202 1203 1204 1205
			if (object->objectSubId != 0)
				appendStringInfo(&buffer, " column %s",
								 get_attname(object->objectId,
											 object->objectSubId));
			break;

		case OCLASS_PROC:
			appendStringInfo(&buffer, "function %s",
1206
							 format_procedure(object->objectId));
1207 1208 1209
			break;

		case OCLASS_TYPE:
1210 1211 1212 1213 1214
			appendStringInfo(&buffer, "type %s",
							 format_type_be(object->objectId));
			break;

		case OCLASS_CAST:
Bruce Momjian's avatar
Bruce Momjian committed
1215 1216 1217 1218 1219 1220
			{
				Relation	castDesc;
				ScanKeyData skey[1];
				SysScanDesc rcscan;
				HeapTuple	tup;
				Form_pg_cast castForm;
1221

Bruce Momjian's avatar
Bruce Momjian committed
1222
				castDesc = heap_openr(CastRelationName, AccessShareLock);
1223

Bruce Momjian's avatar
Bruce Momjian committed
1224 1225 1226
				ScanKeyEntryInitialize(&skey[0], 0x0,
									   ObjectIdAttributeNumber, F_OIDEQ,
									 ObjectIdGetDatum(object->objectId));
1227

Bruce Momjian's avatar
Bruce Momjian committed
1228 1229
				rcscan = systable_beginscan(castDesc, CastOidIndex, true,
											SnapshotNow, 1, skey);
1230

Bruce Momjian's avatar
Bruce Momjian committed
1231
				tup = systable_getnext(rcscan);
1232

Bruce Momjian's avatar
Bruce Momjian committed
1233 1234 1235
				if (!HeapTupleIsValid(tup))
					elog(ERROR, "getObjectDescription: Cast %u does not exist",
						 object->objectId);
1236

Bruce Momjian's avatar
Bruce Momjian committed
1237
				castForm = (Form_pg_cast) GETSTRUCT(tup);
1238

Bruce Momjian's avatar
Bruce Momjian committed
1239 1240 1241
				appendStringInfo(&buffer, "cast from %s to %s",
								 format_type_be(castForm->castsource),
								 format_type_be(castForm->casttarget));
1242

Bruce Momjian's avatar
Bruce Momjian committed
1243 1244 1245 1246
				systable_endscan(rcscan);
				heap_close(castDesc, AccessShareLock);
				break;
			}
1247 1248

		case OCLASS_CONSTRAINT:
Bruce Momjian's avatar
Bruce Momjian committed
1249 1250 1251 1252 1253 1254
			{
				Relation	conDesc;
				ScanKeyData skey[1];
				SysScanDesc rcscan;
				HeapTuple	tup;
				Form_pg_constraint con;
1255

Bruce Momjian's avatar
Bruce Momjian committed
1256
				conDesc = heap_openr(ConstraintRelationName, AccessShareLock);
1257

Bruce Momjian's avatar
Bruce Momjian committed
1258 1259 1260
				ScanKeyEntryInitialize(&skey[0], 0x0,
									   ObjectIdAttributeNumber, F_OIDEQ,
									 ObjectIdGetDatum(object->objectId));
1261

Bruce Momjian's avatar
Bruce Momjian committed
1262 1263
				rcscan = systable_beginscan(conDesc, ConstraintOidIndex, true,
											SnapshotNow, 1, skey);
1264

Bruce Momjian's avatar
Bruce Momjian committed
1265
				tup = systable_getnext(rcscan);
1266

Bruce Momjian's avatar
Bruce Momjian committed
1267 1268 1269
				if (!HeapTupleIsValid(tup))
					elog(ERROR, "getObjectDescription: Constraint %u does not exist",
						 object->objectId);
1270

Bruce Momjian's avatar
Bruce Momjian committed
1271
				con = (Form_pg_constraint) GETSTRUCT(tup);
1272

Bruce Momjian's avatar
Bruce Momjian committed
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283
				if (OidIsValid(con->conrelid))
				{
					appendStringInfo(&buffer, "constraint %s on ",
									 NameStr(con->conname));
					getRelationDescription(&buffer, con->conrelid);
				}
				else
				{
					appendStringInfo(&buffer, "constraint %s",
									 NameStr(con->conname));
				}
1284

Bruce Momjian's avatar
Bruce Momjian committed
1285 1286 1287 1288
				systable_endscan(rcscan);
				heap_close(conDesc, AccessShareLock);
				break;
			}
1289

Tatsuo Ishii's avatar
Tatsuo Ishii committed
1290
		case OCLASS_CONVERSION:
Bruce Momjian's avatar
Bruce Momjian committed
1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304
			{
				HeapTuple	conTup;

				conTup = SearchSysCache(CONOID,
									  ObjectIdGetDatum(object->objectId),
										0, 0, 0);
				if (!HeapTupleIsValid(conTup))
					elog(ERROR, "getObjectDescription: Conversion %u does not exist",
						 object->objectId);
				appendStringInfo(&buffer, "conversion %s",
								 NameStr(((Form_pg_conversion) GETSTRUCT(conTup))->conname));
				ReleaseSysCache(conTup);
				break;
			}
Tatsuo Ishii's avatar
Tatsuo Ishii committed
1305

1306
		case OCLASS_DEFAULT:
Bruce Momjian's avatar
Bruce Momjian committed
1307 1308 1309 1310 1311 1312 1313
			{
				Relation	attrdefDesc;
				ScanKeyData skey[1];
				SysScanDesc adscan;
				HeapTuple	tup;
				Form_pg_attrdef attrdef;
				ObjectAddress colobject;
1314

Bruce Momjian's avatar
Bruce Momjian committed
1315
				attrdefDesc = heap_openr(AttrDefaultRelationName, AccessShareLock);
1316

Bruce Momjian's avatar
Bruce Momjian committed
1317 1318 1319
				ScanKeyEntryInitialize(&skey[0], 0x0,
									   ObjectIdAttributeNumber, F_OIDEQ,
									 ObjectIdGetDatum(object->objectId));
1320

Bruce Momjian's avatar
Bruce Momjian committed
1321 1322
				adscan = systable_beginscan(attrdefDesc, AttrDefaultOidIndex, true,
											SnapshotNow, 1, skey);
1323

Bruce Momjian's avatar
Bruce Momjian committed
1324
				tup = systable_getnext(adscan);
1325

Bruce Momjian's avatar
Bruce Momjian committed
1326 1327 1328
				if (!HeapTupleIsValid(tup))
					elog(ERROR, "getObjectDescription: Default %u does not exist",
						 object->objectId);
1329

Bruce Momjian's avatar
Bruce Momjian committed
1330
				attrdef = (Form_pg_attrdef) GETSTRUCT(tup);
1331

Bruce Momjian's avatar
Bruce Momjian committed
1332 1333 1334
				colobject.classId = RelOid_pg_class;
				colobject.objectId = attrdef->adrelid;
				colobject.objectSubId = attrdef->adnum;
1335

Bruce Momjian's avatar
Bruce Momjian committed
1336 1337
				appendStringInfo(&buffer, "default for %s",
								 getObjectDescription(&colobject));
1338

Bruce Momjian's avatar
Bruce Momjian committed
1339 1340 1341 1342
				systable_endscan(adscan);
				heap_close(attrdefDesc, AccessShareLock);
				break;
			}
1343

1344
		case OCLASS_LANGUAGE:
Bruce Momjian's avatar
Bruce Momjian committed
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358
			{
				HeapTuple	langTup;

				langTup = SearchSysCache(LANGOID,
									  ObjectIdGetDatum(object->objectId),
										 0, 0, 0);
				if (!HeapTupleIsValid(langTup))
					elog(ERROR, "getObjectDescription: Language %u does not exist",
						 object->objectId);
				appendStringInfo(&buffer, "language %s",
								 NameStr(((Form_pg_language) GETSTRUCT(langTup))->lanname));
				ReleaseSysCache(langTup);
				break;
			}
1359 1360 1361

		case OCLASS_OPERATOR:
			appendStringInfo(&buffer, "operator %s",
1362 1363 1364 1365
							 format_operator(object->objectId));
			break;

		case OCLASS_OPCLASS:
Bruce Momjian's avatar
Bruce Momjian committed
1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405
			{
				HeapTuple	opcTup;
				Form_pg_opclass opcForm;
				HeapTuple	amTup;
				Form_pg_am	amForm;
				char	   *nspname;

				opcTup = SearchSysCache(CLAOID,
									  ObjectIdGetDatum(object->objectId),
										0, 0, 0);
				if (!HeapTupleIsValid(opcTup))
					elog(ERROR, "cache lookup of opclass %u failed",
						 object->objectId);
				opcForm = (Form_pg_opclass) GETSTRUCT(opcTup);

				/* Qualify the name if not visible in search path */
				if (OpclassIsVisible(object->objectId))
					nspname = NULL;
				else
					nspname = get_namespace_name(opcForm->opcnamespace);

				appendStringInfo(&buffer, "operator class %s",
								 quote_qualified_identifier(nspname,
											 NameStr(opcForm->opcname)));

				amTup = SearchSysCache(AMOID,
									   ObjectIdGetDatum(opcForm->opcamid),
									   0, 0, 0);
				if (!HeapTupleIsValid(amTup))
					elog(ERROR, "syscache lookup for AM %u failed",
						 opcForm->opcamid);
				amForm = (Form_pg_am) GETSTRUCT(amTup);

				appendStringInfo(&buffer, " for %s",
								 NameStr(amForm->amname));

				ReleaseSysCache(amTup);
				ReleaseSysCache(opcTup);
				break;
			}
1406 1407

		case OCLASS_REWRITE:
Bruce Momjian's avatar
Bruce Momjian committed
1408 1409 1410 1411 1412 1413
			{
				Relation	ruleDesc;
				ScanKeyData skey[1];
				SysScanDesc rcscan;
				HeapTuple	tup;
				Form_pg_rewrite rule;
1414

Bruce Momjian's avatar
Bruce Momjian committed
1415
				ruleDesc = heap_openr(RewriteRelationName, AccessShareLock);
1416

Bruce Momjian's avatar
Bruce Momjian committed
1417 1418 1419
				ScanKeyEntryInitialize(&skey[0], 0x0,
									   ObjectIdAttributeNumber, F_OIDEQ,
									 ObjectIdGetDatum(object->objectId));
1420

Bruce Momjian's avatar
Bruce Momjian committed
1421 1422
				rcscan = systable_beginscan(ruleDesc, RewriteOidIndex, true,
											SnapshotNow, 1, skey);
1423

Bruce Momjian's avatar
Bruce Momjian committed
1424
				tup = systable_getnext(rcscan);
1425

Bruce Momjian's avatar
Bruce Momjian committed
1426 1427 1428
				if (!HeapTupleIsValid(tup))
					elog(ERROR, "getObjectDescription: Rule %u does not exist",
						 object->objectId);
1429

Bruce Momjian's avatar
Bruce Momjian committed
1430
				rule = (Form_pg_rewrite) GETSTRUCT(tup);
1431

Bruce Momjian's avatar
Bruce Momjian committed
1432 1433 1434
				appendStringInfo(&buffer, "rule %s on ",
								 NameStr(rule->rulename));
				getRelationDescription(&buffer, rule->ev_class);
1435

Bruce Momjian's avatar
Bruce Momjian committed
1436 1437 1438 1439
				systable_endscan(rcscan);
				heap_close(ruleDesc, AccessShareLock);
				break;
			}
1440 1441

		case OCLASS_TRIGGER:
Bruce Momjian's avatar
Bruce Momjian committed
1442 1443 1444 1445 1446 1447
			{
				Relation	trigDesc;
				ScanKeyData skey[1];
				SysScanDesc tgscan;
				HeapTuple	tup;
				Form_pg_trigger trig;
1448

Bruce Momjian's avatar
Bruce Momjian committed
1449
				trigDesc = heap_openr(TriggerRelationName, AccessShareLock);
1450

Bruce Momjian's avatar
Bruce Momjian committed
1451 1452 1453
				ScanKeyEntryInitialize(&skey[0], 0x0,
									   ObjectIdAttributeNumber, F_OIDEQ,
									 ObjectIdGetDatum(object->objectId));
1454

Bruce Momjian's avatar
Bruce Momjian committed
1455 1456
				tgscan = systable_beginscan(trigDesc, TriggerOidIndex, true,
											SnapshotNow, 1, skey);
1457

Bruce Momjian's avatar
Bruce Momjian committed
1458
				tup = systable_getnext(tgscan);
1459

Bruce Momjian's avatar
Bruce Momjian committed
1460 1461 1462
				if (!HeapTupleIsValid(tup))
					elog(ERROR, "getObjectDescription: Trigger %u does not exist",
						 object->objectId);
1463

Bruce Momjian's avatar
Bruce Momjian committed
1464
				trig = (Form_pg_trigger) GETSTRUCT(tup);
1465

Bruce Momjian's avatar
Bruce Momjian committed
1466 1467 1468
				appendStringInfo(&buffer, "trigger %s on ",
								 NameStr(trig->tgname));
				getRelationDescription(&buffer, trig->tgrelid);
1469

Bruce Momjian's avatar
Bruce Momjian committed
1470 1471 1472 1473
				systable_endscan(tgscan);
				heap_close(trigDesc, AccessShareLock);
				break;
			}
1474

1475
		case OCLASS_SCHEMA:
Bruce Momjian's avatar
Bruce Momjian committed
1476 1477
			{
				char	   *nspname;
1478

Bruce Momjian's avatar
Bruce Momjian committed
1479 1480 1481 1482 1483 1484 1485
				nspname = get_namespace_name(object->objectId);
				if (!nspname)
					elog(ERROR, "getObjectDescription: Schema %u does not exist",
						 object->objectId);
				appendStringInfo(&buffer, "schema %s", nspname);
				break;
			}
1486

1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
		default:
			appendStringInfo(&buffer, "unknown object %u %u %d",
							 object->classId,
							 object->objectId,
							 object->objectSubId);
			break;
	}

	return buffer.data;
}
1497 1498 1499 1500 1501 1502 1503 1504

/*
 * subroutine for getObjectDescription: describe a relation
 */
static void
getRelationDescription(StringInfo buffer, Oid relid)
{
	HeapTuple	relTup;
Bruce Momjian's avatar
Bruce Momjian committed
1505
	Form_pg_class relForm;
1506 1507
	char	   *nspname;
	char	   *relname;
1508 1509 1510 1511 1512

	relTup = SearchSysCache(RELOID,
							ObjectIdGetDatum(relid),
							0, 0, 0);
	if (!HeapTupleIsValid(relTup))
1513
		elog(ERROR, "cache lookup of relation %u failed", relid);
1514 1515
	relForm = (Form_pg_class) GETSTRUCT(relTup);

1516 1517 1518 1519 1520 1521 1522 1523
	/* Qualify the name if not visible in search path */
	if (RelationIsVisible(relid))
		nspname = NULL;
	else
		nspname = get_namespace_name(relForm->relnamespace);

	relname = quote_qualified_identifier(nspname, NameStr(relForm->relname));

1524 1525 1526 1527
	switch (relForm->relkind)
	{
		case RELKIND_RELATION:
			appendStringInfo(buffer, "table %s",
1528
							 relname);
1529 1530 1531
			break;
		case RELKIND_INDEX:
			appendStringInfo(buffer, "index %s",
1532
							 relname);
1533 1534 1535
			break;
		case RELKIND_SPECIAL:
			appendStringInfo(buffer, "special system relation %s",
1536
							 relname);
1537 1538 1539
			break;
		case RELKIND_SEQUENCE:
			appendStringInfo(buffer, "sequence %s",
1540
							 relname);
1541 1542 1543
			break;
		case RELKIND_UNCATALOGED:
			appendStringInfo(buffer, "uncataloged table %s",
1544
							 relname);
1545 1546 1547
			break;
		case RELKIND_TOASTVALUE:
			appendStringInfo(buffer, "toast table %s",
1548
							 relname);
1549 1550 1551
			break;
		case RELKIND_VIEW:
			appendStringInfo(buffer, "view %s",
1552
							 relname);
1553
			break;
1554 1555 1556 1557
		case RELKIND_COMPOSITE_TYPE:
			appendStringInfo(buffer, "composite type %s",
							 relname);
			break;
1558 1559 1560
		default:
			/* shouldn't get here */
			appendStringInfo(buffer, "relation %s",
1561
							 relname);
1562 1563 1564 1565 1566
			break;
	}

	ReleaseSysCache(relTup);
}