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