/* * map_partition_varattnos - maps varattno of any Vars in expr from the * attno's of 'from_rel' to the attno's of 'to_rel' partition, each of which * may be either a leaf partition or a partitioned table, but both of which * must be from the same partitioning hierarchy. * * Even though all of the same column names must be present in all relations * in the hierarchy, and they must also have the same types, the attnos may * be different. * * If found_whole_row is not NULL, *found_whole_row returns whether a * whole-row variable was found in the input expression. * * Note: this will work on any node tree, so really the argument and result * should be declared "Node *". But a substantial majority of the callers * are working on Lists, so it's less messy to do the casts internally. */ List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel, bool *found_whole_row) { bool my_found_whole_row = false; if (expr != NIL) { AttrNumber *part_attnos; part_attnos = convert_tuples_by_name_map(RelationGetDescr(to_rel), RelationGetDescr(from_rel), gettext_noop("could not convert row type")); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attnos, RelationGetDescr(from_rel)->natts, RelationGetForm(to_rel)->reltype, &my_found_whole_row); } if (found_whole_row) *found_whole_row = my_found_whole_row; return expr; }
/* * 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); }