/** * Reverts a series of SQL patches by looking up their down_sql code and executing it. */ public function rollback() { $this->logger->log('Database rollback: ' . implode(', ', $this->sql_revert_patches), LOG_DEBUG); if (!count($this->sql_revert_patches)) { return; } foreach ($this->sql_revert_patches as $patch_name) { // get the code to revert the patch $result = $this->driver->query("\n SELECT id, down_sql\n FROM db_patches\n WHERE patch_name = '" . $this->driver->escape($patch_name) . "'\n ORDER BY applied_at DESC, id DESC;\n "); if (!($patch_info = $this->driver->fetchAssoc($result))) { return; } if ($patch_info['down_sql'] != '') { // mark the patch as being reverted $this->driver->query("\n UPDATE db_patches\n SET reverted_at = '" . $this->driver->escape($this->timestamp) . "'\n WHERE patch_timestamp = '" . $this->driver->escape($patch_name) . "';\n "); // revert the patch $this->driver->startTransaction(); $this->driver->multiQuery($patch_info['down_sql']); if (false === $result) { throw new DatabaseException('Error reverting patch ' . $patch_name . ': ' . $this->driver->getLastError(), 1); } $this->driver->doCommit(); } // remove the patch from the db_patches table $this->driver->query("\n DELETE FROM db_patches\n WHERE id = " . $this->driver->escape($patch_info['id']) . ";\n "); $this->logger->log("Patch '{$patch_name}' reverted."); } }