|
38 | 38 | #include "utils/tqual.h" |
39 | 39 |
|
40 | 40 |
|
41 | | -static void clone_fk_constraints(Relation pg_constraint, Relation parentRel, |
42 | | - Relation partRel, List *clone, List **cloned); |
43 | | - |
44 | | - |
45 | 41 | /* |
46 | 42 | * CreateConstraintEntry |
47 | 43 | * Create a constraint table entry. |
@@ -385,304 +381,6 @@ CreateConstraintEntry(const char *constraintName, |
385 | 381 | return conOid; |
386 | 382 | } |
387 | 383 |
|
388 | | -/* |
389 | | - * CloneForeignKeyConstraints |
390 | | - * Clone foreign keys from a partitioned table to a newly acquired |
391 | | - * partition. |
392 | | - * |
393 | | - * relationId is a partition of parentId, so we can be certain that it has the |
394 | | - * same columns with the same datatypes. The columns may be in different |
395 | | - * order, though. |
396 | | - * |
397 | | - * The *cloned list is appended ClonedConstraint elements describing what was |
398 | | - * created. |
399 | | - */ |
400 | | -void |
401 | | -CloneForeignKeyConstraints(Oid parentId, Oid relationId, List **cloned) |
402 | | -{ |
403 | | - Relation pg_constraint; |
404 | | - Relation parentRel; |
405 | | - Relation rel; |
406 | | - ScanKeyData key; |
407 | | - SysScanDesc scan; |
408 | | - HeapTuple tuple; |
409 | | - List *clone = NIL; |
410 | | - |
411 | | - parentRel = heap_open(parentId, NoLock); /* already got lock */ |
412 | | - /* see ATAddForeignKeyConstraint about lock level */ |
413 | | - rel = heap_open(relationId, AccessExclusiveLock); |
414 | | - pg_constraint = heap_open(ConstraintRelationId, RowShareLock); |
415 | | - |
416 | | - /* Obtain the list of constraints to clone or attach */ |
417 | | - ScanKeyInit(&key, |
418 | | - Anum_pg_constraint_conrelid, BTEqualStrategyNumber, |
419 | | - F_OIDEQ, ObjectIdGetDatum(parentId)); |
420 | | - scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true, |
421 | | - NULL, 1, &key); |
422 | | - while ((tuple = systable_getnext(scan)) != NULL) |
423 | | - clone = lappend_oid(clone, HeapTupleGetOid(tuple)); |
424 | | - systable_endscan(scan); |
425 | | - |
426 | | - /* Do the actual work, recursing to partitions as needed */ |
427 | | - clone_fk_constraints(pg_constraint, parentRel, rel, clone, cloned); |
428 | | - |
429 | | - /* We're done. Clean up */ |
430 | | - heap_close(parentRel, NoLock); |
431 | | - heap_close(rel, NoLock); /* keep lock till commit */ |
432 | | - heap_close(pg_constraint, RowShareLock); |
433 | | -} |
434 | | - |
435 | | -/* |
436 | | - * clone_fk_constraints |
437 | | - * Recursive subroutine for CloneForeignKeyConstraints |
438 | | - * |
439 | | - * Clone the given list of FK constraints when a partition is attached. |
440 | | - * |
441 | | - * When cloning foreign keys to a partition, it may happen that equivalent |
442 | | - * constraints already exist in the partition for some of them. We can skip |
443 | | - * creating a clone in that case, and instead just attach the existing |
444 | | - * constraint to the one in the parent. |
445 | | - * |
446 | | - * This function recurses to partitions, if the new partition is partitioned; |
447 | | - * of course, only do this for FKs that were actually cloned. |
448 | | - */ |
449 | | -static void |
450 | | -clone_fk_constraints(Relation pg_constraint, Relation parentRel, |
451 | | - Relation partRel, List *clone, List **cloned) |
452 | | -{ |
453 | | - AttrNumber *attmap; |
454 | | - List *partFKs; |
455 | | - List *subclone = NIL; |
456 | | - ListCell *cell; |
457 | | - |
458 | | - /* |
459 | | - * The constraint key may differ, if the columns in the partition are |
460 | | - * different. This map is used to convert them. |
461 | | - */ |
462 | | - attmap = convert_tuples_by_name_map(RelationGetDescr(partRel), |
463 | | - RelationGetDescr(parentRel), |
464 | | - gettext_noop("could not convert row type")); |
465 | | - |
466 | | - partFKs = copyObject(RelationGetFKeyList(partRel)); |
467 | | - |
468 | | - foreach(cell, clone) |
469 | | - { |
470 | | - Oid parentConstrOid = lfirst_oid(cell); |
471 | | - Form_pg_constraint constrForm; |
472 | | - HeapTuple tuple; |
473 | | - AttrNumber conkey[INDEX_MAX_KEYS]; |
474 | | - AttrNumber mapped_conkey[INDEX_MAX_KEYS]; |
475 | | - AttrNumber confkey[INDEX_MAX_KEYS]; |
476 | | - Oid conpfeqop[INDEX_MAX_KEYS]; |
477 | | - Oid conppeqop[INDEX_MAX_KEYS]; |
478 | | - Oid conffeqop[INDEX_MAX_KEYS]; |
479 | | - Constraint *fkconstraint; |
480 | | - bool attach_it; |
481 | | - Oid constrOid; |
482 | | - ObjectAddress parentAddr, |
483 | | - childAddr; |
484 | | - int nelem; |
485 | | - ListCell *cell; |
486 | | - int i; |
487 | | - |
488 | | - tuple = SearchSysCache1(CONSTROID, parentConstrOid); |
489 | | - if (!tuple) |
490 | | - elog(ERROR, "cache lookup failed for constraint %u", |
491 | | - parentConstrOid); |
492 | | - constrForm = (Form_pg_constraint) GETSTRUCT(tuple); |
493 | | - |
494 | | - /* only foreign keys */ |
495 | | - if (constrForm->contype != CONSTRAINT_FOREIGN) |
496 | | - { |
497 | | - ReleaseSysCache(tuple); |
498 | | - continue; |
499 | | - } |
500 | | - |
501 | | - ObjectAddressSet(parentAddr, ConstraintRelationId, parentConstrOid); |
502 | | - |
503 | | - DeconstructFkConstraintRow(tuple, &nelem, conkey, confkey, |
504 | | - conpfeqop, conppeqop, conffeqop); |
505 | | - for (i = 0; i < nelem; i++) |
506 | | - mapped_conkey[i] = attmap[conkey[i] - 1]; |
507 | | - |
508 | | - /* |
509 | | - * Before creating a new constraint, see whether any existing FKs are |
510 | | - * fit for the purpose. If one is, attach the parent constraint to it, |
511 | | - * and don't clone anything. This way we avoid the expensive |
512 | | - * verification step and don't end up with a duplicate FK. This also |
513 | | - * means we don't consider this constraint when recursing to |
514 | | - * partitions. |
515 | | - */ |
516 | | - attach_it = false; |
517 | | - foreach(cell, partFKs) |
518 | | - { |
519 | | - ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, cell); |
520 | | - Form_pg_constraint partConstr; |
521 | | - HeapTuple partcontup; |
522 | | - |
523 | | - attach_it = true; |
524 | | - |
525 | | - /* |
526 | | - * Do some quick & easy initial checks. If any of these fail, we |
527 | | - * cannot use this constraint, but keep looking. |
528 | | - */ |
529 | | - if (fk->confrelid != constrForm->confrelid || fk->nkeys != nelem) |
530 | | - { |
531 | | - attach_it = false; |
532 | | - continue; |
533 | | - } |
534 | | - for (i = 0; i < nelem; i++) |
535 | | - { |
536 | | - if (fk->conkey[i] != mapped_conkey[i] || |
537 | | - fk->confkey[i] != confkey[i] || |
538 | | - fk->conpfeqop[i] != conpfeqop[i]) |
539 | | - { |
540 | | - attach_it = false; |
541 | | - break; |
542 | | - } |
543 | | - } |
544 | | - if (!attach_it) |
545 | | - continue; |
546 | | - |
547 | | - /* |
548 | | - * Looks good so far; do some more extensive checks. Presumably |
549 | | - * the check for 'convalidated' could be dropped, since we don't |
550 | | - * really care about that, but let's be careful for now. |
551 | | - */ |
552 | | - partcontup = SearchSysCache1(CONSTROID, |
553 | | - ObjectIdGetDatum(fk->conoid)); |
554 | | - if (!partcontup) |
555 | | - elog(ERROR, "cache lookup failed for constraint %u", |
556 | | - fk->conoid); |
557 | | - partConstr = (Form_pg_constraint) GETSTRUCT(partcontup); |
558 | | - if (OidIsValid(partConstr->conparentid) || |
559 | | - !partConstr->convalidated || |
560 | | - partConstr->condeferrable != constrForm->condeferrable || |
561 | | - partConstr->condeferred != constrForm->condeferred || |
562 | | - partConstr->confupdtype != constrForm->confupdtype || |
563 | | - partConstr->confdeltype != constrForm->confdeltype || |
564 | | - partConstr->confmatchtype != constrForm->confmatchtype) |
565 | | - { |
566 | | - ReleaseSysCache(partcontup); |
567 | | - attach_it = false; |
568 | | - continue; |
569 | | - } |
570 | | - |
571 | | - ReleaseSysCache(partcontup); |
572 | | - |
573 | | - /* looks good! Attach this constraint */ |
574 | | - ConstraintSetParentConstraint(fk->conoid, |
575 | | - HeapTupleGetOid(tuple)); |
576 | | - CommandCounterIncrement(); |
577 | | - attach_it = true; |
578 | | - break; |
579 | | - } |
580 | | - |
581 | | - /* |
582 | | - * If we attached to an existing constraint, there is no need to |
583 | | - * create a new one. In fact, there's no need to recurse for this |
584 | | - * constraint to partitions, either. |
585 | | - */ |
586 | | - if (attach_it) |
587 | | - { |
588 | | - ReleaseSysCache(tuple); |
589 | | - continue; |
590 | | - } |
591 | | - |
592 | | - constrOid = |
593 | | - CreateConstraintEntry(NameStr(constrForm->conname), |
594 | | - constrForm->connamespace, |
595 | | - CONSTRAINT_FOREIGN, |
596 | | - constrForm->condeferrable, |
597 | | - constrForm->condeferred, |
598 | | - constrForm->convalidated, |
599 | | - HeapTupleGetOid(tuple), |
600 | | - RelationGetRelid(partRel), |
601 | | - mapped_conkey, |
602 | | - nelem, |
603 | | - nelem, |
604 | | - InvalidOid, /* not a domain constraint */ |
605 | | - constrForm->conindid, /* same index */ |
606 | | - constrForm->confrelid, /* same foreign rel */ |
607 | | - confkey, |
608 | | - conpfeqop, |
609 | | - conppeqop, |
610 | | - conffeqop, |
611 | | - nelem, |
612 | | - constrForm->confupdtype, |
613 | | - constrForm->confdeltype, |
614 | | - constrForm->confmatchtype, |
615 | | - NULL, |
616 | | - NULL, |
617 | | - NULL, |
618 | | - NULL, |
619 | | - false, |
620 | | - 1, false, true); |
621 | | - subclone = lappend_oid(subclone, constrOid); |
622 | | - |
623 | | - ObjectAddressSet(childAddr, ConstraintRelationId, constrOid); |
624 | | - recordDependencyOn(&childAddr, &parentAddr, DEPENDENCY_INTERNAL_AUTO); |
625 | | - |
626 | | - fkconstraint = makeNode(Constraint); |
627 | | - /* for now this is all we need */ |
628 | | - fkconstraint->conname = pstrdup(NameStr(constrForm->conname)); |
629 | | - fkconstraint->fk_upd_action = constrForm->confupdtype; |
630 | | - fkconstraint->fk_del_action = constrForm->confdeltype; |
631 | | - fkconstraint->deferrable = constrForm->condeferrable; |
632 | | - fkconstraint->initdeferred = constrForm->condeferred; |
633 | | - |
634 | | - createForeignKeyTriggers(partRel, constrForm->confrelid, fkconstraint, |
635 | | - constrOid, constrForm->conindid, false); |
636 | | - |
637 | | - if (cloned) |
638 | | - { |
639 | | - ClonedConstraint *newc; |
640 | | - |
641 | | - /* |
642 | | - * Feed back caller about the constraints we created, so that they |
643 | | - * can set up constraint verification. |
644 | | - */ |
645 | | - newc = palloc(sizeof(ClonedConstraint)); |
646 | | - newc->relid = RelationGetRelid(partRel); |
647 | | - newc->refrelid = constrForm->confrelid; |
648 | | - newc->conindid = constrForm->conindid; |
649 | | - newc->conid = constrOid; |
650 | | - newc->constraint = fkconstraint; |
651 | | - |
652 | | - *cloned = lappend(*cloned, newc); |
653 | | - } |
654 | | - |
655 | | - ReleaseSysCache(tuple); |
656 | | - } |
657 | | - |
658 | | - pfree(attmap); |
659 | | - list_free_deep(partFKs); |
660 | | - |
661 | | - /* |
662 | | - * If the partition is partitioned, recurse to handle any constraints that |
663 | | - * were cloned. |
664 | | - */ |
665 | | - if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && |
666 | | - subclone != NIL) |
667 | | - { |
668 | | - PartitionDesc partdesc = RelationGetPartitionDesc(partRel); |
669 | | - int i; |
670 | | - |
671 | | - for (i = 0; i < partdesc->nparts; i++) |
672 | | - { |
673 | | - Relation childRel; |
674 | | - |
675 | | - childRel = heap_open(partdesc->oids[i], AccessExclusiveLock); |
676 | | - clone_fk_constraints(pg_constraint, |
677 | | - partRel, |
678 | | - childRel, |
679 | | - subclone, |
680 | | - cloned); |
681 | | - heap_close(childRel, NoLock); /* keep lock till commit */ |
682 | | - } |
683 | | - } |
684 | | -} |
685 | | - |
686 | 384 | /* |
687 | 385 | * Test whether given name is currently used as a constraint name |
688 | 386 | * for the given object (relation or domain). |
|
0 commit comments