|
38 | 38 | #include"utils/tqual.h"
|
39 | 39 |
|
40 | 40 |
|
41 |
| -staticvoidclone_fk_constraints(Relationpg_constraint,RelationparentRel, |
42 |
| -RelationpartRel,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 | returnconOid;
|
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(OidparentId,OidrelationId,List**cloned) |
402 |
| -{ |
403 |
| -Relationpg_constraint; |
404 |
| -RelationparentRel; |
405 |
| -Relationrel; |
406 |
| -ScanKeyDatakey; |
407 |
| -SysScanDescscan; |
408 |
| -HeapTupletuple; |
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 |
| -staticvoid |
450 |
| -clone_fk_constraints(Relationpg_constraint,RelationparentRel, |
451 |
| -RelationpartRel,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 |
| -OidparentConstrOid=lfirst_oid(cell); |
471 |
| -Form_pg_constraintconstrForm; |
472 |
| -HeapTupletuple; |
473 |
| -AttrNumberconkey[INDEX_MAX_KEYS]; |
474 |
| -AttrNumbermapped_conkey[INDEX_MAX_KEYS]; |
475 |
| -AttrNumberconfkey[INDEX_MAX_KEYS]; |
476 |
| -Oidconpfeqop[INDEX_MAX_KEYS]; |
477 |
| -Oidconppeqop[INDEX_MAX_KEYS]; |
478 |
| -Oidconffeqop[INDEX_MAX_KEYS]; |
479 |
| -Constraint*fkconstraint; |
480 |
| -boolattach_it; |
481 |
| -OidconstrOid; |
482 |
| -ObjectAddressparentAddr, |
483 |
| -childAddr; |
484 |
| -intnelem; |
485 |
| -ListCell*cell; |
486 |
| -inti; |
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_constraintpartConstr; |
521 |
| -HeapTuplepartcontup; |
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 |
| -PartitionDescpartdesc=RelationGetPartitionDesc(partRel); |
669 |
| -inti; |
670 |
| - |
671 |
| -for (i=0;i<partdesc->nparts;i++) |
672 |
| -{ |
673 |
| -RelationchildRel; |
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).
|
|