コード例 #1
0
ファイル: pg_constraint.c プロジェクト: RingsC/postgres
/*
 * CloneForeignKeyConstraints
 *		Clone foreign keys from a partitioned table to a newly acquired
 *		partition.
 *
 * relationId is a partition of parentId, so we can be certain that it has the
 * same columns with the same datatypes.  The columns may be in different
 * order, though.
 *
 * The *cloned list is appended ClonedConstraint elements describing what was
 * created.
 */
void
CloneForeignKeyConstraints(Oid parentId, Oid relationId, List **cloned)
{
	Relation	pg_constraint;
	Relation	parentRel;
	Relation	rel;
	ScanKeyData	key;
	SysScanDesc	scan;
	TupleDesc	tupdesc;
	HeapTuple	tuple;
	AttrNumber *attmap;

	parentRel = heap_open(parentId, NoLock);	/* already got lock */
	/* see ATAddForeignKeyConstraint about lock level */
	rel = heap_open(relationId, AccessExclusiveLock);

	pg_constraint = heap_open(ConstraintRelationId, RowShareLock);
	tupdesc = RelationGetDescr(pg_constraint);

	/*
	 * The constraint key may differ, if the columns in the partition are
	 * different.  This map is used to convert them.
	 */
	attmap = convert_tuples_by_name_map(RelationGetDescr(rel),
										RelationGetDescr(parentRel),
										gettext_noop("could not convert row type"));

	ScanKeyInit(&key,
				Anum_pg_constraint_conrelid, BTEqualStrategyNumber,
				F_OIDEQ, ObjectIdGetDatum(parentId));
	scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
							  NULL, 1, &key);

	while ((tuple = systable_getnext(scan)) != NULL)
	{
		Form_pg_constraint	constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
		AttrNumber	conkey[INDEX_MAX_KEYS];
		AttrNumber	mapped_conkey[INDEX_MAX_KEYS];
		AttrNumber	confkey[INDEX_MAX_KEYS];
		Oid			conpfeqop[INDEX_MAX_KEYS];
		Oid			conppeqop[INDEX_MAX_KEYS];
		Oid			conffeqop[INDEX_MAX_KEYS];
		Constraint *fkconstraint;
		ClonedConstraint *newc;
		Oid			constrOid;
		ObjectAddress parentAddr,
					childAddr;
		int			nelem;
		int			i;
		ArrayType  *arr;
		Datum		datum;
		bool		isnull;

		/* only foreign keys */
		if (constrForm->contype != CONSTRAINT_FOREIGN)
			continue;

		ObjectAddressSet(parentAddr, ConstraintRelationId,
						 HeapTupleGetOid(tuple));

		datum = fastgetattr(tuple, Anum_pg_constraint_conkey,
							tupdesc, &isnull);
		if (isnull)
			elog(ERROR, "null conkey");
		arr = DatumGetArrayTypeP(datum);
		nelem = ARR_DIMS(arr)[0];
		if (ARR_NDIM(arr) != 1 ||
			nelem < 1 ||
			nelem > INDEX_MAX_KEYS ||
			ARR_HASNULL(arr) ||
			ARR_ELEMTYPE(arr) != INT2OID)
			elog(ERROR, "conkey is not a 1-D smallint array");
		memcpy(conkey, ARR_DATA_PTR(arr), nelem * sizeof(AttrNumber));

		for (i = 0; i < nelem; i++)
			mapped_conkey[i] = attmap[conkey[i] - 1];

		datum = fastgetattr(tuple, Anum_pg_constraint_confkey,
							tupdesc, &isnull);
		if (isnull)
			elog(ERROR, "null confkey");
		arr = DatumGetArrayTypeP(datum);
		nelem = ARR_DIMS(arr)[0];
		if (ARR_NDIM(arr) != 1 ||
			nelem < 1 ||
			nelem > INDEX_MAX_KEYS ||
			ARR_HASNULL(arr) ||
			ARR_ELEMTYPE(arr) != INT2OID)
			elog(ERROR, "confkey is not a 1-D smallint array");
		memcpy(confkey, ARR_DATA_PTR(arr), nelem * sizeof(AttrNumber));

		datum = fastgetattr(tuple, Anum_pg_constraint_conpfeqop,
							tupdesc, &isnull);
		if (isnull)
			elog(ERROR, "null conpfeqop");
		arr = DatumGetArrayTypeP(datum);
		nelem = ARR_DIMS(arr)[0];
		if (ARR_NDIM(arr) != 1 ||
			nelem < 1 ||
			nelem > INDEX_MAX_KEYS ||
			ARR_HASNULL(arr) ||
			ARR_ELEMTYPE(arr) != OIDOID)
			elog(ERROR, "conpfeqop is not a 1-D OID array");
		memcpy(conpfeqop, ARR_DATA_PTR(arr), nelem * sizeof(Oid));

		datum = fastgetattr(tuple, Anum_pg_constraint_conpfeqop,
							tupdesc, &isnull);
		if (isnull)
			elog(ERROR, "null conpfeqop");
		arr = DatumGetArrayTypeP(datum);
		nelem = ARR_DIMS(arr)[0];
		if (ARR_NDIM(arr) != 1 ||
			nelem < 1 ||
			nelem > INDEX_MAX_KEYS ||
			ARR_HASNULL(arr) ||
			ARR_ELEMTYPE(arr) != OIDOID)
			elog(ERROR, "conpfeqop is not a 1-D OID array");
		memcpy(conpfeqop, ARR_DATA_PTR(arr), nelem * sizeof(Oid));

		datum = fastgetattr(tuple, Anum_pg_constraint_conppeqop,
							tupdesc, &isnull);
		if (isnull)
			elog(ERROR, "null conppeqop");
		arr = DatumGetArrayTypeP(datum);
		nelem = ARR_DIMS(arr)[0];
		if (ARR_NDIM(arr) != 1 ||
			nelem < 1 ||
			nelem > INDEX_MAX_KEYS ||
			ARR_HASNULL(arr) ||
			ARR_ELEMTYPE(arr) != OIDOID)
			elog(ERROR, "conppeqop is not a 1-D OID array");
		memcpy(conppeqop, ARR_DATA_PTR(arr), nelem * sizeof(Oid));

		datum = fastgetattr(tuple, Anum_pg_constraint_conffeqop,
							tupdesc, &isnull);
		if (isnull)
			elog(ERROR, "null conffeqop");
		arr = DatumGetArrayTypeP(datum);
		nelem = ARR_DIMS(arr)[0];
		if (ARR_NDIM(arr) != 1 ||
			nelem < 1 ||
			nelem > INDEX_MAX_KEYS ||
			ARR_HASNULL(arr) ||
			ARR_ELEMTYPE(arr) != OIDOID)
			elog(ERROR, "conffeqop is not a 1-D OID array");
		memcpy(conffeqop, ARR_DATA_PTR(arr), nelem * sizeof(Oid));

		constrOid =
			CreateConstraintEntry(NameStr(constrForm->conname),
								  constrForm->connamespace,
								  CONSTRAINT_FOREIGN,
								  constrForm->condeferrable,
								  constrForm->condeferred,
								  constrForm->convalidated,
								  HeapTupleGetOid(tuple),
								  relationId,
								  mapped_conkey,
								  nelem,
								  nelem,
								  InvalidOid,	/* not a domain constraint */
								  constrForm->conindid,	/* same index */
								  constrForm->confrelid, /* same foreign rel */
								  confkey,
								  conpfeqop,
								  conppeqop,
								  conffeqop,
								  nelem,
								  constrForm->confupdtype,
								  constrForm->confdeltype,
								  constrForm->confmatchtype,
								  NULL,
								  NULL,
								  NULL,
								  NULL,
								  false,
								  1, false, true);

		ObjectAddressSet(childAddr, ConstraintRelationId, constrOid);
		recordDependencyOn(&childAddr, &parentAddr, DEPENDENCY_INTERNAL_AUTO);

		fkconstraint = makeNode(Constraint);
		/* for now this is all we need */
		fkconstraint->fk_upd_action = constrForm->confupdtype;
		fkconstraint->fk_del_action = constrForm->confdeltype;
		fkconstraint->deferrable = constrForm->condeferrable;
		fkconstraint->initdeferred = constrForm->condeferred;

		createForeignKeyTriggers(rel, constrForm->confrelid, fkconstraint,
								 constrOid, constrForm->conindid, false);

		if (cloned)
		{
			/*
			 * Feed back caller about the constraints we created, so that they can
			 * set up constraint verification.
			 */
			newc = palloc(sizeof(ClonedConstraint));
			newc->relid = relationId;
			newc->refrelid = constrForm->confrelid;
			newc->conindid = constrForm->conindid;
			newc->conid = constrOid;
			newc->constraint = fkconstraint;

			*cloned = lappend(*cloned, newc);
		}
	}
	systable_endscan(scan);

	pfree(attmap);

	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
	{
		PartitionDesc	partdesc = RelationGetPartitionDesc(rel);
		int			i;

		for (i = 0; i < partdesc->nparts; i++)
			CloneForeignKeyConstraints(RelationGetRelid(rel),
									   partdesc->oids[i],
									   cloned);
	}

	heap_close(rel, NoLock);		/* keep lock till commit */
	heap_close(parentRel, NoLock);
	heap_close(pg_constraint, RowShareLock);
}
コード例 #2
0
ファイル: index.c プロジェクト: sunyangkobe/cscd43
/* ----------------------------------------------------------------
 *		index_create
 *
 * Returns OID of the created index.
 * ----------------------------------------------------------------
 */
Oid
index_create(Oid heapRelationId,
			 const char *indexRelationName,
			 IndexInfo *indexInfo,
			 Oid accessMethodObjectId,
			 Oid *classObjectId,
			 bool primary,
			 bool isconstraint,
			 bool allow_system_table_mods)
{
	Relation	heapRelation;
	Relation	indexRelation;
	TupleDesc	indexTupDesc;
	bool		shared_relation;
	Oid			namespaceId;
	Oid			indexoid;
	int			i;

	/*
	 * Only SELECT ... FOR UPDATE are allowed while doing this
	 */
	heapRelation = heap_open(heapRelationId, ShareLock);

	/*
	 * The index will be in the same namespace as its parent table, and is
	 * shared across databases if and only if the parent is.
	 */
	namespaceId = RelationGetNamespace(heapRelation);
	shared_relation = heapRelation->rd_rel->relisshared;

	/*
	 * check parameters
	 */
	if (indexInfo->ii_NumIndexAttrs < 1)
		elog(ERROR, "must index at least one column");

	if (!allow_system_table_mods &&
		IsSystemRelation(heapRelation) &&
		IsNormalProcessingMode())
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("user-defined indexes on system catalog tables are not supported")));

	/*
	 * We cannot allow indexing a shared relation after initdb (because
	 * there's no way to make the entry in other databases' pg_class).
	 * Unfortunately we can't distinguish initdb from a manually started
	 * standalone backend.	However, we can at least prevent this mistake
	 * under normal multi-user operation.
	 */
	if (shared_relation && IsUnderPostmaster)
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
			   errmsg("shared indexes cannot be created after initdb")));

	if (get_relname_relid(indexRelationName, namespaceId))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_TABLE),
				 errmsg("relation \"%s\" already exists",
						indexRelationName)));

	/*
	 * construct tuple descriptor for index tuples
	 */
	indexTupDesc = ConstructTupleDescriptor(heapRelation,
											indexInfo,
											classObjectId);

	/*
	 * create the index relation's relcache entry and physical disk file.
	 * (If we fail further down, it's the smgr's responsibility to remove
	 * the disk file again.)
	 */
	indexRelation = heap_create(indexRelationName,
								namespaceId,
								indexTupDesc,
								shared_relation,
								true,
								allow_system_table_mods);

	/* Fetch the relation OID assigned by heap_create */
	indexoid = RelationGetRelid(indexRelation);

	/*
	 * Obtain exclusive lock on it.  Although no other backends can see it
	 * until we commit, this prevents deadlock-risk complaints from lock
	 * manager in cases such as CLUSTER.
	 */
	LockRelation(indexRelation, AccessExclusiveLock);

	/*
	 * Fill in fields of the index's pg_class entry that are not set
	 * correctly by heap_create.
	 *
	 * XXX should have a cleaner way to create cataloged indexes
	 */
	indexRelation->rd_rel->relowner = GetUserId();
	indexRelation->rd_rel->relam = accessMethodObjectId;
	indexRelation->rd_rel->relkind = RELKIND_INDEX;
	indexRelation->rd_rel->relhasoids = false;

	/*
	 * store index's pg_class entry
	 */
	UpdateRelationRelation(indexRelation);

	/*
	 * now update the object id's of all the attribute tuple forms in the
	 * index relation's tuple descriptor
	 */
	InitializeAttributeOids(indexRelation,
							indexInfo->ii_NumIndexAttrs,
							indexoid);

	/*
	 * append ATTRIBUTE tuples for the index
	 */
	AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs);

	/* ----------------
	 *	  update pg_index
	 *	  (append INDEX tuple)
	 *
	 *	  Note that this stows away a representation of "predicate".
	 *	  (Or, could define a rule to maintain the predicate) --Nels, Feb '92
	 * ----------------
	 */
	UpdateIndexRelation(indexoid, heapRelationId, indexInfo,
						classObjectId, primary);

	/*
	 * Register constraint and dependencies for the index.
	 *
	 * If the index is from a CONSTRAINT clause, construct a pg_constraint
	 * entry.  The index is then linked to the constraint, which in turn
	 * is linked to the table.	If it's not a CONSTRAINT, make the
	 * dependency directly on the table.
	 *
	 * We don't need a dependency on the namespace, because there'll be an
	 * indirect dependency via our parent table.
	 *
	 * During bootstrap we can't register any dependencies, and we don't try
	 * to make a constraint either.
	 */
	if (!IsBootstrapProcessingMode())
	{
		ObjectAddress myself,
					referenced;

		myself.classId = RelOid_pg_class;
		myself.objectId = indexoid;
		myself.objectSubId = 0;

		if (isconstraint)
		{
			char		constraintType;
			Oid			conOid;

			if (primary)
				constraintType = CONSTRAINT_PRIMARY;
			else if (indexInfo->ii_Unique)
				constraintType = CONSTRAINT_UNIQUE;
			else
			{
				elog(ERROR, "constraint must be PRIMARY or UNIQUE");
				constraintType = 0;		/* keep compiler quiet */
			}

			/* Shouldn't have any expressions */
			if (indexInfo->ii_Expressions)
				elog(ERROR, "constraints can't have index expressions");

			conOid = CreateConstraintEntry(indexRelationName,
										   namespaceId,
										   constraintType,
										   false,		/* isDeferrable */
										   false,		/* isDeferred */
										   heapRelationId,
										   indexInfo->ii_KeyAttrNumbers,
										   indexInfo->ii_NumIndexAttrs,
										   InvalidOid,	/* no domain */
										   InvalidOid,	/* no foreign key */
										   NULL,
										   0,
										   ' ',
										   ' ',
										   ' ',
										   InvalidOid,	/* no associated index */
										   NULL,		/* no check constraint */
										   NULL,
										   NULL);

			referenced.classId = get_system_catalog_relid(ConstraintRelationName);
			referenced.objectId = conOid;
			referenced.objectSubId = 0;

			recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
		}
		else
		{
			/* Create auto dependencies on simply-referenced columns */
			for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
			{
				if (indexInfo->ii_KeyAttrNumbers[i] != 0)
				{
					referenced.classId = RelOid_pg_class;
					referenced.objectId = heapRelationId;
					referenced.objectSubId = indexInfo->ii_KeyAttrNumbers[i];

					recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
				}
			}
		}

		/* Store dependency on operator classes */
		referenced.classId = get_system_catalog_relid(OperatorClassRelationName);
		for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
		{
			referenced.objectId = classObjectId[i];
			referenced.objectSubId = 0;

			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
		}

		/* Store dependencies on anything mentioned in index expressions */
		if (indexInfo->ii_Expressions)
		{
			recordDependencyOnSingleRelExpr(&myself,
									  (Node *) indexInfo->ii_Expressions,
											heapRelationId,
											DEPENDENCY_NORMAL,
											DEPENDENCY_AUTO);
		}

		/* Store dependencies on anything mentioned in predicate */
		if (indexInfo->ii_Predicate)
		{
			recordDependencyOnSingleRelExpr(&myself,
										(Node *) indexInfo->ii_Predicate,
											heapRelationId,
											DEPENDENCY_NORMAL,
											DEPENDENCY_AUTO);
		}
	}

	/*
	 * Advance the command counter so that we can see the newly-entered
	 * catalog tuples for the index.
	 */
	CommandCounterIncrement();

	/*
	 * In bootstrap mode, we have to fill in the index strategy structure
	 * with information from the catalogs.  If we aren't bootstrapping,
	 * then the relcache entry has already been rebuilt thanks to sinval
	 * update during CommandCounterIncrement.
	 */
	if (IsBootstrapProcessingMode())
		RelationInitIndexAccessInfo(indexRelation);
	else
		Assert(indexRelation->rd_indexcxt != NULL);

	/*
	 * If this is bootstrap (initdb) time, then we don't actually fill in
	 * the index yet.  We'll be creating more indexes and classes later,
	 * so we delay filling them in until just before we're done with
	 * bootstrapping.  Otherwise, we call the routine that constructs the
	 * index.
	 *
	 * In normal processing mode, the heap and index relations are closed by
	 * index_build() --- but we continue to hold the ShareLock on the heap
	 * and the exclusive lock on the index that we acquired above, until
	 * end of transaction.
	 */
	if (IsBootstrapProcessingMode())
	{
		index_register(heapRelationId, indexoid, indexInfo);
		/* XXX shouldn't we close the heap and index rels here? */
	}
	else
		index_build(heapRelation, indexRelation, indexInfo);

	return indexoid;
}