]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Some more poking at schema stuff, on the road towards a more portable table-modificat...
authorBrion Vibber <brion@pobox.com>
Fri, 8 Oct 2010 01:33:02 +0000 (18:33 -0700)
committerBrion Vibber <brion@pobox.com>
Fri, 8 Oct 2010 01:33:02 +0000 (18:33 -0700)
lib/mysqlschema.php
lib/pgsqlschema.php
lib/schema.php

index 696b2b8d2a0ac0d9815775dc8d89cd157d6e2c07..0a5545286b092dbf47d4a3021205bb2151276707 100644 (file)
@@ -322,31 +322,6 @@ class MysqlSchema extends Schema
         return true;
     }
 
-    /**
-     * Look over a list of column definitions and list up which
-     * indices will be present
-     */
-    private function _indexList(array $columns)
-    {
-        $list = array('uniques' => array(),
-                      'primary' => array(),
-                      'indices' => array());
-        foreach ($columns as $cd) {
-            switch ($cd->key) {
-            case 'UNI':
-                $list['uniques'][] = $cd->name;
-                break;
-            case 'PRI':
-                $list['primary'][] = $cd->name;
-                break;
-            case 'MUL':
-                $list['indices'][] = $cd->name;
-                break;
-            }
-        }
-        return $list;
-    }
-
     /**
      * Get the unique index key name for a given column on this table
      */
index 2d0f60983618a87ebaa8026b72599e3602faa441..585d05d615aabbcea5f0d6c3d192537f40e05a1b 100644 (file)
@@ -208,93 +208,6 @@ class PgsqlSchema extends Schema
         return true;
     }
 
-
-    /**
-     * Ensures that a table exists with the given
-     * name and the given column definitions.
-     *
-     * If the table does not yet exist, it will
-     * create the table. If it does exist, it will
-     * alter the table to match the column definitions.
-     *
-     * @param string $tableName name of the table
-     * @param array  $columns   array of ColumnDef
-     *                          objects for the table
-     *
-     * @return boolean success flag
-     */
-
-    public function ensureTable($tableName, $columns)
-    {
-        // XXX: DB engine portability -> toilet
-
-        try {
-            $td = $this->getTableDef($tableName);
-            
-        } catch (Exception $e) {
-            if (preg_match('/no such table/', $e->getMessage())) {
-                return $this->createTable($tableName, $columns);
-            } else {
-                throw $e;
-            }
-        }
-
-        $cur = $this->_names($td->columns);
-        $new = $this->_names($columns);
-
-        $toadd  = array_diff($new, $cur);
-        $todrop = array_diff($cur, $new);
-        $same   = array_intersect($new, $cur);
-        $tomod  = array();
-        foreach ($same as $m) {
-            $curCol = $this->_byName($td->columns, $m);
-            $newCol = $this->_byName($columns, $m);
-            
-
-            if (!$newCol->equals($curCol)) {
-            // BIG GIANT TODO!
-            // stop it detecting different types and trying to modify on every page request
-//                 $tomod[] = $newCol->name;
-            }
-        }
-        if (count($toadd) + count($todrop) + count($tomod) == 0) {
-            // nothing to do
-            return true;
-        }
-
-        // For efficiency, we want this all in one
-        // query, instead of using our methods.
-
-        $phrase = array();
-
-        foreach ($toadd as $columnName) {
-            $cd = $this->_byName($columns, $columnName);
-
-            $phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
-        }
-
-        foreach ($todrop as $columnName) {
-            $phrase[] = 'DROP COLUMN ' . $columnName;
-        }
-
-        foreach ($tomod as $columnName) {
-            $cd = $this->_byName($columns, $columnName);
-
-       /* brute force */
-            $phrase[] = 'DROP COLUMN ' . $columnName;
-            $phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
-        }
-
-        $sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
-        $res = $this->conn->query($sql);
-
-        if (PEAR::isError($res)) {
-            throw new Exception($res->getMessage());
-        }
-
-        return true;
-    }
-
     /**
      * Return the proper SQL for creating or
      * altering a column.
@@ -326,6 +239,49 @@ class PgsqlSchema extends Schema
         return implode(' ', $line);
     }
 
+    /**
+     * Append phrase(s) to an array of partial ALTER TABLE chunks in order
+     * to alter the given column from its old state to a new one.
+     *
+     * @param array $phrase
+     * @param string $columnName
+     * @param array $old previous column definition as found in DB
+     * @param array $cd current column definition
+     */
+    function appendAlterModifyColumn(array &$phrase, $columnName, array $old, array $cd)
+    {
+        $prefix = 'ALTER COLUMN ' . $this->quoteIdentifier($columnName) . ' ';
+
+        $oldType = $this->mapType($old);
+        $newType = $this->mapType($cd);
+        if ($oldType != $newType) {
+            $phrase[] .= $prefix . 'TYPE ' . $newType;
+        }
+
+        if (!empty($old['not null']) && empty($cd['not null'])) {
+            $phrase[] .= $prefix . 'DROP NOT NULL';
+        } else if (empty($old['not null']) && !empty($cd['not null'])) {
+            $phrase[] .= $prefix . 'SET NOT NULL';
+        }
+
+        if (isset($old['default']) && !isset($cd['default'])) {
+            $phrase[] . $prefix . 'DROP DEFAULT';
+        } else if (!isset($old['default']) && isset($cd['default'])) {
+            $phrase[] . $prefix . 'SET DEFAULT ' . $this->quoteDefaultValue($cd);
+        }
+    }
+
+    /**
+     * Quote a db/table/column identifier if necessary.
+     *
+     * @param string $name
+     * @return string
+     */
+    function quoteIdentifier($name)
+    {
+        return '"' . $name . '"';
+    }
+
     function mapType($column)
     {
         $map = array('serial' => 'bigserial', // FIXME: creates the wrong name for the sequence for some internal sequence-lookup function, so better fix this to do the real 'create sequence' dance.
index 5868627ed74e7ff5bfb02bfbd024d249c3171d65..60d3324ad68cd90be5ebf51bda6e1cdaf808c06c 100644 (file)
@@ -345,12 +345,12 @@ class Schema
      * @return boolean success flag
      */
 
-    public function ensureTable($tableName, $columns)
+    public function ensureTable($tableName, $def)
     {
         // XXX: DB engine portability -> toilet
 
         try {
-            $td = $this->getTableDef($tableName);
+            $old = $this->getTableDef($tableName);
         } catch (Exception $e) {
             if (preg_match('/no such table/', $e->getMessage())) {
                 return $this->createTable($tableName, $columns);
@@ -359,20 +359,22 @@ class Schema
             }
         }
 
-        $cur = $this->_names($td->columns);
-        $new = $this->_names($columns);
+        $cur = array_keys($old['fields']);
+        $new = array_keys($def['fields']);
 
         $toadd  = array_diff($new, $cur);
         $todrop = array_diff($cur, $new);
         $same   = array_intersect($new, $cur);
         $tomod  = array();
 
-        foreach ($same as $m) {
-            $curCol = $this->_byName($td->columns, $m);
-            $newCol = $this->_byName($columns, $m);
+        // Find which fields have actually changed definition
+        // in a way that we need to tweak them for this DB type.
+        foreach ($same as $name) {
+            $curCol = $old['fields'][$name];
+            $newCol = $cur['fields'][$name];
 
-            if (!$newCol->equals($curCol)) {
-                $tomod[] = $newCol->name;
+            if (!$this->columnsEqual($curCol, $newCol)) {
+                $tomod[] = $name;
             }
         }
 
@@ -387,19 +389,18 @@ class Schema
         $phrase = array();
 
         foreach ($toadd as $columnName) {
-            $cd = $this->_byName($columns, $columnName);
-
-            $phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
+            $this->appendAlterAddColumn($phrase, $columnName,
+                    $def['fields'][$columnName]);
         }
 
         foreach ($todrop as $columnName) {
-            $phrase[] = 'DROP COLUMN ' . $columnName;
+            $this->appendAlterModifyColumn($phrase, $columnName,
+                    $old['fields'][$columnName],
+                    $def['fields'][$columnName]);
         }
 
         foreach ($tomod as $columnName) {
-            $cd = $this->_byName($columns, $columnName);
-
-            $phrase[] = 'MODIFY COLUMN ' . $this->_columnSql($cd);
+            $this->appendAlterDropColumn($phrase, $columnName);
         }
 
         $sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
@@ -413,6 +414,90 @@ class Schema
         return true;
     }
 
+    /**
+     * Append phrase(s) to an array of partial ALTER TABLE chunks in order
+     * to add the given column definition to the table.
+     *
+     * @param array $phrase
+     * @param string $columnName
+     * @param array $cd 
+     */
+    function appendAlterAddColumn(array &$phrase, $columnName, array $cd)
+    {
+        $phrase[] = 'ADD COLUMN ' .
+                    $this->quoteIdentifier($columnName) .
+                    ' ' .
+                    $this->columnSql($cd);
+    }
+
+    /**
+     * Append phrase(s) to an array of partial ALTER TABLE chunks in order
+     * to alter the given column from its old state to a new one.
+     *
+     * @param array $phrase
+     * @param string $columnName
+     * @param array $old previous column definition as found in DB
+     * @param array $cd current column definition
+     */
+    function appendAlterModifyColumn(array &$phrase, $columnName, array $old, array $cd)
+    {
+        $phrase[] = 'MODIFY COLUMN ' .
+                    $this->quoteIdentifier($columnName) .
+                    ' ' .
+                    $this->columnSql($cd);
+    }
+
+    /**
+     * Append phrase(s) to an array of partial ALTER TABLE chunks in order
+     * to drop the given column definition from the table.
+     *
+     * @param array $phrase
+     * @param string $columnName
+     */
+    function appendAlterDropColumn(array &$phrase, $columnName)
+    {
+        $phrase[] = 'DROP COLUMN ' . $this->quoteIdentifier($columnName);
+    }
+
+    /**
+     * Quote a db/table/column identifier if necessary.
+     *
+     * @param string $name
+     * @return string
+     */
+    function quoteIdentifier($name)
+    {
+        return $name;
+    }
+
+    function quoteDefaultValue($cd)
+    {
+        if ($cd['type'] == 'datetime' && $cd['default'] == 'CURRENT_TIMESTAMP') {
+            return $cd['default'];
+        } else {
+            return $this->quoteValue($cd['default']);
+        }
+    }
+
+    function quoteValue($val)
+    {
+        return $this->conn->escape($val);
+    }
+
+    /**
+     * Check if two column definitions are equivalent.
+     * The default implementation checks _everything_ but in many cases
+     * you may be able to discard a bunch of equivalencies.
+     *
+     * @param array $a
+     * @param array $b
+     * @return boolean
+     */
+    function columnsEqual(array $a, array $b)
+    {
+        return !array_diff_assoc($a, $b) && !array_diff_assoc($b, $a);
+    }
+
     /**
      * Returns the array of names from an array of
      * ColumnDef objects.