IPv6-Zugriff auf Hetzner-Server

Ein von uns gemieteter vServer von Hetzner hatte keine IPv6-Konfiguration. Dieses kann man nachholen indem man folgende Schritte ausführt:

  • Vom Hetzner Robot muss der IPv6-Adressenbereich ermittelt werden
  • Als IPv6-Adresse verwenden wir den Adressenbereich mit ::2 am Ende
  • Diese IP-Adresse muss beim Domänen-Registrar mit dem gewünschten Host-Namen eingetragen werden
  • Der Host-Name muss auf der Robot-Seite von Hetzner eingetragen werden
  • Damit der Server dann auch von Außen über IPv6 erreichbar ist, muss folgender Befehl als Administrator auf dem Windows-Server ausgeführt werden:
netsh interface ipv6 set global randomizeidentifiers=disable

Jetzt ist der Server auch über IPv6 erreichbar.

Hier noch ein Bild, wo die IPv6-Informationen im Robot gefunden werden können:

hetzner-robot

Den Befehl zum Ausschalten der randomize­identifiers-Funktion habe ich dankenswerterweise im Blog von wydler.de gefunden.

Advertisements

SQL-Server: Zyklische Referenzen und mehrere Kaskaden-Pfade

Bei der Umstellung einer Datenbank auf SQL-Server haben wir folgenden Fehler erhalten:

Msg 1785, Level 16, State 0, Line 3
Introducing FOREIGN KEY constraint 'FK_Table' on table 'Table' may cause cycles or multiple cascade paths.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Msg 1750, Level 16, State 0, Line 3
Could not create constraint. See previous errors.

Dieser Fehler bedeutet, dass folgende Szenarien im SQL-Server mit ON DELETE CASCADE nicht funktionieren:

Featured image

Mehrere Kaskaden-Pfade

Das erste Bild zeigt zwei Tabellen, die jeweils aufeinander verweisen (zyklische Referenzen), während das zweite Bild eine rautenförmige Datenbank-Struktur aufweist, bei der es mehrere Pfade geben kann, wie die Enkelkind-Tabelle von der Vater-Tabelle erreicht werden kann.

Der einzige Weg, wie diese Restriktion zu umgehen ist, ist „ON DELETE NO ACTION“ zusammen mit INSTEAD OF DELETE-Triggern zu verwenden. Man findet einige Ressourcen im Internet zu diesem Problem, wie z.B.:

Hier eine Zusammenfassung für die Lösung:

  • Rekursive Trigger müssen für die Datenbank angeschaltet werden:
    ALTER DATABASE [db_name] SET RECURSIVE_TRIGGERS ON
  • Kein SET NOCOUNT ON
  • Ein Überprüfung, ob überhaupt Datensätze zu bearbeiten sind mit:
    IF @@rowcount = 0 RETURN
  • Kapseln der DELETE-Befehle in einer Transaktion
  • Kapseln der DELETE-Befehle (außer des DELETE-Befehls für die Datensätze in der DELETED-Tabelle) in einem
    IF TRIGGER_NESTLEVEL() < 30
  • Nach jedem Befehl prüfen, ob @@ERROR <> 0 ist und dann die Transaktion zurücksetzen und den Trigger beenden
  • Wenn der zu löschende Datensatz Verweise auf andere Datensätze der gleichen Tabelle hat, dann müssen diese von allen DELETE-Befehlen als vorletztes ausgeführt werden
  • Der DELETE-Befehl für die zu löschenden Datensätze in der DELETED-Tabelle muss als letztes ausgeführt werden

Hier ein kleines Beispiel wie der Trigger aussehen könnte:

CREATE TRIGGER [DELETE_MyTable] ON dbo.[MyTable] INSTEAD OF DELETE
AS 
BEGIN
 -- Dieser Trigger wird auch aufgerufen, wenn keine Datensätze zu löschen sind
 IF @@rowcount = 0
  RETURN
 -- Bei einem Fehler die Daten alle wiederherstellen
 BEGIN TRAN
  -- Zu tiefe Rekursionen vermeiden
  IF TRIGGER_NESTLEVEL() < 30
  BEGIN
   -- Datensätze aus SubTable1 löschen
   DELETE t1 FROM [SubTable1] AS t1 WHERE EXISTS (SELECT * FROM DELETED [t2] WHERE [t1].MyTableID=[t2].MyTableID)
   IF (@@ERROR <> 0) GOTO PROBLEM
   -- Datensätze aus SubTable2 löschen
   DELETE t1 FROM [SubTable2] AS t1 WHERE EXISTS (SELECT * FROM DELETED [t2] WHERE [t1].MyTableID=[t2].MyTableID)
   IF (@@ERROR <> 0) GOTO PROBLEM
   -- Datensätze aus MyTable löschen auf die der Wert der Spalte OldMyTableID der zu löschenden Datensätze verweist
   DELETE t1 FROM [MyTable] AS t1 WHERE EXISTS (SELECT * FROM DELETED [t2] WHERE [t1].MyTableID=[t2].OldMyTableID)
   IF (@@ERROR <> 0) GOTO PROBLEM
  END
  -- Datensätze aus der DELETED-Tabelle löschen
  DELETE t1 FROM [MyTable] AS t1 WHERE EXISTS (SELECT * FROM DELETED [t2] WHERE [t1].MyTableID=[t2].MyTableID)
  IF (@@ERROR <> 0) GOTO PROBLEM
 COMMIT TRAN
 RETURN
PROBLEM:
 ROLLBACK TRAN
END

Ich bevorzuge hier die DELETE WHERE EXISTS-Syntax, weil diese auch mit zusammengesetzten Primärschlüsseln funktioniert.