|
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