From 8c56f9d1d7c7b0af2d34ee94ffd1cd6165210cb0 Mon Sep 17 00:00:00 2001
From: Demian Katz <demian.katz@villanova.edu>
Date: Thu, 16 Aug 2012 16:06:32 -0400
Subject: [PATCH] Progress on database upgrade (not complete yet).

---
 .../VuFind/Controller/Plugin/DbUpgrade.php    | 144 ++++++++++++------
 .../VuFind/Controller/UpgradeController.php   |  77 ++++++----
 2 files changed, 139 insertions(+), 82 deletions(-)

diff --git a/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php b/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php
index 3629d9a1b7a..34fecfa918d 100644
--- a/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php
+++ b/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php
@@ -26,7 +26,8 @@
  * @link     http://www.vufind.org  Main Page
  */
 namespace VuFind\Controller\Plugin;
-use Zend\Mvc\Controller\Plugin\AbstractPlugin;
+use Zend\Db\Adapter\Adapter as DbAdapter, Zend\Db\Metadata\Metadata as DbMetadata,
+    Zend\Mvc\Controller\Plugin\AbstractPlugin;
 
 /**
  * Zend action helper to perform database upgrades
@@ -40,6 +41,8 @@ use Zend\Mvc\Controller\Plugin\AbstractPlugin;
 class DbUpgrade extends AbstractPlugin
 {
     protected $dbCommands = array();
+    protected $adapter;
+    protected $tableInfo = false;
 
     /**
      * Given a SQL file, parse it for table creation commands.
@@ -50,7 +53,7 @@ class DbUpgrade extends AbstractPlugin
      */
     public function loadSql($file)
     {
-        $sql = file_get_contents(APPLICATION_PATH . '/module/VuFind/sql/mysql.sql');
+        $sql = file_get_contents($file);
         $statements = explode(';', $sql);
 
         // Create an array, indexed by table name, of commands necessary to create
@@ -71,32 +74,76 @@ class DbUpgrade extends AbstractPlugin
     }
 
     /**
-     * Get a list of all tables in the database.
+     * Get the database adapter.
      *
-     * @throws Exception
-     * @return array
+     * @return DbAdapter
      */
-    public function getAllTables()
+    public function getAdapter()
     {
-        /* TODO
-        // Load the default database adapter (by this point, it should be valid):
-        $db = Zend_Db_Table::getDefaultAdapter();
-        if (!is_object($db)) {
-            throw new \Exception('Could not load default database adapter.');
+        if (!is_object($this->adapter)) {
+            throw new \Exception('Database adapter not set.');
         }
+        return $this->adapter;
+    }
 
-        // Get a list of all tables in the database:
-        $results = $db->query("SHOW TABLES;");
-        $tmp = $results->fetchAll();
-        $tables = array();
-        foreach ($tmp as $current) {
-            $keys = array_keys($current);
-            $tables[] = trim(strtolower($current[$keys[0]]));
-        }
-        return $tables;
+    /**
+     * Set a database adapter.
+     *
+     * @param DbAdapter $adapter Adapter to set
+     *
+     * @return DbUpgrade
+     */
+    public function setAdapter($adapter)
+    {
+        $this->adapter = $adapter;
+        return $this;
+    }
+
+    /**
+     * Execute a query.
+     *
+     * @param string $sql SQL to run
+     *
+     * @return void
+     */
+    public function query($sql)
+    {
+        /* TODO
+        $this->getAdapter()->query($sql, DbAdapter::QUERY_MODE_EXECUTE);
          */
     }
 
+    /**
+     * Load table metadata.
+     *
+     * @param bool $reload Force a reload? (Default is false).
+     *
+     * @return array
+     */
+    protected function getTableInfo($reload = false)
+    {
+        if ($reload || !$this->tableInfo) {
+            $metadata = new DbMetadata($this->getAdapter());
+            $tables = $metadata->getTables();
+            $this->tableInfo = array();
+            foreach ($tables as $current) {
+                $this->tableInfo[$current->getName()] = $current;
+            }
+        }
+        return $this->tableInfo;
+    }
+
+    /**
+     * Get a list of all tables in the database.
+     *
+     * @throws Exception
+     * @return array
+     */
+    protected function getAllTables()
+    {
+        return array_keys($this->getTableInfo());
+    }
+
     /**
      * Get information on all columns in a table, keyed by column name.
      *
@@ -105,24 +152,15 @@ class DbUpgrade extends AbstractPlugin
      * @throws Exception
      * @return array
      */
-    public function getTableColumns($table)
+    protected function getTableColumns($table)
     {
-        /* TODO
-        // Load the default database adapter (by this point, it should be valid):
-        $db = Zend_Db_Table::getDefaultAdapter();
-        if (!is_object($db)) {
-            throw new \Exception('Could not load default database adapter.');
-        }
-
-        // Get a list of all tables in the database:
-        $results = $db->query("DESCRIBE `$table`;");
-        $tmp = $results->fetchAll();
-        $columns = array();
-        foreach ($tmp as $current) {
-            $columns[trim(strtolower($current['Field']))] = $current;
+        $info = $this->getTableInfo();
+        $columns = isset($info[$table]) ? $info[$table]->getColumns() : array();
+        $retVal = array();
+        foreach ($columns as $current) {
+            $retVal[strtolower($current->getName())] = $current;
         }
-        return $columns;
-         */
+        return $retVal;
     }
 
     /**
@@ -148,16 +186,15 @@ class DbUpgrade extends AbstractPlugin
     /**
      * Create missing tables based on the output of getMissingTables().
      *
-     * @param array  $tables Output of getMissingTables()
-     * @param object $db     Database connection with CREATE permission
+     * @param array $tables Output of getMissingTables()
      *
      * @throws Exception
      * @return void
      */
-    public function createMissingTables($tables, $db)
+    public function createMissingTables($tables)
     {
         foreach ($tables as $table) {
-            $db->query($this->dbCommands[$table][0]);
+            $this->query($this->dbCommands[$table][0]);
         }
     }
 
@@ -211,13 +248,14 @@ class DbUpgrade extends AbstractPlugin
      */
     public function getModifiedColumns()
     {
+        /* TODO
         $missing = array();
         foreach ($this->dbCommands as $table => $sql) {
             // Parse column names out of the CREATE TABLE SQL, which will always be
             // the first entry in the array; we assume the standard mysqldump
             // formatting is used here.
             preg_match_all(
-                '/^  `([^`]*)`\s+([^\s,]+)[\s,]+.*$/m', $sql[0],
+                '/^  `([^`]*)`\s+([^\s,]+)[\t ,]+.*$/m', $sql[0],
                 $matches
             );
             $expectedColumns = array_map('strtolower', $matches[1]);
@@ -236,7 +274,16 @@ class DbUpgrade extends AbstractPlugin
             // Now check for mismatched types:
             $actualColumns = $this->getTableColumns($table);
             foreach ($expectedColumns as $i => $column) {
-                if ($actualColumns[$column]['Type'] != $expectedTypes[$i]) {
+                $type = $actualColumns[$column]->getDataType();
+                $charLen = $actualColumns[$column]->getCharacterMaximumLength();
+                if ($charLen) {
+                    $type .= '(' . $charLen . ')';
+                }
+                $precision = $actualColumns[$column]->getNumericPrecision();
+                if ($precision) {
+                    $type .= '(' . $precision . ')';
+                }
+                if ($type != $expectedTypes[$i]) {
                     if (!isset($missing[$table])) {
                         $missing[$table] = array();
                     }
@@ -245,22 +292,22 @@ class DbUpgrade extends AbstractPlugin
             }
         }
         return $missing;
+         */
     }
 
     /**
      * Create missing columns based on the output of getMissingColumns().
      *
-     * @param array  $columns Output of getMissingColumns()
-     * @param object $db      Database connection with ALTER permission
+     * @param array $columns Output of getMissingColumns()
      *
      * @throws Exception
      * @return void
      */
-    public function createMissingColumns($columns, $db)
+    public function createMissingColumns($columns)
     {
         foreach ($columns as $table => $sql) {
             foreach ($sql as $column) {
-                $db->query("ALTER TABLE `{$table}` ADD COLUMN {$column}");
+                $this->query("ALTER TABLE `{$table}` ADD COLUMN {$column}");
             }
         }
     }
@@ -269,16 +316,15 @@ class DbUpgrade extends AbstractPlugin
      * Modify columns based on the output of getModifiedColumns().
      *
      * @param array  $columns Output of getModifiedColumns()
-     * @param object $db      Database connection with ALTER permission
      *
      * @throws Exception
      * @return void
      */
-    public function updateModifiedColumns($columns, $db)
+    public function updateModifiedColumns($columns)
     {
         foreach ($columns as $table => $sql) {
             foreach ($sql as $column) {
-                $db->query("ALTER TABLE `{$table}` MODIFY COLUMN {$column}");
+                $this->query("ALTER TABLE `{$table}` MODIFY COLUMN {$column}");
             }
         }
     }
diff --git a/module/VuFind/src/VuFind/Controller/UpgradeController.php b/module/VuFind/src/VuFind/Controller/UpgradeController.php
index 4ea41a43e51..986179925ca 100644
--- a/module/VuFind/src/VuFind/Controller/UpgradeController.php
+++ b/module/VuFind/src/VuFind/Controller/UpgradeController.php
@@ -28,8 +28,9 @@
 namespace VuFind\Controller;
 use ArrayObject, VuFind\Cache\Manager as CacheManager,
     VuFind\Config\Reader as ConfigReader, VuFind\Cookie\Container as CookieContainer,
-    VuFind\Db\Table\Resource as ResourceTable,
+    VuFind\Db\AdapterFactory, VuFind\Db\Table\Resource as ResourceTable,
     VuFind\Exception\RecordMissing as RecordMissingException, VuFind\Record,
+    Zend\Db\TableGateway\Feature\GlobalAdapterFeature as DbGlobalAdapter,
     Zend\Session\Container as SessionContainer;
 
 /**
@@ -160,6 +161,18 @@ class UpgradeController extends AbstractBase
         }
     }
 
+    /**
+     * Get a database adapter for root access using credentials in session.
+     *
+     * @return \Zend\Db\Adapter\Adapter
+     */
+    protected function getRootDbAdapter()
+    {
+        return AdapterFactory::getAdapter(
+            $this->session->dbRootUser, $this->session->dbRootPass
+        );
+    }
+
     /**
      * Upgrade the database.
      *
@@ -167,32 +180,33 @@ class UpgradeController extends AbstractBase
      */
     public function fixdatabaseAction()
     {
-        /* TODO
         try {
             // Set up the helper with information from our SQL file:
-            $this->_helper->dbUpgrade->loadSql(APPLICATION_PATH . '/sql/mysql.sql');
+            $this->dbUpgrade()
+                ->setAdapter(DbGlobalAdapter::getStaticAdapter())
+                ->loadSql(APPLICATION_PATH . '/module/VuFind/sql/mysql.sql');
 
+            /* TODO
             // Check for missing tables.  Note that we need to finish dealing with
             // missing tables before we proceed to the missing columns check, or else
             // the missing tables will cause fatal errors during the column test.
-            $missingTables = $this->_helper->dbUpgrade->getMissingTables();
+            $missingTables = $this->dbUpgrade()->getMissingTables();
             if (!empty($missingTables)) {
                 if (!isset($this->session->dbRootUser)
                     || !isset($this->session->dbRootPass)
                 ) {
                     return $this->forwardTo('Upgrade', 'GetDbCredentials');
                 }
-                $db = VF_DB::connect(
-                    $this->session->dbRootUser, $this->session->dbRootPass
-                );
-                $this->_helper->dbUpgrade->createMissingTables($missingTables, $db);
+                $db = $this->getRootDbAdapter();
+                $this->dbUpgrade()->setAdapter($db)
+                    ->createMissingTables($missingTables);
                 $this->session->warnings->append(
-                    "Created missing table(s): " . implode(', ', $missingTables
+                    "Created missing table(s): " . implode(', ', $missingTables)
                 );
             }
 
             // Check for missing columns.
-            $missingCols = $this->_helper->dbUpgrade->getMissingColumns();
+            $missingCols = $this->dbUpgrade()->getMissingColumns();
             if (!empty($missingCols)) {
                 if (!isset($this->session->dbRootUser)
                     || !isset($this->session->dbRootPass)
@@ -200,11 +214,10 @@ class UpgradeController extends AbstractBase
                     return $this->forwardTo('Upgrade', 'GetDbCredentials');
                 }
                 if (!isset($db)) {  // connect to DB if not already connected
-                    $db = VF_DB::connect(
-                        $this->session->dbRootUser, $this->session->dbRootPass
-                    );
+                    $db = $this->getRootDbAdapter();
                 }
-                $this->_helper->dbUpgrade->createMissingColumns($missingCols, $db);
+                $this->dbUpgrade()->setAdapter($db)
+                    ->createMissingColumns($missingCols);
                 $this->session->warnings->append(
                     "Added column(s) to table(s): "
                     . implode(', ', array_keys($missingCols))
@@ -212,7 +225,7 @@ class UpgradeController extends AbstractBase
             }
 
             // Check for modified columns.
-            $modifiedCols = $this->_helper->dbUpgrade->getModifiedColumns();
+            $modifiedCols = $this->dbUpgrade()->getModifiedColumns();
             if (!empty($modifiedCols)) {
                 if (!isset($this->session->dbRootUser)
                     || !isset($this->session->dbRootPass)
@@ -220,11 +233,10 @@ class UpgradeController extends AbstractBase
                     return $this->forwardTo('Upgrade', 'GetDbCredentials');
                 }
                 if (!isset($db)) {  // connect to DB if not already connected
-                    $db = VF_DB::connect(
-                        $this->session->dbRootUser, $this->session->dbRootPass
-                    );
+                    $db = $this->getRootDbAdapter();
                 }
-                $this->_helper->dbUpgrade->updateModifiedColumns($modifiedCols, $db);
+                $this->dbUpgrade()->setAdapter($db)
+                    ->updateModifiedColumns($modifiedCols);
                 $this->session->warnings->append(
                     "Modified column(s) in table(s): "
                     . implode(', ', array_keys($modifiedCols))
@@ -241,6 +253,7 @@ class UpgradeController extends AbstractBase
                 $this->view->anonymousCount = $anonymousTags;
                 return $this->forwardTo('Upgrade', 'FixAnonymousTags');
             }
+             */
         } catch (\Exception $e) {
             $this->flashMessenger()->setNamespace('error')
                 ->addMessage('Database upgrade failed: ' . $e->getMessage());
@@ -249,7 +262,6 @@ class UpgradeController extends AbstractBase
 
         $this->cookie->databaseOkay = true;
         return $this->forwardTo('Upgrade', 'Home');
-         */
     }
 
     /**
@@ -259,18 +271,18 @@ class UpgradeController extends AbstractBase
      */
     public function getdbcredentialsAction()
     {
-        /* TODO
-        $this->view->dbrootuser = $this->_request->getParam('dbrootuser', 'root');
+        $dbrootuser = $this->params()->fromPost('dbrootuser', 'root');
 
         // Process form submission:
-        if (strlen($this->_request->getParam('submit', '')) > 0) {
-            $pass = $this->_request->getParam('dbrootpass');
+        if (strlen($this->params()->fromPost('submit', '')) > 0) {
+            $pass = $this->params()->fromPost('dbrootpass');
 
             // Test the connection:
             try {
-                $db = VF_DB::connect($this->view->dbrootuser, $pass);
-                $db->query("SELECT * FROM user;");  // query a table known to exist
-                $this->session->dbRootUser = $this->view->dbrootuser;
+                // Query a table known to exist
+                $db = AdapterFactory::getAdapter($dbrootuser, $pass);
+                $db->query("SELECT * FROM user;");
+                $this->session->dbRootUser = $dbrootuser;
                 $this->session->dbRootPass = $pass;
                 return $this->forwardTo('Upgrade', 'FixDatabase');
             } catch (\Exception $e) {
@@ -278,7 +290,8 @@ class UpgradeController extends AbstractBase
                     ->addMessage('Could not connect; please try again.');
             }
         }
-         */
+
+        return $this->createViewModel(array('dbrootuser' => $dbrootuser));
     }
 
     /**
@@ -290,14 +303,14 @@ class UpgradeController extends AbstractBase
     {
         /* TODO
         // Handle skip action:
-        if (strlen($this->_request->getParam('skip', '')) > 0) {
+        if (strlen($this->params()->fromPost('skip', '')) > 0) {
             $this->cookie->skipAnonymousTags = true;
             return $this->forwardTo('Upgrade', 'FixDatabase');
         }
 
         // Handle submit action:
-        if (strlen($this->_request->getParam('submit', '')) > 0) {
-            $user = $this->_request->getParam('username');
+        if (strlen($this->params()->fromPost('submit', '')) > 0) {
+            $user = $this->params()->fromPost('username');
             if (empty($user)) {
                 $this->flashMessenger()->setNamespace('error')
                     ->addMessage('Username must not be empty.');
@@ -422,12 +435,10 @@ class UpgradeController extends AbstractBase
             return $this->redirect()->toRoute('upgrade-fixconfig');
         }
 
-        /* TODO
         // Now make sure the database is up to date:
         if (!isset($this->cookie->databaseOkay)) {
             return $this->redirect()->toRoute('upgrade-fixdatabase');
         }
-         */
 
         // Check for missing metadata in the resource table; note that we do a
         // redirect rather than a forward here so that a submit button clicked
-- 
GitLab