Correctly restore composite VKeys in DomainContent (#825)

* Restore composite vkeys in DomainContent

PollMessage/BillingEvent vkeys in DomainContent must have their ofy keys
restored from other fields in DomainContent (namely the repo id and their
specific history event ids).

Add PostLoad methods to DomainContent and DomainHistory to do the restoration.

* Fixes for review.

* Deal with foreign-key cycles
This commit is contained in:
Michael Muller 2020-10-07 12:42:01 -04:00 committed by GitHub
parent 96e9c1e0af
commit 151a2afb14
10 changed files with 343 additions and 5 deletions

View file

@ -35,6 +35,7 @@ import javax.persistence.Index;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.JoinTable; import javax.persistence.JoinTable;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.PostLoad;
import javax.persistence.Table; import javax.persistence.Table;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -106,6 +107,12 @@ public class DomainBase extends DomainContent
return gracePeriods; return gracePeriods;
} }
@PostLoad
@SuppressWarnings("UnusedMethod")
private final void postLoad() {
restoreOfyKeys(getRepoId());
}
@Override @Override
public VKey<DomainBase> createVKey() { public VKey<DomainBase> createVKey() {
return VKey.create(DomainBase.class, getRepoId(), Key.create(this)); return VKey.create(DomainBase.class, getRepoId(), Key.create(this));

View file

@ -40,6 +40,7 @@ import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.collect.Streams; import com.google.common.collect.Streams;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Ignore; import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.IgnoreSave; import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.Index;
@ -49,6 +50,7 @@ import google.registry.flows.ResourceFlowUtils;
import google.registry.model.EppResource; import google.registry.model.EppResource;
import google.registry.model.EppResource.ResourceWithTransferData; import google.registry.model.EppResource.ResourceWithTransferData;
import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.rgp.GracePeriodStatus;
@ -57,6 +59,7 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.DomainTransferData; import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferStatus; import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
@ -209,6 +212,15 @@ public class DomainContent extends EppResource
@Column(name = "deletion_poll_message_id") @Column(name = "deletion_poll_message_id")
VKey<PollMessage.OneTime> deletePollMessage; VKey<PollMessage.OneTime> deletePollMessage;
/**
* History record for the delete poll message.
*
* <p>Here so we can restore the original ofy key from sql.
*/
@Column(name = "deletion_poll_message_history_id")
@Ignore
Long deletePollMessageHistoryId;
/** /**
* The recurring billing event associated with this domain's autorenewals. * The recurring billing event associated with this domain's autorenewals.
* *
@ -220,6 +232,15 @@ public class DomainContent extends EppResource
@Column(name = "billing_recurrence_id") @Column(name = "billing_recurrence_id")
VKey<BillingEvent.Recurring> autorenewBillingEvent; VKey<BillingEvent.Recurring> autorenewBillingEvent;
/**
* History record for the autorenew billing event.
*
* <p>Here so we can restore the original ofy key from sql.
*/
@Column(name = "billing_recurrence_history_id")
@Ignore
Long autorenewBillingEventHistoryId;
/** /**
* The recurring poll message associated with this domain's autorenewals. * The recurring poll message associated with this domain's autorenewals.
* *
@ -231,6 +252,13 @@ public class DomainContent extends EppResource
@Column(name = "autorenew_poll_message_id") @Column(name = "autorenew_poll_message_id")
VKey<PollMessage.Autorenew> autorenewPollMessage; VKey<PollMessage.Autorenew> autorenewPollMessage;
/**
* History record for the autorenew poll message.
*
* <p>Here so we can restore the original ofy key from sql.
*/
@Ignore Long autorenewPollMessageHistoryId;
/** The unexpired grace periods for this domain (some of which may not be active yet). */ /** The unexpired grace periods for this domain (some of which may not be active yet). */
@Transient Set<GracePeriod> gracePeriods; @Transient Set<GracePeriod> gracePeriods;
@ -284,10 +312,22 @@ public class DomainContent extends EppResource
nullToEmptyImmutableCopy(gracePeriods).stream() nullToEmptyImmutableCopy(gracePeriods).stream()
.map(gracePeriod -> gracePeriod.cloneWithDomainRepoId(getRepoId())) .map(gracePeriod -> gracePeriod.cloneWithDomainRepoId(getRepoId()))
.collect(toImmutableSet()); .collect(toImmutableSet());
// Restore history record ids.
autorenewPollMessageHistoryId = getHistoryId(autorenewPollMessage);
autorenewBillingEventHistoryId = getHistoryId(autorenewBillingEvent);
deletePollMessageHistoryId = getHistoryId(deletePollMessage);
} }
/**
* The {@link javax.persistence.PostLoad} method for {@link DomainContent}.
*
* <p>We name this domainContentPostLoad to distinguish it from the {@link PostLoad} method in
* DomainBase (if they share the same name, this one is never called).
*/
@PostLoad @PostLoad
void postLoad() { @SuppressWarnings("UnusedMethod")
private final void domainContentPostLoad() {
// Reconstitute the contact list. // Reconstitute the contact list.
ImmutableSet.Builder<DesignatedContact> contactsBuilder = new ImmutableSet.Builder<>(); ImmutableSet.Builder<DesignatedContact> contactsBuilder = new ImmutableSet.Builder<>();
@ -308,6 +348,48 @@ public class DomainContent extends EppResource
allContacts = contactsBuilder.build(); allContacts = contactsBuilder.build();
} }
/**
* Restores the composite ofy keys from SQL data.
*
* <p>MUST ONLY BE CALLED FROM A PostLoad method. This is a package-visible method that
* effectively mutates an immutable object.
*
* <p>We have to do this because:
*
* <ul>
* <li>We've changed the {@link PostLoad} method behavior to make all {@link PostLoad} calls in
* the class hierarchy (and not merely the most specific one) be called after an object is
* loaded.
* <li>When restoring a {@link DomainBase} object (which is a subclass) the repo id is not
* populated until after our {@link PostLoad} method is called. Therefore, we need to
* restore these ofy keys (which depend on the repo id) from {@link DomainBase}'s {@link
* PostLoad} method.
* <li>When restoring a {@link DomainHistory} object, hibernate restores a {@link DomainContent}
* instance, therefore we need our own {@link PostLoad} method to restore the other fields.
* In order to restore the ofy keys, we need to invoke this method separately from {@link
* DomainHistory}'s {@link PostLoad} method and pass in the repo id, which is stored in a
* different field in {@link DomainHistory}.
* </ul>
*/
void restoreOfyKeys(String repoId) {
// Reconstitute the ofy keys.
Key<DomainBase> myKey = Key.create(DomainBase.class, repoId);
deletePollMessage = restoreOfyFrom(myKey, deletePollMessage, deletePollMessageHistoryId);
autorenewBillingEvent =
restoreOfyFrom(myKey, autorenewBillingEvent, autorenewBillingEventHistoryId);
autorenewPollMessage =
restoreOfyFrom(myKey, autorenewPollMessage, autorenewPollMessageHistoryId);
}
private <T> VKey<T> restoreOfyFrom(Key<DomainBase> domainKey, VKey<T> key, Long historyId) {
if (historyId == null) {
// This is a legacy key (or a null key, in which case this works too)
return VKey.restoreOfyFrom(key, EntityGroupRoot.class, "per-tld");
} else {
return VKey.restoreOfyFrom(key, domainKey, HistoryEntry.class, historyId);
}
}
public ImmutableSet<String> getSubordinateHosts() { public ImmutableSet<String> getSubordinateHosts() {
return nullToEmptyImmutableCopy(subordinateHosts); return nullToEmptyImmutableCopy(subordinateHosts);
} }
@ -655,6 +737,17 @@ public class DomainContent extends EppResource
+ " use DomainBase instead"); + " use DomainBase instead");
} }
private static Long getHistoryId(VKey<?> key) {
if (key == null) {
return null;
}
Key<?> parent = key.getOfyKey().getParent();
if (parent == null || parent.getKind().equals("EntityGroupRoot")) {
return null;
}
return parent.getId();
}
/** Predicate to determine if a given {@link DesignatedContact} is the registrant. */ /** Predicate to determine if a given {@link DesignatedContact} is the registrant. */
static final Predicate<DesignatedContact> IS_REGISTRANT = static final Predicate<DesignatedContact> IS_REGISTRANT =
(DesignatedContact contact) -> DesignatedContact.Type.REGISTRANT.equals(contact.type); (DesignatedContact contact) -> DesignatedContact.Type.REGISTRANT.equals(contact.type);
@ -824,16 +917,19 @@ public class DomainContent extends EppResource
public B setDeletePollMessage(VKey<PollMessage.OneTime> deletePollMessage) { public B setDeletePollMessage(VKey<PollMessage.OneTime> deletePollMessage) {
getInstance().deletePollMessage = deletePollMessage; getInstance().deletePollMessage = deletePollMessage;
getInstance().deletePollMessageHistoryId = getHistoryId(deletePollMessage);
return thisCastToDerived(); return thisCastToDerived();
} }
public B setAutorenewBillingEvent(VKey<BillingEvent.Recurring> autorenewBillingEvent) { public B setAutorenewBillingEvent(VKey<BillingEvent.Recurring> autorenewBillingEvent) {
getInstance().autorenewBillingEvent = autorenewBillingEvent; getInstance().autorenewBillingEvent = autorenewBillingEvent;
getInstance().autorenewBillingEventHistoryId = getHistoryId(autorenewBillingEvent);
return thisCastToDerived(); return thisCastToDerived();
} }
public B setAutorenewPollMessage(VKey<PollMessage.Autorenew> autorenewPollMessage) { public B setAutorenewPollMessage(VKey<PollMessage.Autorenew> autorenewPollMessage) {
getInstance().autorenewPollMessage = autorenewPollMessage; getInstance().autorenewPollMessage = autorenewPollMessage;
getInstance().autorenewPollMessageHistoryId = getHistoryId(autorenewPollMessage);
return thisCastToDerived(); return thisCastToDerived();
} }

View file

@ -170,6 +170,8 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
// domainContent with a null object. Unfortunately, the updateTimestamp is never null in SQL. // domainContent with a null object. Unfortunately, the updateTimestamp is never null in SQL.
if (domainContent.getDomainName() == null) { if (domainContent.getDomainName() == null) {
domainContent = null; domainContent = null;
} else {
domainContent.restoreOfyKeys(domainRepoId);
} }
} }
parent = Key.create(DomainBase.class, domainRepoId); parent = Key.create(DomainBase.class, domainRepoId);

View file

@ -273,8 +273,11 @@ class InitSqlPipelineTest {
"revisions", "revisions",
"updateTimestamp", "updateTimestamp",
"autorenewBillingEvent", "autorenewBillingEvent",
"autorenewBillingEventHistoryId",
"autorenewPollMessage", "autorenewPollMessage",
"autorenewPollMessageHistoryId",
"deletePollMessage", "deletePollMessage",
"deletePollMessageHistoryId",
"nsHosts", "nsHosts",
"transferData"); "transferData");
assertThat(actual.getAdminContact().getSqlKey()) assertThat(actual.getAdminContact().getSqlKey())

View file

@ -25,6 +25,11 @@ import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.contact.ContactResource; import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact.Type; import google.registry.model.domain.DesignatedContact.Type;
import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.domain.launch.LaunchNotice;
@ -33,6 +38,8 @@ import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth; import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.ContactTransferData; import google.registry.model.transfer.ContactTransferData;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestRules; import google.registry.persistence.transaction.JpaTestRules;
@ -60,6 +67,7 @@ public class DomainBaseSqlTest {
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension(); new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
private DomainBase domain; private DomainBase domain;
private DomainHistory historyEntry;
private VKey<ContactResource> contactKey; private VKey<ContactResource> contactKey;
private VKey<ContactResource> contact2Key; private VKey<ContactResource> contact2Key;
private VKey<HostResource> host1VKey; private VKey<HostResource> host1VKey;
@ -395,6 +403,174 @@ public class DomainBaseSqlTest {
}); });
} }
@Test
void persistDomainWithCompositeVKeys() {
jpaTm()
.transact(
() -> {
historyEntry =
new DomainHistory.Builder()
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(Period.create(1, Period.Unit.YEARS))
.setModificationTime(DateTime.now(UTC))
.setParent(Key.create(DomainBase.class, "4-COM"))
.setDomainRepoId("4-COM")
// These are non-null, but I don't think some tests set them.
.setReason("felt like it")
.setRequestedByRegistrar(false)
.setXmlBytes(new byte[0])
.build();
BillingEvent.Recurring billEvent =
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId("example.com")
.setClientId("registrar1")
.setDomainRepoId("4-COM")
.setDomainHistoryRevisionId(1L)
.setEventTime(DateTime.now(UTC).plusYears(1))
.setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntry)
.build();
PollMessage.Autorenew autorenewPollMessage =
new PollMessage.Autorenew.Builder()
.setClientId("registrar1")
.setEventTime(DateTime.now(UTC).plusYears(1))
.setParent(historyEntry)
.build();
PollMessage.OneTime deletePollMessage =
new PollMessage.OneTime.Builder()
.setClientId("registrar1")
.setEventTime(DateTime.now(UTC).plusYears(1))
.setParent(historyEntry)
.build();
jpaTm().insert(contact);
jpaTm().insert(contact2);
jpaTm().insert(host);
domain =
domain
.asBuilder()
.setAutorenewBillingEvent(billEvent.createVKey())
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
.setDeletePollMessage(deletePollMessage.createVKey())
.build();
historyEntry = historyEntry.asBuilder().setDomainContent(domain).build();
jpaTm().insert(historyEntry);
jpaTm().insert(autorenewPollMessage);
jpaTm().insert(billEvent);
jpaTm().insert(deletePollMessage);
jpaTm().insert(domain);
});
// Store the existing BillingRecurrence VKey. This happens after the event has been persisted.
DomainBase persisted = jpaTm().transact(() -> jpaTm().load(domain.createVKey()));
// Verify that the domain data has been persisted.
// dsData still isn't persisted. gracePeriods appears to have the same values but for some
// reason is showing up as different.
assertEqualDomainExcept(persisted, "creationTime", "dsData", "gracePeriods");
// Verify that the DomainContent object from the history record sets the fields correctly.
DomainHistory persistedHistoryEntry =
jpaTm().transact(() -> jpaTm().load(historyEntry.createVKey()));
assertThat(persistedHistoryEntry.getDomainContent().get().getAutorenewPollMessage())
.isEqualTo(domain.getAutorenewPollMessage());
assertThat(persistedHistoryEntry.getDomainContent().get().getAutorenewBillingEvent())
.isEqualTo(domain.getAutorenewBillingEvent());
assertThat(persistedHistoryEntry.getDomainContent().get().getDeletePollMessage())
.isEqualTo(domain.getDeletePollMessage());
}
@Test
void persistDomainWithLegacyVKeys() {
jpaTm()
.transact(
() -> {
historyEntry =
new DomainHistory.Builder()
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setPeriod(Period.create(1, Period.Unit.YEARS))
.setModificationTime(DateTime.now(UTC))
.setParent(Key.create(DomainBase.class, "4-COM"))
.setDomainRepoId("4-COM")
// These are non-null, but I don't think some tests set them.
.setReason("felt like it")
.setRequestedByRegistrar(false)
.setXmlBytes(new byte[0])
.build();
BillingEvent.Recurring billEvent =
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId("example.com")
.setClientId("registrar1")
.setDomainRepoId("4-COM")
.setDomainHistoryRevisionId(1L)
.setEventTime(DateTime.now(UTC).plusYears(1))
.setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntry)
.build();
PollMessage.Autorenew autorenewPollMessage =
new PollMessage.Autorenew.Builder()
.setClientId("registrar1")
.setEventTime(DateTime.now(UTC).plusYears(1))
.setParent(historyEntry)
.build();
PollMessage.OneTime deletePollMessage =
new PollMessage.OneTime.Builder()
.setClientId("registrar1")
.setEventTime(DateTime.now(UTC).plusYears(1))
.setParent(historyEntry)
.build();
jpaTm().insert(contact);
jpaTm().insert(contact2);
jpaTm().insert(host);
domain =
domain
.asBuilder()
.setAutorenewBillingEvent(
createLegacyVKey(BillingEvent.Recurring.class, billEvent.getId()))
.setAutorenewPollMessage(
createLegacyVKey(PollMessage.Autorenew.class, deletePollMessage.getId()))
.setDeletePollMessage(
createLegacyVKey(PollMessage.OneTime.class, autorenewPollMessage.getId()))
.build();
historyEntry = historyEntry.asBuilder().setDomainContent(domain).build();
jpaTm().insert(historyEntry);
jpaTm().insert(autorenewPollMessage);
jpaTm().insert(billEvent);
jpaTm().insert(deletePollMessage);
jpaTm().insert(domain);
});
// Store the existing BillingRecurrence VKey. This happens after the event has been persisted.
DomainBase persisted = jpaTm().transact(() -> jpaTm().load(domain.createVKey()));
// Verify that the domain data has been persisted.
// dsData still isn't persisted. gracePeriods appears to have the same values but for some
// reason is showing up as different.
assertEqualDomainExcept(persisted, "creationTime", "dsData", "gracePeriods");
// Verify that the DomainContent object from the history record sets the fields correctly.
DomainHistory persistedHistoryEntry =
jpaTm().transact(() -> jpaTm().load(historyEntry.createVKey()));
assertThat(persistedHistoryEntry.getDomainContent().get().getAutorenewPollMessage())
.isEqualTo(domain.getAutorenewPollMessage());
assertThat(persistedHistoryEntry.getDomainContent().get().getAutorenewBillingEvent())
.isEqualTo(domain.getAutorenewBillingEvent());
assertThat(persistedHistoryEntry.getDomainContent().get().getDeletePollMessage())
.isEqualTo(domain.getDeletePollMessage());
}
private <T> VKey<T> createLegacyVKey(Class<T> clazz, long id) {
return VKey.create(
clazz, id, Key.create(Key.create(EntityGroupRoot.class, "per-tld"), clazz, id));
}
private void assertEqualDomainExcept(DomainBase thatDomain, String... excepts) { private void assertEqualDomainExcept(DomainBase thatDomain, String... excepts) {
// Fix DS data, since we can't persist it yet. // Fix DS data, since we can't persist it yet.
thatDomain = thatDomain =

View file

@ -67,6 +67,7 @@ public class PollMessageTest extends EntityTestCase {
.build()); .build());
oneTime = oneTime =
new PollMessage.OneTime.Builder() new PollMessage.OneTime.Builder()
.setId(100L)
.setClientId("TheRegistrar") .setClientId("TheRegistrar")
.setEventTime(fakeClock.nowUtc()) .setEventTime(fakeClock.nowUtc())
.setMsg("Test poll message") .setMsg("Test poll message")
@ -74,6 +75,7 @@ public class PollMessageTest extends EntityTestCase {
.build(); .build();
autoRenew = autoRenew =
new PollMessage.Autorenew.Builder() new PollMessage.Autorenew.Builder()
.setId(200L)
.setClientId("TheRegistrar") .setClientId("TheRegistrar")
.setEventTime(fakeClock.nowUtc()) .setEventTime(fakeClock.nowUtc())
.setMsg("Test poll message") .setMsg("Test poll message")

View file

@ -58,3 +58,4 @@ V57__history_null_content.sql
V58__drop_default_value_and_sequences_for_billing_event.sql V58__drop_default_value_and_sequences_for_billing_event.sql
V59__use_composite_primary_key_for_contact_and_host_history_table.sql V59__use_composite_primary_key_for_contact_and_host_history_table.sql
V60__remove_pollmessage_sequence.sql V60__remove_pollmessage_sequence.sql
V61__domain_hist_columns.sql

View file

@ -0,0 +1,39 @@
-- Copyright 2020 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http:--www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- These history ids are technically foreign keys, we don't want to constrain
-- them because they're temporary and we don't need to because they are
-- already indirectly constrained by the relationships with the
-- PollMessages/BillingEvents involved.
ALTER TABLE "Domain" ADD COLUMN billing_recurrence_history_id int8;
ALTER TABLE "Domain" ADD COLUMN autorenew_poll_message_history_id int8;
ALTER TABLE "Domain" ADD COLUMN deletion_poll_message_history_id int8;
ALTER TABLE "DomainHistory" ADD COLUMN billing_recurrence_history_id int8;
ALTER TABLE "DomainHistory" ADD COLUMN autorenew_poll_message_history_id int8;
ALTER TABLE "DomainHistory" ADD COLUMN deletion_poll_message_history_id int8;
-- Drop and re-add DomainHistory's Domain FK constraint to make it deferrable,
-- this breaks a cycle with Domain -> PollMessage|BillingEvent -> History.
ALTER TABLE "DomainHistory" DROP CONSTRAINT fk_domain_history_domain_repo_id;
ALTER TABLE "DomainHistory"
ADD CONSTRAINT fk_domain_history_domain_repo_id
FOREIGN KEY (domain_repo_id) REFERENCES "Domain"(repo_id)
DEFERRABLE INITIALLY DEFERRED;
-- Same for PollMessage -> Domain, breaking Domain -> PollMessage -> Domain.
ALTER TABLE "PollMessage" DROP CONSTRAINT fk_poll_message_domain_repo_id;
ALTER TABLE "PollMessage"
ADD CONSTRAINT fk_poll_message_domain_repo_id
FOREIGN KEY (domain_repo_id) REFERENCES "Domain"(repo_id)
DEFERRABLE INITIALLY DEFERRED;

View file

@ -249,10 +249,13 @@ create sequence temp_history_id_sequence start 1 increment 50;
auth_info_repo_id text, auth_info_repo_id text,
auth_info_value text, auth_info_value text,
billing_recurrence_id int8, billing_recurrence_id int8,
billing_recurrence_history_id int8,
autorenew_end_time timestamptz, autorenew_end_time timestamptz,
autorenew_poll_message_id int8, autorenew_poll_message_id int8,
autorenew_poll_message_history_id int8,
billing_contact text, billing_contact text,
deletion_poll_message_id int8, deletion_poll_message_id int8,
deletion_poll_message_history_id int8,
domain_name text, domain_name text,
idn_table_name text, idn_table_name text,
last_transfer_time timestamptz, last_transfer_time timestamptz,
@ -301,10 +304,13 @@ create sequence temp_history_id_sequence start 1 increment 50;
auth_info_repo_id text, auth_info_repo_id text,
auth_info_value text, auth_info_value text,
billing_recurrence_id int8, billing_recurrence_id int8,
billing_recurrence_history_id int8,
autorenew_end_time timestamptz, autorenew_end_time timestamptz,
autorenew_poll_message_id int8, autorenew_poll_message_id int8,
autorenew_poll_message_history_id int8,
billing_contact text, billing_contact text,
deletion_poll_message_id int8, deletion_poll_message_id int8,
deletion_poll_message_history_id int8,
domain_name text, domain_name text,
idn_table_name text, idn_table_name text,
last_transfer_time timestamptz, last_transfer_time timestamptz,

View file

@ -353,7 +353,10 @@ CREATE TABLE public."Domain" (
billing_recurrence_id bigint, billing_recurrence_id bigint,
autorenew_poll_message_id bigint, autorenew_poll_message_id bigint,
deletion_poll_message_id bigint, deletion_poll_message_id bigint,
autorenew_end_time timestamp with time zone autorenew_end_time timestamp with time zone,
billing_recurrence_history_id bigint,
autorenew_poll_message_history_id bigint,
deletion_poll_message_history_id bigint
); );
@ -420,7 +423,10 @@ CREATE TABLE public."DomainHistory" (
autorenew_end_time timestamp with time zone, autorenew_end_time timestamp with time zone,
history_other_registrar_id text, history_other_registrar_id text,
history_period_unit text, history_period_unit text,
history_period_value integer history_period_value integer,
billing_recurrence_history_id bigint,
autorenew_poll_message_history_id bigint,
deletion_poll_message_history_id bigint
); );
@ -1768,7 +1774,7 @@ ALTER TABLE ONLY public."Domain"
-- --
ALTER TABLE ONLY public."DomainHistory" ALTER TABLE ONLY public."DomainHistory"
ADD CONSTRAINT fk_domain_history_domain_repo_id FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id); ADD CONSTRAINT fk_domain_history_domain_repo_id FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id) DEFERRABLE INITIALLY DEFERRED;
-- --
@ -1904,7 +1910,7 @@ ALTER TABLE ONLY public."PollMessage"
-- --
ALTER TABLE ONLY public."PollMessage" ALTER TABLE ONLY public."PollMessage"
ADD CONSTRAINT fk_poll_message_domain_repo_id FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id); ADD CONSTRAINT fk_poll_message_domain_repo_id FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id) DEFERRABLE INITIALLY DEFERRED;
-- --