r4064 add DB triggers to prevent invalid port links (#363)
authorAaron Dummer <aaron@dummer.info>
Fri, 24 Dec 2010 20:21:52 +0000 (20:21 +0000)
committerAaron Dummer <aaron@dummer.info>
Fri, 24 Dec 2010 20:21:52 +0000 (20:21 +0000)
ChangeLog
README
inc/dictionary.php
install/init-structure.sql
upgrade.php

index e496dcf..3875416 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -11,6 +11,7 @@
        bugfix: UI: pager in ipv4net shows appropriate page when IP is highlighted
        bugfix: create IPv4 network button, if pressed to open in a new window, was redirecting the parent window, too
        bugfix: when searching for IP not belonging to any known network, the ugly assertion failed page was shown.
+       bugfix: add DB triggers to prevent invalid port links (#363, by Aaron Dummer)
        update: links to ports added to object search results
        update: custom rearch results provided by users' plugins now supported
        new feature: syncdomain.php now can create child processes to speed up 802.1Q sync
diff --git a/README b/README
index 121825c..aae9fc0 100644 (file)
--- a/README
+++ b/README
@@ -98,6 +98,11 @@ should be sufficient:
 *                                                     *
 *******************************************************
 
+*** Upgrading to 0.19.x ***
+
+This release utilizes database triggers, which are only available in
+MySQL >= 5.0.2.
+
 *** Upgrading to 0.18.x ***
 
 RackTables from its version 0.18.0 and later is not compatible with
index e12a6a4..99db7d6 100644 (file)
@@ -26,9 +26,12 @@ function isInnoDBSupported ()
        global $dbxlink;
        // create a temp table
        $dbxlink->query("CREATE TABLE `innodb_test` (`id` int) ENGINE=InnoDB");
-       $row = $dbxlink->query("SHOW TABLE STATUS LIKE 'innodb_test'")->fetch(PDO::FETCH_ASSOC);
+       $innodb_row = $dbxlink->query("SHOW TABLE STATUS LIKE 'innodb_test'")->fetch(PDO::FETCH_ASSOC);
+       // create a trigger
+       $dbxlink->query("CREATE TRIGGER `trigger_test` BEFORE INSERT ON `innodb_test` FOR EACH ROW BEGIN END");
+       $trigger_row = $dbxlink->query("SELECT COUNT(*) AS count FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = SCHEMA() AND TRIGGER_NAME = 'trigger_test'")->fetch(PDO::FETCH_ASSOC);
        $dbxlink->query("DROP TABLE `innodb_test`");
-       if ($row['Engine'] == 'InnoDB')
+       if ($innodb_row['Engine'] == 'InnoDB' && $trigger_row['count'] == 1)
                return TRUE;
 
        return FALSE;
index bd292af..e0b54e0 100644 (file)
@@ -250,6 +250,42 @@ CREATE TABLE `Link` (
   CONSTRAINT `Link-FK-b` FOREIGN KEY (`portb`) REFERENCES `Port` (`id`) ON DELETE CASCADE
 ) ENGINE=InnoDB;
 
+DELIMITER ;;
+CREATE TRIGGER `checkForDuplicateLinksBeforeInsert` BEFORE INSERT ON `Link`
+  FOR EACH ROW
+BEGIN
+  DECLARE count INTEGER;
+
+  IF NEW.porta = NEW.portb THEN
+    SET NEW.porta = NULL;
+  END IF;
+
+  SELECT COUNT(*) INTO count FROM Link WHERE porta IN (NEW.porta,NEW.portb) OR portb IN (NEW.porta,NEW.portb);
+
+  IF count > 0 THEN
+    SET NEW.porta = NULL;
+  END IF;
+END;;
+
+CREATE TRIGGER `checkForDuplicateLinksBeforeUpdate` BEFORE UPDATE ON `Link`
+  FOR EACH ROW
+BEGIN
+  DECLARE count INTEGER;
+
+  IF NEW.porta = NEW.portb THEN
+    SET NEW.porta = NULL;
+  END IF;
+
+  SELECT COUNT(*) INTO count FROM Link WHERE
+  (NEW.porta IN (porta,portb) AND NEW.porta != porta) OR
+  (NEW.portb IN (porta,portb) AND NEW.portb != portb);
+  IF count > 0 THEN
+    SET NEW.porta = NULL;
+  END IF;
+END;;
+DELIMITER ;
+
 CREATE TABLE `Molecule` (
   `id` int(10) unsigned NOT NULL auto_increment,
   PRIMARY KEY  (`id`)
index 72f86dd..52bb113 100644 (file)
@@ -756,6 +756,23 @@ CREATE TABLE `VLANValidID` (
                        $query[] = "UPDATE Config SET varvalue = '0.18.5' WHERE varname = 'DB_VERSION'";
                        break;
                case '0.19.0':
+                       if (!isInnoDBSupported ())
+                       {
+                               showFailure ("Cannot upgrade because triggers are not supported by your MySQL server.", __FILE__);
+                               die;
+                       }
+
+                       // search for invalid Links
+                       $result = $dbxlink->query ("SELECT * FROM Link WHERE porta=portb OR porta IN (SELECT portb FROM Link) OR portb IN (SELECT porta FROM Link);");
+                       $rows = $result->fetchAll (PDO::FETCH_ASSOC);
+                       if (count($rows) > 0)
+                       {
+                               foreach ($rows as $row)
+                                       $invalid_msg .= sprintf("porta: %s, portb: %s\n", $row['porta'], $row['portb']);
+                               showFailure("Cannot upgrade because one or more invalid port links exist:\n{$invalid_msg}", __FILE__);
+                               die;    
+                       }
+
                        $query[] = 'ALTER TABLE `File` ADD `thumbnail` LONGBLOB NULL AFTER `atime`';
                        $query[] = "
 CREATE TABLE `IPv6Address` (
@@ -810,6 +827,42 @@ CREATE TABLE `ObjectLog` (
   KEY `date` (`date`),
   CONSTRAINT `ObjectLog-FK-object_id` FOREIGN KEY (`object_id`) REFERENCES `RackObject` (`id`) ON DELETE CASCADE
 ) ENGINE=InnoDB
+";
+                       $query[] = "
+CREATE TRIGGER `checkForDuplicateLinksBeforeInsert` BEFORE INSERT ON `Link`
+  FOR EACH ROW
+BEGIN
+  DECLARE count INTEGER;
+
+  IF NEW.porta = NEW.portb THEN
+    SET NEW.porta = NULL;
+  END IF;
+
+  SELECT COUNT(*) INTO count FROM Link WHERE porta IN (NEW.porta,NEW.portb) OR portb IN (NEW.porta,NEW.portb);
+
+  IF count > 0 THEN
+    SET NEW.porta = NULL;
+  END IF;
+END;
+";
+                       $query[] = "
+CREATE TRIGGER `checkForDuplicateLinksBeforeUpdate` BEFORE UPDATE ON `Link`
+  FOR EACH ROW
+BEGIN
+  DECLARE count INTEGER;
+
+  IF NEW.porta = NEW.portb THEN
+    SET NEW.porta = NULL;
+  END IF;
+
+  SELECT COUNT(*) INTO count FROM Link WHERE
+  (NEW.porta IN (porta,portb) AND NEW.porta != porta) OR
+  (NEW.portb IN (porta,portb) AND NEW.portb != portb);
+  IF count > 0 THEN
+    SET NEW.porta = NULL;
+  END IF;
+END;
 ";
                        $query[] = "ALTER TABLE `TagStorage` CHANGE COLUMN `entity_realm` `entity_realm` ENUM('file','ipv4net','ipv4vs','ipv4rspool','object','rack','user','ipv6net') NOT NULL DEFAULT 'object' FIRST";
                        $query[] = "ALTER TABLE `FileLink` CHANGE COLUMN `entity_type` `entity_type` ENUM('ipv4net','ipv4rspool','ipv4vs','object','rack','user','ipv6net') NOT NULL DEFAULT 'object' AFTER `file_id`";