コード例 #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
ファイル: inherit.c プロジェクト: hasegeli/postgres
/*
 * expand_inherited_rtentry
 *		Check whether a rangetable entry represents an inheritance set.
 *		If so, add entries for all the child tables to the query's
 *		rangetable, and build AppendRelInfo nodes for all the child tables
 *		and add them to root->append_rel_list.  If not, clear the entry's
 *		"inh" flag to prevent later code from looking for AppendRelInfos.
 *
 * Note that the original RTE is considered to represent the whole
 * inheritance set.  The first of the generated RTEs is an RTE for the same
 * table, but with inh = false, to represent the parent table in its role
 * as a simple member of the inheritance set.
 *
 * A childless table is never considered to be an inheritance set. For
 * regular inheritance, a parent RTE must always have at least two associated
 * AppendRelInfos: one corresponding to the parent table as a simple member of
 * inheritance set and one or more corresponding to the actual children.
 * Since a partitioned table is not scanned, it might have only one associated
 * AppendRelInfo.
 */
static void
expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
{
	Oid			parentOID;
	PlanRowMark *oldrc;
	Relation	oldrelation;
	LOCKMODE	lockmode;
	List	   *inhOIDs;
	ListCell   *l;

	/* Does RT entry allow inheritance? */
	if (!rte->inh)
		return;
	/* Ignore any already-expanded UNION ALL nodes */
	if (rte->rtekind != RTE_RELATION)
	{
		Assert(rte->rtekind == RTE_SUBQUERY);
		return;
	}
	/* Fast path for common case of childless table */
	parentOID = rte->relid;
	if (!has_subclass(parentOID))
	{
		/* Clear flag before returning */
		rte->inh = false;
		return;
	}

	/*
	 * The rewriter should already have obtained an appropriate lock on each
	 * relation named in the query.  However, for each child relation we add
	 * to the query, we must obtain an appropriate lock, because this will be
	 * the first use of those relations in the parse/rewrite/plan pipeline.
	 * Child rels should use the same lockmode as their parent.
	 */
	lockmode = rte->rellockmode;

	/* Scan for all members of inheritance set, acquire needed locks */
	inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);

	/*
	 * Check that there's at least one descendant, else treat as no-child
	 * case.  This could happen despite above has_subclass() check, if table
	 * once had a child but no longer does.
	 */
	if (list_length(inhOIDs) < 2)
	{
		/* Clear flag before returning */
		rte->inh = false;
		return;
	}

	/*
	 * If parent relation is selected FOR UPDATE/SHARE, we need to mark its
	 * PlanRowMark as isParent = true, and generate a new PlanRowMark for each
	 * child.
	 */
	oldrc = get_plan_rowmark(root->rowMarks, rti);
	if (oldrc)
		oldrc->isParent = true;

	/*
	 * Must open the parent relation to examine its tupdesc.  We need not lock
	 * it; we assume the rewriter already did.
	 */
	oldrelation = heap_open(parentOID, NoLock);

	/* Scan the inheritance set and expand it */
	if (RelationGetPartitionDesc(oldrelation) != NULL)
	{
		Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);

		/*
		 * If this table has partitions, recursively expand them in the order
		 * in which they appear in the PartitionDesc.  While at it, also
		 * extract the partition key columns of all the partitioned tables.
		 */
		expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,
								   lockmode, &root->append_rel_list);
	}
	else
	{
		List	   *appinfos = NIL;
		RangeTblEntry *childrte;
		Index		childRTindex;

		/*
		 * This table has no partitions.  Expand any plain inheritance
		 * children in the order the OIDs were returned by
		 * find_all_inheritors.
		 */
		foreach(l, inhOIDs)
		{
			Oid			childOID = lfirst_oid(l);
			Relation	newrelation;

			/* Open rel if needed; we already have required locks */
			if (childOID != parentOID)
				newrelation = heap_open(childOID, NoLock);
			else
				newrelation = oldrelation;

			/*
			 * It is possible that the parent table has children that are temp
			 * tables of other backends.  We cannot safely access such tables
			 * (because of buffering issues), and the best thing to do seems
			 * to be to silently ignore them.
			 */
			if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
			{
				heap_close(newrelation, lockmode);
				continue;
			}

			expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc,
											newrelation,
											&appinfos, &childrte,
											&childRTindex);

			/* Close child relations, but keep locks */
			if (childOID != parentOID)
				heap_close(newrelation, NoLock);
		}

		/*
		 * If all the children were temp tables, pretend it's a
		 * non-inheritance situation; we don't need Append node in that case.
		 * The duplicate RTE we added for the parent table is harmless, so we
		 * don't bother to get rid of it; ditto for the useless PlanRowMark
		 * node.
		 */
		if (list_length(appinfos) < 2)
			rte->inh = false;
		else
			root->append_rel_list = list_concat(root->append_rel_list,
												appinfos);

	}