From ae770ff568ceb6f595e9d3b56004662804c490e7 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sat, 6 Sep 2014 22:50:26 +0000 Subject: [PATCH 01/29] reduce complexity in start method --- src/Ifsnop/Mysqldump/Mysqldump.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index ca635ce3..a5fe22d4 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -110,6 +110,8 @@ public function __construct( throw new Exception("Unexpected value in dumpSettings: (" . implode(",", $diff) . ")\n"); } + // Create a new compressManager to manage compressed output + $this->compressManager = CompressManagerFactory::create($this->dumpSettings['compress']); } /** @@ -200,9 +202,6 @@ public function start($filename = '') // Connect to database $this->connect(); - // Create a new compressManager to manage compressed output - $this->compressManager = CompressManagerFactory::create($this->dumpSettings['compress']); - $this->compressManager->open($this->fileName); // Formating dump file From 8d8003292c4e352d80afbb269dce9d39cbce6dce Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sat, 6 Sep 2014 22:53:06 +0000 Subject: [PATCH 02/29] added some @todo --- src/Ifsnop/Mysqldump/Mysqldump.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index a5fe22d4..44152e9c 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -338,6 +338,8 @@ private function getTableStructure($tablename) /** * Escape values with quotes when needed + * @todo use is_number instead of ctype_digit and intval + * @todo get column data type, use it to quote results * * @param array $arr Array of strings to be quoted * From 14cd9b181a5daf2b968bf323717bd7c86f3f64d6 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sat, 6 Sep 2014 22:53:42 +0000 Subject: [PATCH 03/29] fix newline --- src/Ifsnop/Mysqldump/Mysqldump.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 44152e9c..8025bf1d 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -146,7 +146,6 @@ public static function array_replace_recursive($array1, $array2) */ private function connect() { - // Connecting with PDO try { switch ($this->dbType) { From b3f66491dc83302f4e8a2f38a6da6d537071f41c Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sun, 7 Sep 2014 00:27:04 +0000 Subject: [PATCH 04/29] rewrite table and view dump code --- README.md | 17 +++ src/Ifsnop/Mysqldump/Mysqldump.php | 221 +++++++++++++++++++++-------- 2 files changed, 180 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index e21991c6..3b908892 100644 --- a/README.md +++ b/README.md @@ -170,9 +170,26 @@ Plain old PHP: - http://stackoverflow.com/questions/13728106/unexpectedly-hitting-php-memory-limit-with-a-single-pdo-query/13729745#13729745 - http://www.php.net/manual/en/mysqlinfo.concepts.buffering.php +## Errors + +To dump a database, you need the following privileges : + +- **SELECT** + - In order to dump table structures and data. +- **SHOW VIEW** + - If any databases has views, else you will get an error. +- **TRIGGER** + - If any table has one or more triggers. +- **LOCK TABLES** + - If "lock tables" option was enabled. + +Use **SHOW GRANTS FOR user@host;** to know what privileges user has. See the following link for more information: + +[Which are the minimum privileges required to get a backup of a MySQL database schema?](http://dba.stackexchange.com/questions/55546/which-are-the-minimum-privileges-required-to-get-a-backup-of-a-mysql-database-sc/55572#55572) ## TODO +- Write any tests. - Write unit tests. ## Contributing diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 8025bf1d..6859c294 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -201,41 +201,24 @@ public function start($filename = '') // Connect to database $this->connect(); + // Create output file $this->compressManager->open($this->fileName); - // Formating dump file + // Write some basic info to output file $this->compressManager->write($this->getHeader()); if ($this->dumpSettings['add-drop-database']) { $this->compressManager->write($this->typeAdapter->add_drop_database($this->db)); } - // Listing all tables from database - $this->tables = array(); - if (empty($this->dumpSettings['include-tables'])) { - // include all tables for now, blacklisting happens later - foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->db)) as $row) { - array_push($this->tables, current($row)); - } - } else { - // include only the tables mentioned in include-tables - foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->db)) as $row) { - if (in_array(current($row), $this->dumpSettings['include-tables'], true)) { - array_push($this->tables, current($row)); - $elem = array_search( - current($row), - $this->dumpSettings['include-tables'] - ); - unset($this->dumpSettings['include-tables'][$elem]); - } - } - } + $this->getDatabaseStructure(); - // If there still are some tables in include-tables array, that means - // that some tables weren't found. Give proper error and exit. + // If there still are some tables/views in include-tables array, + // that means that some tables or views weren't found. + // Give proper error and exit. if (0 < count($this->dumpSettings['include-tables'])) { - $table = implode(",", $this->dumpSettings['include-tables']); - throw new Exception("Table (" . $table . ") not found in database"); + $name = implode(",", $this->dumpSettings['include-tables']); + throw new Exception("Table or View (" . $name . ") not found in database"); } // Disable checking foreign keys @@ -245,21 +228,8 @@ public function start($filename = '') ); } - // Exporting tables one by one - foreach ($this->tables as $table) { - if (in_array($table, $this->dumpSettings['exclude-tables'], true)) { - continue; - } - $isTable = $this->getTableStructure($table); - if (true === $isTable && false === $this->dumpSettings['no-data']) { - $this->listValues($table); - } - } - - // Exporting views one by one - foreach ($this->views as $view) { - $this->compressManager->write($view); - } + $this->exportTables(); + $this->exportViews(); // Enable checking foreign keys if needed if ($this->dumpSettings['disable-foreign-keys-check']) { @@ -294,45 +264,146 @@ private function getHeader() } /** - * Table structure extractor. Will return true if it's a table, - * false if it's a view. + * Reads table and views names from database. + * Fills $this->tables array so they will be dumped later. * - * @param string $tablename Name of table to export + * @return null + */ + private function getDatabaseStructure() + { + // Listing all tables from database + $this->tables = array(); + if (empty($this->dumpSettings['include-tables'])) { + // include all tables for now, blacklisting happens later + foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->db)) as $row) { + array_push($this->tables, current($row)); + } + } else { + // include only the tables mentioned in include-tables + foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->db)) as $row) { + if (in_array(current($row), $this->dumpSettings['include-tables'], true)) { + array_push($this->tables, current($row)); + $elem = array_search( + current($row), + $this->dumpSettings['include-tables'] + ); + unset($this->dumpSettings['include-tables'][$elem]); + } + } + } + + // Listing all views from database + $this->views = array(); + if (empty($this->dumpSettings['include-tables'])) { + // include all views for now, blacklisting happens later + foreach ($this->dbHandler->query($this->typeAdapter->show_views($this->db)) as $row) { + array_push($this->views, current($row)); + } + } else { + // include only the tables mentioned in include-tables + foreach ($this->dbHandler->query($this->typeAdapter->show_views($this->db)) as $row) { + if (in_array(current($row), $this->dumpSettings['include-tables'], true)) { + array_push($this->views, current($row)); + $elem = array_search( + current($row), + $this->dumpSettings['include-tables'] + ); + unset($this->dumpSettings['include-tables'][$elem]); + } + } + } + } + + /** + * Exports all the tables selected from database + * + * @return null + */ + private function exportTables() + { + // Exporting tables one by one + foreach ($this->tables as $table) { + if (in_array($table, $this->dumpSettings['exclude-tables'], true)) { + continue; + } + $this->getTableStructure($table); + if (false === $this->dumpSettings['no-data']) { + $this->listValues($table); + } + } + } + + /** + * Exports all the views found in database + * + * @return null + */ + private function exportViews() + { + // Exporting views one by one + foreach ($this->views as $view) { + if (in_array($view, $this->dumpSettings['exclude-tables'], true)) { + continue; + } + $this->getViewStructure($view); + } + } + + /** + * Table structure extractor. + * + * @param string $tableName Name of table to export * @return boolean */ - private function getTableStructure($tablename) + private function getTableStructure($tableName) { - $stmt = $this->typeAdapter->show_create_table($tablename); + $stmt = $this->typeAdapter->show_create_table($tableName); foreach ($this->dbHandler->query($stmt) as $r) { if (isset($r['Create Table'])) { if (!$this->dumpSettings['no-create-info']) { $this->compressManager->write( "--\n" . - "-- Table structure for table `$tablename`\n" . + "-- Table structure for table `$tableName`\n" . "--\n\n" ); if ($this->dumpSettings['add-drop-table']) { - $this->compressManager->write("DROP TABLE IF EXISTS `$tablename`;\n\n"); + $this->compressManager->write("/*!50001 DROP TABLE IF EXISTS `$tableName`*/;\n\n"); } $this->compressManager->write($r['Create Table'] . ";\n\n"); } return true; } + throw new Exception("Error getting table structure, unknown output"); + } + } + /** + * View structure extractor. + * + * @param string $viewName Name of view to export + * @return boolean + */ + private function getViewStructure($viewName) + { + $stmt = $this->typeAdapter->show_create_view($viewName); + foreach ($this->dbHandler->query($stmt) as $r) { if (isset($r['Create View'])) { if (!$this->dumpSettings['no-create-info']) { - $view = "-- --------------------------------------------------------" . - "\n\n" . - "--\n" . - "-- Table structure for view `$tablename`\n" . - "--\n\n"; - $view .= $r['Create View'] . ";\n\n"; - $this->views[] = $view; + $this->compressManager->write( + "--\n" . + "-- Table structure for view `$viewName`\n" . + "--\n\n" + ); + if ($this->dumpSettings['add-drop-table']) { + $this->compressManager->write("/*!50001 DROP TABLE IF EXISTS `$viewName`*/;\n"); + $this->compressManager->write("/*!50001 DROP VIEW IF EXISTS `$viewName`*/;\n\n"); + } + $this->compressManager->write($r['Create View'] . ";\n\n"); } - return false; + return true; } + throw new Exception("Error getting view structure, unknown output"); } - throw new Exception("Error getting table structure, unknown output"); } /** @@ -432,6 +503,9 @@ private function listValues($tablename) if ($this->dumpSettings['lock-tables']) { $this->typeAdapter->unlock_table($tablename); } + + $this->compressManager->write("\n"); + } } @@ -614,11 +688,19 @@ public static function create($c, $dbHandler = null) $method = __NAMESPACE__ . "\\" . "TypeAdapter" . $c; return new $method($dbHandler); } - public function show_create_table($tablename) + + public function show_create_table($tableName) { return "SELECT tbl_name as 'Table', sql as 'Create Table' " . "FROM sqlite_master " . - "WHERE type='table' AND tbl_name='$tablename'"; + "WHERE type='table' AND tbl_name='$tableName'"; + } + + public function show_create_view($viewName) + { + return "SELECT tbl_name as 'View', sql as 'Create View' " . + "FROM sqlite_master " . + "WHERE type='view' AND tbl_name='$viewName'"; } public function show_tables() @@ -626,6 +708,11 @@ public function show_tables() return "SELECT tbl_name FROM sqlite_master where type='table'"; } + public function show_views() + { + return "SELECT tbl_name FROM sqlite_master where type='view'"; + } + public function start_transaction() { return "BEGIN EXCLUSIVE"; @@ -699,6 +786,11 @@ public function show_create_table($tableName) return "SHOW CREATE TABLE `$tableName`"; } + public function show_create_view($viewName) + { + return "SHOW CREATE VIEW `$viewName`"; + } + public function show_tables() { if (func_num_args() != 1) { @@ -712,6 +804,19 @@ public function show_tables() "WHERE TABLE_TYPE='BASE TABLE' AND TABLE_SCHEMA='${args[0]}'"; } + public function show_views() + { + if (func_num_args() != 1) { + return ""; + } + + $args = func_get_args(); + + return "SELECT TABLE_NAME AS tbl_name " . + "FROM INFORMATION_SCHEMA.TABLES " . + "WHERE TABLE_TYPE='VIEW' AND TABLE_SCHEMA='${args[0]}'"; + } + public function start_transaction() { return "SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ; " . From e5ad8710494939d9fd6972119fa2050194744750 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sun, 7 Sep 2014 09:28:28 +0000 Subject: [PATCH 05/29] fix return path --- src/Ifsnop/Mysqldump/Mysqldump.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 6859c294..338c4180 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -350,10 +350,10 @@ private function exportViews() } /** - * Table structure extractor. + * Table structure extractor * * @param string $tableName Name of table to export - * @return boolean + * @return null */ private function getTableStructure($tableName) { @@ -371,17 +371,17 @@ private function getTableStructure($tableName) } $this->compressManager->write($r['Create Table'] . ";\n\n"); } - return true; + return; } throw new Exception("Error getting table structure, unknown output"); } } /** - * View structure extractor. + * View structure extractor * * @param string $viewName Name of view to export - * @return boolean + * @return null */ private function getViewStructure($viewName) { @@ -400,7 +400,7 @@ private function getViewStructure($viewName) } $this->compressManager->write($r['Create View'] . ";\n\n"); } - return true; + return; } throw new Exception("Error getting view structure, unknown output"); } From 768fd49e4d76a5d265d3d294c3ba54bda4c7591f Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sun, 7 Sep 2014 09:49:44 +0000 Subject: [PATCH 06/29] added a few constants to make use easier --- src/Ifsnop/Mysqldump/Mysqldump.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 338c4180..99768236 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -32,8 +32,15 @@ */ class Mysqldump { + + // Same as mysqldump const MAXLINESIZE = 1000000; + // Available compression methods as constants + const GZIP = "Gzip"; + const BZIP2 = "Bzip2"; + const NONE = "None"; + // This can be set both on constructor or manually public $host; public $user; From 19c37561569aca31ae39b3a39899b8af2e5f8c33 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sun, 7 Sep 2014 15:34:20 +0000 Subject: [PATCH 07/29] Change \n with PHP_EOL, to make output compatible readable in windows --- src/Ifsnop/Mysqldump/Mysqldump.php | 91 +++++++++++++++++------------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 99768236..7da2f751 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -114,7 +114,7 @@ public function __construct( $diff = array_diff(array_keys($this->dumpSettings), array_keys($dumpSettingsDefault)); if (count($diff)>0) { - throw new Exception("Unexpected value in dumpSettings: (" . implode(",", $diff) . ")\n"); + throw new Exception("Unexpected value in dumpSettings: (" . implode(",", $diff) . ")"); } // Create a new compressManager to manage compressed output @@ -245,6 +245,9 @@ public function start($filename = '') ); } + // Write some stats to output file + $this->compressManager->write($this->getFooter()); + // Close output file $this->compressManager->close(); } @@ -256,20 +259,32 @@ public function start($filename = '') private function getHeader() { // Some info about software, source and time - $header = "-- mysqldump-php https://github.com/ifsnop/mysqldump-php\n" . - "--\n" . - "-- Host: {$this->host}\tDatabase: {$this->db}\n" . - "-- ------------------------------------------------------\n"; + $header = "-- mysqldump-php https://github.com/ifsnop/mysqldump-php" . PHP_EOL . + "--" . PHP_EOL . + "-- Host: {$this->host}\tDatabase: {$this->db}" . PHP_EOL . + "-- ------------------------------------------------------" . PHP_EOL; if (!empty($this->version)) { - $header .= "-- Server version \t" . $this->version . "\n"; + $header .= "-- Server version \t" . $this->version . PHP_EOL; } - $header .= "-- Date: " . date('r') . "\n\n"; + $header .= "-- Date: " . date('r') . PHP_EOL . PHP_EOL; return $header; } + /** + * Returns footer for dump file + * + * @return string + */ + private function getFooter() + { + $footer = "-- Dump completed on: " . date('r') . PHP_EOL; + + return $footer; + } + /** * Reads table and views names from database. * Fills $this->tables array so they will be dumped later. @@ -369,14 +384,14 @@ private function getTableStructure($tableName) if (isset($r['Create Table'])) { if (!$this->dumpSettings['no-create-info']) { $this->compressManager->write( - "--\n" . - "-- Table structure for table `$tableName`\n" . - "--\n\n" + "--" . PHP_EOL . + "-- Table structure for table `$tableName`" . PHP_EOL . + "--" . PHP_EOL . PHP_EOL ); if ($this->dumpSettings['add-drop-table']) { - $this->compressManager->write("/*!50001 DROP TABLE IF EXISTS `$tableName`*/;\n\n"); + $this->compressManager->write("/*!50001 DROP TABLE IF EXISTS `$tableName`*/;" . PHP_EOL . PHP_EOL); } - $this->compressManager->write($r['Create Table'] . ";\n\n"); + $this->compressManager->write($r['Create Table'] . ";" . PHP_EOL . PHP_EOL); } return; } @@ -397,15 +412,15 @@ private function getViewStructure($viewName) if (isset($r['Create View'])) { if (!$this->dumpSettings['no-create-info']) { $this->compressManager->write( - "--\n" . - "-- Table structure for view `$viewName`\n" . - "--\n\n" + "--" . PHP_EOL . + "-- Table structure for view `$viewName`" . PHP_EOL . + "--" . PHP_EOL . PHP_EOL ); if ($this->dumpSettings['add-drop-table']) { - $this->compressManager->write("/*!50001 DROP TABLE IF EXISTS `$viewName`*/;\n"); - $this->compressManager->write("/*!50001 DROP VIEW IF EXISTS `$viewName`*/;\n\n"); + $this->compressManager->write("/*!50001 DROP TABLE IF EXISTS `$viewName`*/;" . PHP_EOL); + $this->compressManager->write("/*!50001 DROP VIEW IF EXISTS `$viewName`*/;" . PHP_EOL . PHP_EOL); } - $this->compressManager->write($r['Create View'] . ";\n\n"); + $this->compressManager->write($r['Create View'] . ";" . PHP_EOL . PHP_EOL); } return; } @@ -452,9 +467,9 @@ private function escape($arr) private function listValues($tablename) { $this->compressManager->write( - "--\n" . - "-- Dumping data for table `$tablename`\n" . - "--\n\n" + "--" . PHP_EOL . + "-- Dumping data for table `$tablename`" . PHP_EOL . + "--" . PHP_EOL . PHP_EOL ); if ($this->dumpSettings['single-transaction']) { @@ -490,13 +505,13 @@ private function listValues($tablename) if (($lineSize > self::MAXLINESIZE) || !$this->dumpSettings['extended-insert']) { $onlyOnce = true; - $lineSize = $this->compressManager->write(";\n"); + $lineSize = $this->compressManager->write(";" . PHP_EOL); } } $resultSet->closeCursor(); if (!$onlyOnce) { - $this->compressManager->write(";\n"); + $this->compressManager->write(";" . PHP_EOL); } if ($this->dumpSettings['add-locks']) { @@ -511,7 +526,7 @@ private function listValues($tablename) $this->typeAdapter->unlock_table($tablename); } - $this->compressManager->write("\n"); + $this->compressManager->write(PHP_EOL); } } @@ -742,27 +757,27 @@ public function unlock_table() public function start_add_lock_table() { - return "\n"; + return PHP_EOL; } public function end_add_lock_table() { - return "\n"; + return PHP_EOL; } public function start_disable_foreign_keys_check() { - return "\n"; + return PHP_EOL; } public function end_disable_foreign_keys_check() { - return "\n"; + return PHP_EOL; } public function add_drop_database() { - return "\n"; + return PHP_EOL; } } @@ -861,24 +876,24 @@ public function start_add_lock_table() $args = func_get_args(); - return "LOCK TABLES `${args[0]}` WRITE;\n"; + return "LOCK TABLES `${args[0]}` WRITE;" . PHP_EOL; } public function end_add_lock_table() { - return "UNLOCK TABLES;\n"; + return "UNLOCK TABLES;" . PHP_EOL; } public function start_disable_foreign_keys_check() { - return "-- Ignore checking of foreign keys\n" . - "SET FOREIGN_KEY_CHECKS = 0;\n\n"; + return "-- Ignore checking of foreign keys" . PHP_EOL . + "SET FOREIGN_KEY_CHECKS = 0;" . PHP_EOL . PHP_EOL; } public function end_disable_foreign_keys_check() { - return "\n-- Unignore checking of foreign keys\n" . - "SET FOREIGN_KEY_CHECKS = 1; \n\n"; + return "-- Unignore checking of foreign keys" . PHP_EOL . + "SET FOREIGN_KEY_CHECKS = 1;" . PHP_EOL . PHP_EOL; } public function add_drop_database() @@ -889,7 +904,7 @@ public function add_drop_database() $args = func_get_args(); - $ret = "/*!40000 DROP DATABASE IF EXISTS `${args[0]}`*/;\n"; + $ret = "/*!40000 DROP DATABASE IF EXISTS `${args[0]}`*/;" . PHP_EOL; $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'character_set_database';"); $characterSet = $resultSet->fetchColumn(1); @@ -901,8 +916,8 @@ public function add_drop_database() $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `${args[0]}`". " /*!40100 DEFAULT CHARACTER SET " . $characterSet . - " COLLATE " . $collationDb . "*/;\n" . - "USE `${args[0]}`;\n\n"; + " COLLATE " . $collationDb . "*/;" . PHP_EOL . + "USE `${args[0]}`;" . PHP_EOL . PHP_EOL; return $ret; } From b79e513b9f59b16f2e084f8beadbee5d3e80be96 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sun, 7 Sep 2014 21:42:30 +0000 Subject: [PATCH 08/29] escape values using column type information --- src/Ifsnop/Mysqldump/Mysqldump.php | 121 +++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 24 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 7da2f751..4c215fac 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -37,9 +37,25 @@ class Mysqldump const MAXLINESIZE = 1000000; // Available compression methods as constants - const GZIP = "Gzip"; - const BZIP2 = "Bzip2"; - const NONE = "None"; + const GZIP = 'Gzip'; + const BZIP2 = 'Bzip2'; + const NONE = 'None'; + + // Numerical Mysql types + public $mysqlNumericalTypes = array( + 'bit', + 'tinyint', + 'smallint', + 'mediumint', + 'int', + 'integer', + 'bigint', + 'real', + 'double', + 'float', + 'decimal', + 'numeric' + ); // This can be set both on constructor or manually public $host; @@ -58,6 +74,7 @@ class Mysqldump private $dumpSettings = array(); private $pdoSettings = array(); private $version; + private $tableColumnTypes = array(); /** * Constructor of Mysqldump. Note that in the case of an SQLite database @@ -393,10 +410,50 @@ private function getTableStructure($tableName) } $this->compressManager->write($r['Create Table'] . ";" . PHP_EOL . PHP_EOL); } - return; + + break; } throw new Exception("Error getting table structure, unknown output"); } + + $columnTypes = array(); + $columns = $this->dbHandler->query($this->typeAdapter->show_columns($tableName), + PDO::FETCH_ASSOC + ); + + foreach($columns as $key => $col) { + $types = $this->parseColumnType($col); + $columnTypes[$col['Field']] = $types['is_numeric']; + } + $this->tableColumnTypes[$tableName] = $columnTypes; + return; + } + + /** + * Decode column metadata and fill info structure. + * type and is_numeric will always be available. + * + * @param array $colType Array returned from "SHOW COLUMNS FROM tableName" + * @return array + */ + private function parseColumnType($colType) + { + $colInfo = array(); + $colParts = explode(" ", $colType['Type']); + + if($fparen = strpos($colParts[0], "(")) + { + $colInfo['type'] = substr($colParts[0], 0, $fparen); + $colInfo['length'] = str_replace(")", "", substr($colParts[0], $fparen+1)); + $colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL; + } + else + { + $colInfo['type'] = $colParts[0]; + } + $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlNumericalTypes); + + return $colInfo; } /** @@ -433,27 +490,27 @@ private function getViewStructure($viewName) * @todo use is_number instead of ctype_digit and intval * @todo get column data type, use it to quote results * - * @param array $arr Array of strings to be quoted + * @param string $tableName Name of table which contains rows + * @param array $row Associative array of column names and values to be quoted * * @return string */ - private function escape($arr) + private function escape($tableName, $row) { $ret = array(); - - foreach ($arr as $val) { - if (is_null($val)) { + foreach ($row as $colName => $colValue) { + if (is_null($colValue)) { $ret[] = "NULL"; - } elseif (ctype_digit($val) && ((string) intval($val) === $val)) { + } elseif ($this->tableColumnTypes[$tableName][$colName]) { + // if (ctype_digit($val) && ((string) intval($val) === $val)) { // Since "(string) intval($val) === $val" is slower, first check ctype_digit, then run comparison // We can't use ctype_digit alone, as this will trim off leading zeros on string values // but will quote negative integers (not a big deal IMHO) - $ret[] = $val; + $ret[] = $colValue; } else { - $ret[] = $this->dbHandler->quote($val); + $ret[] = $this->dbHandler->quote($colValue); } } - return $ret; } @@ -464,11 +521,11 @@ private function escape($arr) * * @return null */ - private function listValues($tablename) + private function listValues($tableName) { $this->compressManager->write( "--" . PHP_EOL . - "-- Dumping data for table `$tablename`" . PHP_EOL . + "-- Dumping data for table `$tableName`" . PHP_EOL . "--" . PHP_EOL . PHP_EOL ); @@ -477,26 +534,26 @@ private function listValues($tablename) } if ($this->dumpSettings['lock-tables']) { - $this->typeAdapter->lock_table($tablename); + $this->typeAdapter->lock_table($tableName); } if ($this->dumpSettings['add-locks']) { - $this->compressManager->write($this->typeAdapter->start_add_lock_table($tablename)); + $this->compressManager->write($this->typeAdapter->start_add_lock_table($tableName)); } $onlyOnce = true; $lineSize = 0; - $stmt = "SELECT * FROM `$tablename`"; + $stmt = "SELECT * FROM `$tableName`"; if ($this->dumpSettings['where']) { $stmt .= " WHERE {$this->dumpSettings['where']}"; } $resultSet = $this->dbHandler->query($stmt); - $resultSet->setFetchMode(PDO::FETCH_NUM); - foreach ($resultSet as $r) { - $vals = $this->escape($r); + $resultSet->setFetchMode(PDO::FETCH_ASSOC); + foreach ($resultSet as $row) { + $vals = $this->escape($tableName, $row); if ($onlyOnce || !$this->dumpSettings['extended-insert']) { $lineSize += $this->compressManager->write( - "INSERT INTO `$tablename` VALUES (" . implode(",", $vals) . ")" + "INSERT INTO `$tableName` VALUES (" . implode(",", $vals) . ")" ); $onlyOnce = false; } else { @@ -515,7 +572,7 @@ private function listValues($tablename) } if ($this->dumpSettings['add-locks']) { - $this->compressManager->write($this->typeAdapter->end_add_lock_table($tablename)); + $this->compressManager->write($this->typeAdapter->end_add_lock_table($tableName)); } if ($this->dumpSettings['single-transaction']) { @@ -523,7 +580,7 @@ private function listValues($tablename) } if ($this->dumpSettings['lock-tables']) { - $this->typeAdapter->unlock_table($tablename); + $this->typeAdapter->unlock_table($tableName); } $this->compressManager->write(PHP_EOL); @@ -735,6 +792,11 @@ public function show_views() return "SELECT tbl_name FROM sqlite_master where type='view'"; } + public function show_columns($tableName) + { + return "pragma table_info($tableName)"; + } + public function start_transaction() { return "BEGIN EXCLUSIVE"; @@ -839,6 +901,17 @@ public function show_views() "WHERE TABLE_TYPE='VIEW' AND TABLE_SCHEMA='${args[0]}'"; } + public function show_columns() + { + if (func_num_args() != 1) { + return ""; + } + + $args = func_get_args(); + + return "SHOW COLUMNS FROM `${args[0]}`;"; + } + public function start_transaction() { return "SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ; " . From f52c6a2098902be46a60423515c41ba3858cca11 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Mon, 8 Sep 2014 21:13:53 +0000 Subject: [PATCH 09/29] small speed improvement, move assignment outside loop --- src/Ifsnop/Mysqldump/Mysqldump.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 4c215fac..5bfc32a5 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -498,10 +498,11 @@ private function getViewStructure($viewName) private function escape($tableName, $row) { $ret = array(); + $columnTypes = $this->tableColumnTypes[$tableName]; foreach ($row as $colName => $colValue) { if (is_null($colValue)) { $ret[] = "NULL"; - } elseif ($this->tableColumnTypes[$tableName][$colName]) { + } elseif ($columnTypes[$colName]) { // if (ctype_digit($val) && ((string) intval($val) === $val)) { // Since "(string) intval($val) === $val" is slower, first check ctype_digit, then run comparison // We can't use ctype_digit alone, as this will trim off leading zeros on string values From 516ca15c797610274960158f906cafef0f43fae9 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Wed, 10 Sep 2014 07:53:14 +0000 Subject: [PATCH 10/29] added triggers dump --- README.md | 8 +- src/Ifsnop/Mysqldump/Mysqldump.php | 133 +++++++++++++++++++++++++++-- 2 files changed, 135 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3b908892..bb60be27 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,9 @@ Plain old PHP: 'extended-insert' => true, 'disable-foreign-keys-check' => false, 'where' => '', - 'no-create-info' => false + 'no-create-info' => false, + 'skip-triggers' => false, + 'add-drop-trigger' => true ); $pdoSettingsDefaults = array(PDO::ATTR_PERSISTENT => true, @@ -159,6 +161,10 @@ Plain old PHP: - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_where - **no-create-info** - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_no-create-info +- **skip-triggers** + - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_triggers +- **add-drop-triggers** + - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_add-drop-trigger ## PDO Settings diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 5bfc32a5..b8243267 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -67,6 +67,7 @@ class Mysqldump // Internal stuff private $tables = array(); private $views = array(); + private $triggers = array(); private $dbHandler; private $dbType; private $compressManager; @@ -112,7 +113,9 @@ public function __construct( 'extended-insert' => true, 'disable-foreign-keys-check' => false, 'where' => '', - 'no-create-info' => false + 'no-create-info' => false, + 'skip-triggers' => false, + 'add-drop-trigger' => true ); $pdoSettingsDefault = array(PDO::ATTR_PERSISTENT => true, @@ -254,6 +257,7 @@ public function start($filename = '') $this->exportTables(); $this->exportViews(); + $this->exportTriggers(); // Enable checking foreign keys if needed if ($this->dumpSettings['disable-foreign-keys-check']) { @@ -351,6 +355,14 @@ private function getDatabaseStructure() } } } + + // Listing all triggers from database + $this->triggers = array(); + if (false === $this->dumpSettings['skip-triggers']) { + foreach ($this->dbHandler->query($this->typeAdapter->show_triggers($this->db)) as $row) { + array_push($this->triggers, $row['Trigger']); + } + } } /** @@ -388,9 +400,24 @@ private function exportViews() } } + /** + * Exports all the triggers found in database + * + * @return null + */ + private function exportTriggers() + { + // Exporting views one by one + foreach ($this->triggers as $trigger) { + $this->getTriggerStructure($trigger); + } + } + + /** * Table structure extractor * + * @todo move specific mysql code to typeAdapter * @param string $tableName Name of table to export * @return null */ @@ -459,6 +486,7 @@ private function parseColumnType($colType) /** * View structure extractor * + * @todo move mysql specific code to typeAdapter * @param string $viewName Name of view to export * @return null */ @@ -485,6 +513,30 @@ private function getViewStructure($viewName) } } + /** + * Trigger structure extractor + * + * @param string $triggerName Name of trigger to export + * @return null + */ + private function getTriggerStructure($triggerName) + { + $stmt = $this->typeAdapter->show_create_trigger($triggerName); + foreach ($this->dbHandler->query($stmt) as $r) { + if ($this->dumpSettings['add-drop-trigger']) { + $this->compressManager->write( + $this->typeAdapter->add_drop_trigger($triggerName) + ); + } + $this->compressManager->write( + $this->typeAdapter->create_trigger($r) + ); + return; + } + + } + + /** * Escape values with quotes when needed * @todo use is_number instead of ctype_digit and intval @@ -518,7 +570,7 @@ private function escape($tableName, $row) /** * Table rows extractor * - * @param string $tablename Name of table to export + * @param string $tableName Name of table to export * * @return null */ @@ -783,14 +835,37 @@ public function show_create_view($viewName) "WHERE type='view' AND tbl_name='$viewName'"; } + /** + * function show_create_trigger Get trigger creation code from database + * @todo make it do something + */ + public function show_create_trigger($triggerName) + { + return ""; + } + + /** + * function create_trigger Modify trigger code, add delimiters, etc + * @todo make it do something + */ + public function create_trigger($triggerName) + { + return ""; + } + public function show_tables() { - return "SELECT tbl_name FROM sqlite_master where type='table'"; + return "SELECT tbl_name FROM sqlite_master WHERE type='table'"; } public function show_views() { - return "SELECT tbl_name FROM sqlite_master where type='view'"; + return "SELECT tbl_name FROM sqlite_master WHERE type='view'"; + } + + public function show_triggers() + { + return "SELECT name FROM sqlite_master WHERE type='trigger'"; } public function show_columns($tableName) @@ -842,6 +917,11 @@ public function add_drop_database() { return PHP_EOL; } + + public function add_drop_trigger() + { + return PHP_EOL; + } } class TypeAdapterPgsql extends TypeAdapterFactory @@ -876,6 +956,30 @@ public function show_create_view($viewName) return "SHOW CREATE VIEW `$viewName`"; } + public function show_create_trigger($triggerName) + { + return "SHOW CREATE TRIGGER `$triggerName`"; + } + + public function create_trigger($row) + { + $ret = ""; + if (isset($row['SQL Original Statement'])) { + $triggerStmt = $row['SQL Original Statement']; + $triggerStmtReplaced = str_replace("CREATE", "/*!50003 CREATE*/", $triggerStmt); + if ( false === $triggerStmtReplaced ) { + $triggerStmtReplaced = $triggerStmt; + } + $ret = "DELIMITER ;;" . PHP_EOL . + $triggerStmtReplaced . ";;" . PHP_EOL . + "DELIMITER ;" . PHP_EOL . PHP_EOL; + } else { + throw new Exception("Error getting trigger code, unknown output"); + } + + return $ret; + } + public function show_tables() { if (func_num_args() != 1) { @@ -902,6 +1006,18 @@ public function show_views() "WHERE TABLE_TYPE='VIEW' AND TABLE_SCHEMA='${args[0]}'"; } + public function show_triggers() + { + if (func_num_args() != 1) { + return ""; + } + + $args = func_get_args(); + + return "SHOW TRIGGERS FROM `${args[0]}`;"; + } + + public function show_columns() { if (func_num_args() != 1) { @@ -961,13 +1077,16 @@ public function end_add_lock_table() public function start_disable_foreign_keys_check() { return "-- Ignore checking of foreign keys" . PHP_EOL . + "SET AUTOCOMMIT = 0;" . PHP_EOL . "SET FOREIGN_KEY_CHECKS = 0;" . PHP_EOL . PHP_EOL; } public function end_disable_foreign_keys_check() { return "-- Unignore checking of foreign keys" . PHP_EOL . - "SET FOREIGN_KEY_CHECKS = 1;" . PHP_EOL . PHP_EOL; + "SET FOREIGN_KEY_CHECKS = 1;" . PHP_EOL . + "COMMIT;" . PHP_EOL . + "SET AUTOCOMMIT = 1;" . PHP_EOL . PHP_EOL; } public function add_drop_database() @@ -995,4 +1114,8 @@ public function add_drop_database() return $ret; } + + public function add_drop_trigger($triggerName) { + return "DROP TRIGGER IF EXISTS `$triggerName`;" . PHP_EOL; + } } From c612e854ba642f26457dd8c300f569355bd0e396 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Wed, 10 Sep 2014 16:22:19 +0000 Subject: [PATCH 11/29] fix: derived classes should have same function signature --- src/Ifsnop/Mysqldump/Mysqldump.php | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index b8243267..840008ca 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -868,9 +868,15 @@ public function show_triggers() return "SELECT name FROM sqlite_master WHERE type='trigger'"; } - public function show_columns($tableName) + public function show_columns() { - return "pragma table_info($tableName)"; + if (func_num_args() != 1) { + return ""; + } + + $args = func_get_args(); + + return "pragma table_info(${args[0]})"; } public function start_transaction() @@ -1115,7 +1121,14 @@ public function add_drop_database() return $ret; } - public function add_drop_trigger($triggerName) { - return "DROP TRIGGER IF EXISTS `$triggerName`;" . PHP_EOL; + public function add_drop_trigger() + { + if (func_num_args() != 1) { + return ""; + } + + $args = func_get_args(); + + return "DROP TRIGGER IF EXISTS `${args[0]};" . PHP_EOL; } } From 53074d8633cb0a494b31526ba5ddef3ee149410a Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Wed, 10 Sep 2014 17:25:24 +0000 Subject: [PATCH 12/29] enabling mysql on travis --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 15eece73..e3db80fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,19 @@ language: php php: + - 5.6 - 5.5 - 5.4 - 5.3 - hhvm +services: + - mysql + before_script: - curl -s http://getcomposer.org/installer | php - php composer.phar install --dev + - mysql -e 'create database mysqldump;' -script: phpunit +script: + - php src/Ifsnop/Mysqldump/Mysqldump.php From e42fdada0072b17b625c36a0fcb18357bd86d12e Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Wed, 10 Sep 2014 19:01:40 +0000 Subject: [PATCH 13/29] Implemented hex-blob as a setting closes #65 --- README.md | 5 ++- src/Ifsnop/Mysqldump/Mysqldump.php | 60 ++++++++++++++++++------------ 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index bb60be27..01a9f4fe 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,8 @@ Plain old PHP: 'where' => '', 'no-create-info' => false, 'skip-triggers' => false, - 'add-drop-trigger' => true + 'add-drop-trigger' => true, + 'hex-blob' => true ); $pdoSettingsDefaults = array(PDO::ATTR_PERSISTENT => true, @@ -165,6 +166,8 @@ Plain old PHP: - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_triggers - **add-drop-triggers** - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_add-drop-trigger +- **hex-blob** + - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_hex-blob ## PDO Settings diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 840008ca..05f9de50 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -42,19 +42,30 @@ class Mysqldump const NONE = 'None'; // Numerical Mysql types - public $mysqlNumericalTypes = array( - 'bit', - 'tinyint', - 'smallint', - 'mediumint', - 'int', - 'integer', - 'bigint', - 'real', - 'double', - 'float', - 'decimal', - 'numeric' + public $mysqlTypes = array( + 'numerical' => array( + 'bit', + 'tinyint', + 'smallint', + 'mediumint', + 'int', + 'integer', + 'bigint', + 'real', + 'double', + 'float', + 'decimal', + 'numeric' + ), + 'blob' => array( + 'tinyblob', + 'blob', + 'mediumblob', + 'longblob', + 'binary', + 'varbinary', + 'bit' + ) ); // This can be set both on constructor or manually @@ -115,7 +126,8 @@ public function __construct( 'where' => '', 'no-create-info' => false, 'skip-triggers' => false, - 'add-drop-trigger' => true + 'add-drop-trigger' => true, + 'hex-blob' => true ); $pdoSettingsDefault = array(PDO::ATTR_PERSISTENT => true, @@ -450,7 +462,10 @@ private function getTableStructure($tableName) foreach($columns as $key => $col) { $types = $this->parseColumnType($col); - $columnTypes[$col['Field']] = $types['is_numeric']; + $columnTypes[$col['Field']] = array( + 'is_numeric'=> $types['is_numeric'], + 'is_blob' => $types['is_blob'] + ); } $this->tableColumnTypes[$tableName] = $columnTypes; return; @@ -458,7 +473,7 @@ private function getTableStructure($tableName) /** * Decode column metadata and fill info structure. - * type and is_numeric will always be available. + * type, is_numeric and is_blob will always be available. * * @param array $colType Array returned from "SHOW COLUMNS FROM tableName" * @return array @@ -478,7 +493,8 @@ private function parseColumnType($colType) { $colInfo['type'] = $colParts[0]; } - $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlNumericalTypes); + $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlTypes['numerical']); + $colInfo['is_blob'] = in_array($colInfo['type'], $this->mysqlTypes['blob']); return $colInfo; } @@ -554,12 +570,10 @@ private function escape($tableName, $row) foreach ($row as $colName => $colValue) { if (is_null($colValue)) { $ret[] = "NULL"; - } elseif ($columnTypes[$colName]) { - // if (ctype_digit($val) && ((string) intval($val) === $val)) { - // Since "(string) intval($val) === $val" is slower, first check ctype_digit, then run comparison - // We can't use ctype_digit alone, as this will trim off leading zeros on string values - // but will quote negative integers (not a big deal IMHO) + } elseif ($columnTypes[$colName]['is_numeric']) { $ret[] = $colValue; + } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) { + $ret[] = '0x' . bin2hex($colValue); } else { $ret[] = $this->dbHandler->quote($colValue); } @@ -1129,6 +1143,6 @@ public function add_drop_trigger() $args = func_get_args(); - return "DROP TRIGGER IF EXISTS `${args[0]};" . PHP_EOL; + return "DROP TRIGGER IF EXISTS `${args[0]}`;" . PHP_EOL; } } From 1c98849bd55cb1e4ad2d907b94c0b25cd9d32a34 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Wed, 10 Sep 2014 21:46:17 +0000 Subject: [PATCH 14/29] added disable-keys --- README.md | 14 +- src/Ifsnop/Mysqldump/Mysqldump.php | 218 +++++++++++++++++++++++++---- 2 files changed, 199 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 01a9f4fe..301ff87b 100644 --- a/README.md +++ b/README.md @@ -110,18 +110,20 @@ Plain old PHP: 'exclude-tables' => array(), 'compress' => 'None', 'no-data' => false, - 'add-drop-database' => false, 'add-drop-table' => false, 'single-transaction' => true, 'lock-tables' => false, 'add-locks' => true, 'extended-insert' => true, + 'disable-keys' => true, 'disable-foreign-keys-check' => false, 'where' => '', 'no-create-info' => false, 'skip-triggers' => false, 'add-drop-trigger' => true, - 'hex-blob' => true + 'hex-blob' => true, + 'databases' => false, + 'add-drop-database' => false ); $pdoSettingsDefaults = array(PDO::ATTR_PERSISTENT => true, @@ -144,8 +146,6 @@ Plain old PHP: - Gzip, Bzip2, None - **no-data** - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_no-data -- **add-drop-database** - - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_add-drop-database - **add-drop-table** - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_add-drop-table - **single-transaction** @@ -156,6 +156,8 @@ Plain old PHP: - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_add-locks - **extended-insert** - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_extended-insert +- **disable-keys** + - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_disable-keys - **disable-foreign-keys-check** - http://dev.mysql.com/doc/refman/5.5/en/optimizing-innodb-bulk-data-loading.html - **where** @@ -168,6 +170,10 @@ Plain old PHP: - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_add-drop-trigger - **hex-blob** - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_hex-blob +- **databases** + - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_databases +- **add-drop-database** + - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_add-drop-database ## PDO Settings diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 05f9de50..1c8ed7ea 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -116,18 +116,20 @@ public function __construct( 'exclude-tables' => array(), 'compress' => 'None', 'no-data' => false, - 'add-drop-database' => false, 'add-drop-table' => false, 'single-transaction' => true, 'lock-tables' => true, 'add-locks' => true, 'extended-insert' => true, + 'disable-keys' => true, 'disable-foreign-keys-check' => false, 'where' => '', 'no-create-info' => false, 'skip-triggers' => false, 'add-drop-trigger' => true, - 'hex-blob' => true + 'hex-blob' => true, + 'databases' => false, + 'add-drop-database' => false, ); $pdoSettingsDefault = array(PDO::ATTR_PERSISTENT => true, @@ -246,12 +248,21 @@ public function start($filename = '') // Write some basic info to output file $this->compressManager->write($this->getHeader()); - if ($this->dumpSettings['add-drop-database']) { - $this->compressManager->write($this->typeAdapter->add_drop_database($this->db)); + if ($this->dumpSettings['databases'] && + $this->dumpSettings['add-drop-database']) { + $this->compressManager->write( + $this->typeAdapter->add_drop_database($this->db) + ); } $this->getDatabaseStructure(); + if ($this->dumpSettings['databases']) { + $this->compressManager->write( + $this->typeAdapter->databases($this->db) + ); + } + // If there still are some tables/views in include-tables array, // that means that some tables or views weren't found. // Give proper error and exit. @@ -445,9 +456,13 @@ private function getTableStructure($tableName) "--" . PHP_EOL . PHP_EOL ); if ($this->dumpSettings['add-drop-table']) { - $this->compressManager->write("/*!50001 DROP TABLE IF EXISTS `$tableName`*/;" . PHP_EOL . PHP_EOL); + $this->compressManager->write( + $this->typeAdapter->drop_table($tableName) + ); } - $this->compressManager->write($r['Create Table'] . ";" . PHP_EOL . PHP_EOL); + $this->compressManager->write( + $this->typeAdapter->create_table($r) + ); } break; @@ -518,8 +533,9 @@ private function getViewStructure($viewName) "--" . PHP_EOL . PHP_EOL ); if ($this->dumpSettings['add-drop-table']) { - $this->compressManager->write("/*!50001 DROP TABLE IF EXISTS `$viewName`*/;" . PHP_EOL); - $this->compressManager->write("/*!50001 DROP VIEW IF EXISTS `$viewName`*/;" . PHP_EOL . PHP_EOL); + $this->compressManager->write( + $this->typeAdapter->drop_view($viewName) + ); } $this->compressManager->write($r['Create View'] . ";" . PHP_EOL . PHP_EOL); } @@ -605,7 +621,15 @@ private function listValues($tableName) } if ($this->dumpSettings['add-locks']) { - $this->compressManager->write($this->typeAdapter->start_add_lock_table($tableName)); + $this->compressManager->write( + $this->typeAdapter->start_add_lock_table($tableName) + ); + } + + if ($this->dumpSettings['disable-keys']) { + $this->compressManager->write( + $this->typeAdapter->start_add_disable_keys($tableName) + ); } $onlyOnce = true; @@ -638,8 +662,16 @@ private function listValues($tableName) $this->compressManager->write(";" . PHP_EOL); } + if ($this->dumpSettings['disable-keys']) { + $this->compressManager->write( + $this->typeAdapter->end_add_disable_keys($tableName) + ); + } + if ($this->dumpSettings['add-locks']) { - $this->compressManager->write($this->typeAdapter->end_add_lock_table($tableName)); + $this->compressManager->write( + $this->typeAdapter->end_add_lock_table($tableName) + ); } if ($this->dumpSettings['single-transaction']) { @@ -835,6 +867,15 @@ public static function create($c, $dbHandler = null) return new $method($dbHandler); } + /** + * function databases Add sql to create and use database + * @todo make it do something with sqlite + */ + public function databases() + { + return ""; + } + public function show_create_table($tableName) { return "SELECT tbl_name as 'Table', sql as 'Create Table' " . @@ -842,6 +883,15 @@ public function show_create_table($tableName) "WHERE type='table' AND tbl_name='$tableName'"; } + /** + * function create_table Get table creation code from database + * @todo make it do something with sqlite + */ + public function create_table($row) + { + return ""; + } + public function show_create_view($viewName) { return "SELECT tbl_name as 'View', sql as 'Create View' " . @@ -851,7 +901,7 @@ public function show_create_view($viewName) /** * function show_create_trigger Get trigger creation code from database - * @todo make it do something + * @todo make it do something with sqlite */ public function show_create_trigger($triggerName) { @@ -860,7 +910,7 @@ public function show_create_trigger($triggerName) /** * function create_trigger Modify trigger code, add delimiters, etc - * @todo make it do something + * @todo make it do something with sqlite */ public function create_trigger($triggerName) { @@ -923,6 +973,16 @@ public function end_add_lock_table() return PHP_EOL; } + public function start_add_disable_keys() + { + return PHP_EOL; + } + + public function end_add_disable_keys() + { + return PHP_EOL; + } + public function start_disable_foreign_keys_check() { return PHP_EOL; @@ -942,6 +1002,17 @@ public function add_drop_trigger() { return PHP_EOL; } + + public function drop_table() + { + return PHP_EOL; + } + + public function drop_view() + { + return PHP_EOL; + } + } class TypeAdapterPgsql extends TypeAdapterFactory @@ -966,6 +1037,35 @@ public function __construct ($dbHandler) $this->dbHandler = $dbHandler; } + public function databases() + { + if (func_num_args() != 1) { + return ""; + } + + $args = func_get_args(); + + $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'character_set_database';"); + $characterSet = $resultSet->fetchColumn(1); + $resultSet->closeCursor(); + + $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'collation_database';"); + $collationDb = $resultSet->fetchColumn(1); + $resultSet->closeCursor(); + $ret = ""; + + if (false === $this->dumpSettings['add-drop-database']) { + $ret = $this->getDatabaseHeader(); + } + + $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `${args[0]}`". + " /*!40100 DEFAULT CHARACTER SET " . $characterSet . + " COLLATE " . $collationDb . "*/;" . PHP_EOL . PHP_EOL . + "USE `${args[0]}`;" . PHP_EOL . PHP_EOL; + + return $ret; + } + public function show_create_table($tableName) { return "SHOW CREATE TABLE `$tableName`"; @@ -981,22 +1081,42 @@ public function show_create_trigger($triggerName) return "SHOW CREATE TRIGGER `$triggerName`"; } + public function create_table($row) + { + $ret = ""; + if (isset($row['Create Table'])) { + $ret = $row['Create Table'] . PHP_EOL . PHP_EOL; + } else { + throw new Exception("Error getting trigger code, unknown output"); + } + return $ret; + + } + public function create_trigger($row) { $ret = ""; if (isset($row['SQL Original Statement'])) { $triggerStmt = $row['SQL Original Statement']; - $triggerStmtReplaced = str_replace("CREATE", "/*!50003 CREATE*/", $triggerStmt); + $triggerStmtReplaced = str_replace( + "CREATE DEFINER", + "/*!50003 CREATE*/ /*!50017 DEFINER", + $triggerStmt + ); + $triggerStmtReplaced = str_replace( + " TRIGGER", + "*/ /*!50003 TRIGGER", + $triggerStmtReplaced + ); if ( false === $triggerStmtReplaced ) { $triggerStmtReplaced = $triggerStmt; } $ret = "DELIMITER ;;" . PHP_EOL . - $triggerStmtReplaced . ";;" . PHP_EOL . + $triggerStmtReplaced . "*/;;" . PHP_EOL . "DELIMITER ;" . PHP_EOL . PHP_EOL; } else { throw new Exception("Error getting trigger code, unknown output"); } - return $ret; } @@ -1094,6 +1214,24 @@ public function end_add_lock_table() return "UNLOCK TABLES;" . PHP_EOL; } + public function start_add_disable_keys() + { + if (func_num_args() != 1) { + return ""; + } + $args = func_get_args(); + return "/*!40000 ALTER TABLE `${args[0]}` DISABLE KEYS */;" . PHP_EOL; + } + + public function end_add_disable_keys() + { + if (func_num_args() != 1) { + return ""; + } + $args = func_get_args(); + return "/*!40000 ALTER TABLE `${args[0]}` ENABLE KEYS */;" . PHP_EOL; + } + public function start_disable_foreign_keys_check() { return "-- Ignore checking of foreign keys" . PHP_EOL . @@ -1117,25 +1255,34 @@ public function add_drop_database() $args = func_get_args(); - $ret = "/*!40000 DROP DATABASE IF EXISTS `${args[0]}`*/;" . PHP_EOL; + return $this->getDatabaseHeader($args[0]) . + "/*!40000 DROP DATABASE IF EXISTS `${args[0]}`*/;" . + PHP_EOL . PHP_EOL; + } - $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'character_set_database';"); - $characterSet = $resultSet->fetchColumn(1); - $resultSet->closeCursor(); + public function add_drop_trigger() + { + if (func_num_args() != 1) { + return ""; + } - $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'collation_database';"); - $collationDb = $resultSet->fetchColumn(1); - $resultSet->closeCursor(); + $args = func_get_args(); - $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `${args[0]}`". - " /*!40100 DEFAULT CHARACTER SET " . $characterSet . - " COLLATE " . $collationDb . "*/;" . PHP_EOL . - "USE `${args[0]}`;" . PHP_EOL . PHP_EOL; + return "DROP TRIGGER IF EXISTS `${args[0]}`;" . PHP_EOL; + } - return $ret; + public function drop_table() + { + if (func_num_args() != 1) { + return ""; + } + + $args = func_get_args(); + + return "DROP TABLE IF EXISTS `${args[0]}`;" . PHP_EOL; } - public function add_drop_trigger() + public function drop_view() { if (func_num_args() != 1) { return ""; @@ -1143,6 +1290,19 @@ public function add_drop_trigger() $args = func_get_args(); - return "DROP TRIGGER IF EXISTS `${args[0]}`;" . PHP_EOL; + return "DROP TABLE IF EXISTS `${args[0]}`;" . PHP_EOL . + "/*!50001 DROP VIEW IF EXISTS `${args[0]}`*/;" . PHP_EOL; + } + + public function getDatabaseHeader() { + if (func_num_args() != 1) { + return ""; + } + + $args = func_get_args(); + + return "--" . PHP_EOL . + "-- Current Database: `${args[0]}`" . PHP_EOL . + "--" . PHP_EOL . PHP_EOL; } } From c30011caa20dad5a41082b6bb66661837e11d30d Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Wed, 10 Sep 2014 22:02:42 +0000 Subject: [PATCH 15/29] fixed a bug in create table --- src/Ifsnop/Mysqldump/Mysqldump.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 1c8ed7ea..e9871ce8 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -1085,7 +1085,7 @@ public function create_table($row) { $ret = ""; if (isset($row['Create Table'])) { - $ret = $row['Create Table'] . PHP_EOL . PHP_EOL; + $ret = $row['Create Table'] . ";" . PHP_EOL . PHP_EOL; } else { throw new Exception("Error getting trigger code, unknown output"); } From ba8fd0be07d2567b5647ef8e9c869573404badc9 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Thu, 11 Sep 2014 07:51:41 +0000 Subject: [PATCH 16/29] fix for no data in blob, closes 65 --- src/Ifsnop/Mysqldump/Mysqldump.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index e9871ce8..605208fe 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -589,7 +589,11 @@ private function escape($tableName, $row) } elseif ($columnTypes[$colName]['is_numeric']) { $ret[] = $colValue; } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) { - $ret[] = '0x' . bin2hex($colValue); + if (empty($colValue)) { + $ret[] = "''"; + } else { + $ret[] = '0x' . bin2hex($colValue); + } } else { $ret[] = $this->dbHandler->quote($colValue); } From 14cee4d8f2630f5359d9a6cd7276ef9fc71156f4 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Thu, 11 Sep 2014 16:00:39 +0000 Subject: [PATCH 17/29] added some database tests --- tests/tests.sql | 137 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/tests.sql diff --git a/tests/tests.sql b/tests/tests.sql new file mode 100644 index 00000000..ba70581c --- /dev/null +++ b/tests/tests.sql @@ -0,0 +1,137 @@ +DROP DATABASE IF EXISTS `test001`; +CREATE DATABASE `test001`; +USE `test001`; + +DROP TABLE IF EXISTS `test000`; +CREATE TABLE `test000` ( + `id` int, + `col01` bit(1) DEFAULT NULL, + `col02` tinyint(4) DEFAULT NULL, + `col03` tinyint(4) UNSIGNED DEFAULT NULL, + `col10` bigint DEFAULT NULL, + `col11` bigint UNSIGNED DEFAULT NULL, + `col15` double DEFAULT NULL, + `col27` varchar(6) DEFAULT NULL +); +INSERT INTO `test000` VALUES (1, +1, +-128, +255, +-9223372036854775808, +18446744073709551615, +-2.2250738585072014e-308, +"0abcde"); + + + +DROP TABLE IF EXISTS `test001`; +CREATE TABLE `test001` ( + `id` int, + `col` bit(1) DEFAULT NULL +); +INSERT INTO `test001` VALUES (1,NULL); +INSERT INTO `test001` VALUES (2,0); +INSERT INTO `test001` VALUES (3,1); + +DROP TABLE IF EXISTS `test002`; +CREATE TABLE `test002` ( + `id` int, + `col` tinyint(4) DEFAULT NULL +); +INSERT INTO `test002` VALUES (1,NULL); +INSERT INTO `test002` VALUES (2,-128); +INSERT INTO `test002` VALUES (3,0); +INSERT INTO `test002` VALUES (4,127); + +DROP TABLE IF EXISTS `test003`; +CREATE TABLE `test003` ( + `id` int, + `col` tinyint(4) UNSIGNED DEFAULT NULL +); +INSERT INTO `test003` VALUES (1,NULL); +INSERT INTO `test003` VALUES (2,0); +INSERT INTO `test003` VALUES (3,255); + +DROP TABLE IF EXISTS `test010`; +CREATE TABLE `test010` ( + `id` int, + `col` bigint DEFAULT NULL +); +INSERT INTO `test010` VALUES (1,NULL); +INSERT INTO `test010` VALUES (2, –9223372036854775808); +INSERT INTO `test010` VALUES (3, 0); +INSERT INTO `test010` VALUES (4, 9223372036854775807); + +DROP TABLE IF EXISTS `test011`; +CREATE TABLE `test011` ( + `id` int, + `col` bigint UNSIGNED DEFAULT NULL +); +INSERT INTO `test011` VALUES (1,NULL); +INSERT INTO `test011` VALUES (3, 0); +INSERT INTO `test011` VALUES (4, 18446744073709551615); + + +DROP TABLE IF EXISTS `test015`; +CREATE TABLE `test015` ( + `id` int, + `col` double DEFAULT NULL +); +INSERT INTO `test015` VALUES (1,NULL); +INSERT INTO `test015` VALUES (2, -1.7976931348623157e308); +INSERT INTO `test015` VALUES (3, -2.2250738585072014e-308); +INSERT INTO `test015` VALUES (4,0); +INSERT INTO `test015` VALUES (5, 2.2250738585072014e-308); +INSERT INTO `test015` VALUES (6, 1.7976931348623157e308); + + +DROP TABLE IF EXISTS `test027`; +CREATE TABLE `test027` ( + `id` int, + `col` varchar(6) DEFAULT NULL +); +INSERT INTO `test027` VALUES (1,NULL); +INSERT INTO `test027` VALUES (2, ''); +INSERT INTO `test027` VALUES (3, '0'); +INSERT INTO `test027` VALUES (4, '2e308'); +INSERT INTO `test027` VALUES (5, '999.99'); +INSERT INTO `test027` VALUES (6, '-2e-30'); +INSERT INTO `test027` VALUES (7, '-99.99'); +INSERT INTO `test027` VALUES (8, '0'); +INSERT INTO `test027` VALUES (9, '0abcde'); +INSERT INTO `test027` VALUES (10, '123'); + +DROP TABLE IF EXISTS `test029`; +CREATE TABLE `test029` ( + `id` int, + `col` blob NOT NULL +); +INSERT INTO `test029` VALUES (1,0x000102030405060708090A0B0C0D0E0F); +INSERT INTO `test029` VALUES (1,''); + +DROP TABLE IF EXISTS `test033`; +CREATE TABLE `test033` ( + `id` int, + `col` text NOT NULL +); +INSERT INTO `test033` VALUES (1,0x000102030405060708090A0B0C0D0E0F); + + +DROP VIEW IF EXISTS `test100`; +CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `test100` AS select `test000`.`id` AS `id`,`test000`.`col01` AS `col01`,`test000`.`col02` AS `col02`,`test000`.`col03` AS `col03`,`test000`.`col10` AS `col10`,`test000`.`col11` AS `col11`,`test000`.`col15` AS `col15`,`test000`.`col27` AS `col27` from `test000`; + +DROP VIEW IF EXISTS `test127`; +CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `test127` AS select `test027`.`id` AS `id`,`test027`.`col` AS `col` from `test027`; + + +DROP TABLE IF EXISTS `test200`; +CREATE TABLE `test200` ( + `id` int, + `col` tinyint(4) DEFAULT NULL +); + +CREATE TRIGGER before_test200_insert + BEFORE insert ON `test200` + FOR EACH ROW set NEW.col = NEW.col + 1; +INSERT INTO `test200` VALUES (1,1); + From 420ae16067620682de95e0013b727c59125a0233 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Thu, 11 Sep 2014 20:14:05 +0000 Subject: [PATCH 18/29] travis db tests --- .travis.yml | 2 +- tests/.gitkeep | 1 - tests/{tests.sql => original.sql} | 57 +++++++++++++------------------ tests/test.sh | 19 +++++++++++ 4 files changed, 44 insertions(+), 35 deletions(-) delete mode 100644 tests/.gitkeep rename tests/{tests.sql => original.sql} (69%) create mode 100755 tests/test.sh diff --git a/.travis.yml b/.travis.yml index e3db80fe..1c2e2d7d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ services: before_script: - curl -s http://getcomposer.org/installer | php - php composer.phar install --dev - - mysql -e 'create database mysqldump;' script: - php src/Ifsnop/Mysqldump/Mysqldump.php + - cd tests && ./test.sh diff --git a/tests/.gitkeep b/tests/.gitkeep deleted file mode 100644 index 8b137891..00000000 --- a/tests/.gitkeep +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/tests.sql b/tests/original.sql similarity index 69% rename from tests/tests.sql rename to tests/original.sql index ba70581c..607dbb24 100644 --- a/tests/tests.sql +++ b/tests/original.sql @@ -13,16 +13,7 @@ CREATE TABLE `test000` ( `col15` double DEFAULT NULL, `col27` varchar(6) DEFAULT NULL ); -INSERT INTO `test000` VALUES (1, -1, --128, -255, --9223372036854775808, -18446744073709551615, --2.2250738585072014e-308, -"0abcde"); - - +INSERT INTO `test000` VALUES (1,0x01,-128,255,-9223372036854775808,18446744073709551615,-2.2250738585072014e-308,'0abcde'); DROP TABLE IF EXISTS `test001`; CREATE TABLE `test001` ( @@ -30,8 +21,8 @@ CREATE TABLE `test001` ( `col` bit(1) DEFAULT NULL ); INSERT INTO `test001` VALUES (1,NULL); -INSERT INTO `test001` VALUES (2,0); -INSERT INTO `test001` VALUES (3,1); +INSERT INTO `test001` VALUES (2,0x00); +INSERT INTO `test001` VALUES (3,0x01); DROP TABLE IF EXISTS `test002`; CREATE TABLE `test002` ( @@ -58,9 +49,9 @@ CREATE TABLE `test010` ( `col` bigint DEFAULT NULL ); INSERT INTO `test010` VALUES (1,NULL); -INSERT INTO `test010` VALUES (2, –9223372036854775808); -INSERT INTO `test010` VALUES (3, 0); -INSERT INTO `test010` VALUES (4, 9223372036854775807); +INSERT INTO `test010` VALUES (2,-9223372036854775808); +INSERT INTO `test010` VALUES (3,0); +INSERT INTO `test010` VALUES (4,9223372036854775807); DROP TABLE IF EXISTS `test011`; CREATE TABLE `test011` ( @@ -68,8 +59,8 @@ CREATE TABLE `test011` ( `col` bigint UNSIGNED DEFAULT NULL ); INSERT INTO `test011` VALUES (1,NULL); -INSERT INTO `test011` VALUES (3, 0); -INSERT INTO `test011` VALUES (4, 18446744073709551615); +INSERT INTO `test011` VALUES (3,0); +INSERT INTO `test011` VALUES (4,18446744073709551615); DROP TABLE IF EXISTS `test015`; @@ -78,11 +69,11 @@ CREATE TABLE `test015` ( `col` double DEFAULT NULL ); INSERT INTO `test015` VALUES (1,NULL); -INSERT INTO `test015` VALUES (2, -1.7976931348623157e308); -INSERT INTO `test015` VALUES (3, -2.2250738585072014e-308); +INSERT INTO `test015` VALUES (2,-1.7976931348623157e308); +INSERT INTO `test015` VALUES (3,-2.2250738585072014e-308); INSERT INTO `test015` VALUES (4,0); -INSERT INTO `test015` VALUES (5, 2.2250738585072014e-308); -INSERT INTO `test015` VALUES (6, 1.7976931348623157e308); +INSERT INTO `test015` VALUES (5,2.2250738585072014e-308); +INSERT INTO `test015` VALUES (6,1.7976931348623157e308); DROP TABLE IF EXISTS `test027`; @@ -91,15 +82,15 @@ CREATE TABLE `test027` ( `col` varchar(6) DEFAULT NULL ); INSERT INTO `test027` VALUES (1,NULL); -INSERT INTO `test027` VALUES (2, ''); -INSERT INTO `test027` VALUES (3, '0'); -INSERT INTO `test027` VALUES (4, '2e308'); -INSERT INTO `test027` VALUES (5, '999.99'); -INSERT INTO `test027` VALUES (6, '-2e-30'); -INSERT INTO `test027` VALUES (7, '-99.99'); -INSERT INTO `test027` VALUES (8, '0'); -INSERT INTO `test027` VALUES (9, '0abcde'); -INSERT INTO `test027` VALUES (10, '123'); +INSERT INTO `test027` VALUES (2,''); +INSERT INTO `test027` VALUES (3,'0'); +INSERT INTO `test027` VALUES (4,'2e308'); +INSERT INTO `test027` VALUES (5,'999.99'); +INSERT INTO `test027` VALUES (6,'-2e-30'); +INSERT INTO `test027` VALUES (7,'-99.99'); +INSERT INTO `test027` VALUES (8,'0'); +INSERT INTO `test027` VALUES (9,'0abcde'); +INSERT INTO `test027` VALUES (10,'123'); DROP TABLE IF EXISTS `test029`; CREATE TABLE `test029` ( @@ -107,14 +98,14 @@ CREATE TABLE `test029` ( `col` blob NOT NULL ); INSERT INTO `test029` VALUES (1,0x000102030405060708090A0B0C0D0E0F); -INSERT INTO `test029` VALUES (1,''); +INSERT INTO `test029` VALUES (2,''); DROP TABLE IF EXISTS `test033`; CREATE TABLE `test033` ( `id` int, `col` text NOT NULL ); -INSERT INTO `test033` VALUES (1,0x000102030405060708090A0B0C0D0E0F); +INSERT INTO `test033` VALUES (1,'test test test'); DROP VIEW IF EXISTS `test100`; @@ -133,5 +124,5 @@ CREATE TABLE `test200` ( CREATE TRIGGER before_test200_insert BEFORE insert ON `test200` FOR EACH ROW set NEW.col = NEW.col + 1; -INSERT INTO `test200` VALUES (1,1); +-- INSERT INTO `test200` VALUES (1,1); -- trigger tests diff --git a/tests/test.sh b/tests/test.sh new file mode 100755 index 00000000..29bf36ea --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,19 @@ +mysql -uroot < original.sql +mysqldump -uroot test001 --extended-insert=false --hex-blob=true > mysqldump.sql + +cat original.sql | grep ^INSERT > original.filtered.sql +cat mysqldump.sql | grep ^INSERT > mysqldump.filtered.sql + +diff original.filtered.sql mysqldump.filtered.sql + +ret=$? + +rm original.filtered.sql +rm mysqldump.filtered.sql +rm mysqldump.sql + +echo $ret + + + + From ea1c8269973912cd9649e7e54f94299be2dd38e5 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Fri, 12 Sep 2014 16:31:35 +0000 Subject: [PATCH 19/29] fix hex-blob when converting bit type --- README.md | 2 ++ src/Ifsnop/Mysqldump/Mysqldump.php | 22 +++++++++++++--------- tests/test.sh | 11 ++++++++--- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 301ff87b..fe7681ef 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,8 @@ To dump a database, you need the following privileges : - If any table has one or more triggers. - **LOCK TABLES** - If "lock tables" option was enabled. +- **SUPER** + - If "single-transaction" option was enabled. Use **SHOW GRANTS FOR user@host;** to know what privileges user has. See the following link for more information: diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 605208fe..dd88fa14 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -471,7 +471,8 @@ private function getTableStructure($tableName) } $columnTypes = array(); - $columns = $this->dbHandler->query($this->typeAdapter->show_columns($tableName), + $columns = $this->dbHandler->query( + $this->typeAdapter->show_columns($tableName), PDO::FETCH_ASSOC ); @@ -479,7 +480,8 @@ private function getTableStructure($tableName) $types = $this->parseColumnType($col); $columnTypes[$col['Field']] = array( 'is_numeric'=> $types['is_numeric'], - 'is_blob' => $types['is_blob'] + 'is_blob' => $types['is_blob'], + 'type' => $types['type'] ); } $this->tableColumnTypes[$tableName] = $columnTypes; @@ -571,8 +573,6 @@ private function getTriggerStructure($triggerName) /** * Escape values with quotes when needed - * @todo use is_number instead of ctype_digit and intval - * @todo get column data type, use it to quote results * * @param string $tableName Name of table which contains rows * @param array $row Associative array of column names and values to be quoted @@ -586,14 +586,18 @@ private function escape($tableName, $row) foreach ($row as $colName => $colValue) { if (is_null($colValue)) { $ret[] = "NULL"; - } elseif ($columnTypes[$colName]['is_numeric']) { - $ret[] = $colValue; } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) { - if (empty($colValue)) { - $ret[] = "''"; + if ($columnTypes[$colName]['type'] == 'bit') { + $ret[] = '0x' . bin2hex(chr($colValue)); } else { - $ret[] = '0x' . bin2hex($colValue); + if (empty($colValue)) { + $ret[] = "''"; + } else { + $ret[] = '0x' . bin2hex($colValue); + } } + } elseif ($columnTypes[$colName]['is_numeric']) { + $ret[] = $colValue; } else { $ret[] = $this->dbHandler->quote($colValue); } diff --git a/tests/test.sh b/tests/test.sh index 29bf36ea..d9aaba3e 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -1,19 +1,24 @@ mysql -uroot < original.sql mysqldump -uroot test001 --extended-insert=false --hex-blob=true > mysqldump.sql +php test.php cat original.sql | grep ^INSERT > original.filtered.sql cat mysqldump.sql | grep ^INSERT > mysqldump.filtered.sql +cat mysqldump-php.sql | grep ^INSERT > mysqldump-php.filtered.sql diff original.filtered.sql mysqldump.filtered.sql +ret1=$? -ret=$? +diff original.filtered.sql mysqldump-php.filtered.sql +ret2=$? rm original.filtered.sql rm mysqldump.filtered.sql +rm mysqldump-php.filtered.sql +rm mysqldump-php.sql rm mysqldump.sql -echo $ret - +exit $ret1 From 719da0a0472c0e327fc85119b2bab35bcc8e3b65 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Fri, 12 Sep 2014 23:42:37 +0000 Subject: [PATCH 20/29] fix create view --- src/Ifsnop/Mysqldump/Mysqldump.php | 77 +++++++++++++++++++++++------- tests/original.sql | 4 +- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index dd88fa14..7727722b 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -525,25 +525,24 @@ private function parseColumnType($colType) */ private function getViewStructure($viewName) { - $stmt = $this->typeAdapter->show_create_view($viewName); - foreach ($this->dbHandler->query($stmt) as $r) { - if (isset($r['Create View'])) { - if (!$this->dumpSettings['no-create-info']) { + if (!$this->dumpSettings['no-create-info']) { + $ret = "--" . PHP_EOL . + "-- Table structure for view `${viewName}`" . PHP_EOL . + "--" . PHP_EOL . PHP_EOL; + $this->compressManager->write($ret); + + $stmt = $this->typeAdapter->show_create_view($viewName); + foreach ($this->dbHandler->query($stmt) as $r) { + if ($this->dumpSettings['add-drop-table']) { $this->compressManager->write( - "--" . PHP_EOL . - "-- Table structure for view `$viewName`" . PHP_EOL . - "--" . PHP_EOL . PHP_EOL + $this->typeAdapter->drop_view($viewName) ); - if ($this->dumpSettings['add-drop-table']) { - $this->compressManager->write( - $this->typeAdapter->drop_view($viewName) - ); - } - $this->compressManager->write($r['Create View'] . ";" . PHP_EOL . PHP_EOL); } - return; + $this->compressManager->write( + $this->typeAdapter->create_view($r) + ); + break; } - throw new Exception("Error getting view structure, unknown output"); } } @@ -588,12 +587,12 @@ private function escape($tableName, $row) $ret[] = "NULL"; } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) { if ($columnTypes[$colName]['type'] == 'bit') { - $ret[] = '0x' . bin2hex(chr($colValue)); + $ret[] = '0x' . strtoupper(bin2hex(chr($colValue))); } else { if (empty($colValue)) { $ret[] = "''"; } else { - $ret[] = '0x' . bin2hex($colValue); + $ret[] = '0x' . strtoupper(bin2hex($colValue)); } } } elseif ($columnTypes[$colName]['is_numeric']) { @@ -907,6 +906,15 @@ public function show_create_view($viewName) "WHERE type='view' AND tbl_name='$viewName'"; } + /** + * function create_view Get view creation code from database + * @todo make it do something with sqlite + */ + public function create_view($row) + { + return ""; + } + /** * function show_create_trigger Get trigger creation code from database * @todo make it do something with sqlite @@ -1095,10 +1103,43 @@ public function create_table($row) if (isset($row['Create Table'])) { $ret = $row['Create Table'] . ";" . PHP_EOL . PHP_EOL; } else { - throw new Exception("Error getting trigger code, unknown output"); + throw new Exception("Error getting table code, unknown output"); } return $ret; + } + public function create_view($row) + { + $ret = ""; + if (isset($row['Create View'])) { + $triggerStmt = $row['Create View']; + $triggerStmtReplaced1 = str_replace( + "CREATE ALGORITHM", + "/*!50001 CREATE ALGORITHM", + $triggerStmt + ); + $triggerStmtReplaced2 = str_replace( + " DEFINER=", + " */" . PHP_EOL . "/*!50013 DEFINER=", + $triggerStmtReplaced1 + ); + $triggerStmtReplaced3 = str_replace( + " VIEW ", + " */" . PHP_EOL . "/*!50001 VIEW ", + $triggerStmtReplaced2 + ); + if (false === $triggerStmtReplaced1 || + false === $triggerStmtReplaced2 || + false === $triggerStmtReplaced3) { + $triggerStmtReplaced = $triggerStmt; + } else { + $triggerStmtReplaced = $triggerStmtReplaced3 . " */;"; + } + $ret .= $triggerStmtReplaced . PHP_EOL . PHP_EOL; + return $ret; + } else { + throw new Exception("Error getting view structure, unknown output"); + } } public function create_trigger($row) diff --git a/tests/original.sql b/tests/original.sql index 607dbb24..1fa54369 100644 --- a/tests/original.sql +++ b/tests/original.sql @@ -5,7 +5,7 @@ USE `test001`; DROP TABLE IF EXISTS `test000`; CREATE TABLE `test000` ( `id` int, - `col01` bit(1) DEFAULT NULL, + `col01` bit(6) DEFAULT NULL, `col02` tinyint(4) DEFAULT NULL, `col03` tinyint(4) UNSIGNED DEFAULT NULL, `col10` bigint DEFAULT NULL, @@ -13,7 +13,7 @@ CREATE TABLE `test000` ( `col15` double DEFAULT NULL, `col27` varchar(6) DEFAULT NULL ); -INSERT INTO `test000` VALUES (1,0x01,-128,255,-9223372036854775808,18446744073709551615,-2.2250738585072014e-308,'0abcde'); +INSERT INTO `test000` VALUES (1,0x21,-128,255,-9223372036854775808,18446744073709551615,-2.2250738585072014e-308,'0abcde'); DROP TABLE IF EXISTS `test001`; CREATE TABLE `test001` ( From 43db1879d16bc64cdc0891952eab22708288dfec Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sat, 13 Sep 2014 09:32:13 +0000 Subject: [PATCH 21/29] make tests ignore lowercase hex-blobs --- tests/original.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/original.sql b/tests/original.sql index 1fa54369..ef9162ea 100644 --- a/tests/original.sql +++ b/tests/original.sql @@ -97,7 +97,7 @@ CREATE TABLE `test029` ( `id` int, `col` blob NOT NULL ); -INSERT INTO `test029` VALUES (1,0x000102030405060708090A0B0C0D0E0F); +INSERT INTO `test029` VALUES (1,0x0001020304050607080990919293949596979899); INSERT INTO `test029` VALUES (2,''); DROP TABLE IF EXISTS `test033`; From ff16704dd1561660049e923bdd9998a0fc925e9d Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sat, 13 Sep 2014 09:33:14 +0000 Subject: [PATCH 22/29] enable testing mysqldump-php class, and compare output with mysqldump and original sql file --- tests/test.php | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/test.php diff --git a/tests/test.php b/tests/test.php new file mode 100644 index 00000000..c57bc3ea --- /dev/null +++ b/tests/test.php @@ -0,0 +1,35 @@ + IMysqldump\Mysqldump::NONE, + 'no-data' => false, + 'add-drop-table' => true, + 'single-transaction' => false, + 'lock-tables' => true, + 'add-locks' => true, + 'extended-insert' => false, + 'disable-foreign-keys-check' => false, + 'skip-triggers' => false, + 'add-drop-trigger' => true, + 'databases' => false, + 'add-drop-database' => false, + 'hex-blob' => true + ); + +$dump = new IMysqldump\Mysqldump( + "test001", + "travis", + "", + "localhost", + "mysql", + $dumpSettings); + +$dump->start("mysqldump-php.sql"); + +exit; From a1c8abbacf05514cc59ddd617b4984f1df6cc767 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sat, 13 Sep 2014 09:46:47 +0000 Subject: [PATCH 23/29] check more errors with tests --- src/Ifsnop/Mysqldump/Mysqldump.php | 2 +- tests/test.sh | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 7727722b..ea8c1467 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -525,7 +525,7 @@ private function parseColumnType($colType) */ private function getViewStructure($viewName) { - if (!$this->dumpSettings['no-create-info']) { + if (false === $this->dumpSettings['no-create-info']) { $ret = "--" . PHP_EOL . "-- Table structure for view `${viewName}`" . PHP_EOL . "--" . PHP_EOL . PHP_EOL; diff --git a/tests/test.sh b/tests/test.sh index d9aaba3e..a2c935a5 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -1,16 +1,27 @@ +#!/bin/bash + +for i in $(seq 0 20) ; do + ret[i]=-1 +done + mysql -uroot < original.sql +ret[0]=$? + mysqldump -uroot test001 --extended-insert=false --hex-blob=true > mysqldump.sql +ret[1]=$? + php test.php +ret[2]=$? cat original.sql | grep ^INSERT > original.filtered.sql cat mysqldump.sql | grep ^INSERT > mysqldump.filtered.sql cat mysqldump-php.sql | grep ^INSERT > mysqldump-php.filtered.sql diff original.filtered.sql mysqldump.filtered.sql -ret1=$? +ret[3]=$? diff original.filtered.sql mysqldump-php.filtered.sql -ret2=$? +ret[4]=$? rm original.filtered.sql rm mysqldump.filtered.sql @@ -18,7 +29,9 @@ rm mysqldump-php.filtered.sql rm mysqldump-php.sql rm mysqldump.sql -exit $ret1 - - +total=0 +for i in $(seq 0 4) ; do + total=(${ret[i]} + $total) +done +exit $total From a687c825e1571815fd7c939845da17ee994d6ab6 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sat, 13 Sep 2014 10:18:06 +0000 Subject: [PATCH 24/29] moved all database specific code to typeAdapter class --- src/Ifsnop/Mysqldump/Mysqldump.php | 205 +++++++++++++++-------------- tests/test.php | 8 +- 2 files changed, 113 insertions(+), 100 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index ea8c1467..1d51076c 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -41,33 +41,6 @@ class Mysqldump const BZIP2 = 'Bzip2'; const NONE = 'None'; - // Numerical Mysql types - public $mysqlTypes = array( - 'numerical' => array( - 'bit', - 'tinyint', - 'smallint', - 'mediumint', - 'int', - 'integer', - 'bigint', - 'real', - 'double', - 'float', - 'decimal', - 'numeric' - ), - 'blob' => array( - 'tinyblob', - 'blob', - 'mediumblob', - 'longblob', - 'binary', - 'varbinary', - 'bit' - ) - ); - // This can be set both on constructor or manually public $host; public $user; @@ -259,7 +232,7 @@ public function start($filename = '') if ($this->dumpSettings['databases']) { $this->compressManager->write( - $this->typeAdapter->databases($this->db) + $this->typeAdapter->databases($this->db, $this->dumpSettings) ); } @@ -414,12 +387,14 @@ private function exportTables() */ private function exportViews() { - // Exporting views one by one - foreach ($this->views as $view) { - if (in_array($view, $this->dumpSettings['exclude-tables'], true)) { - continue; + if (false === $this->dumpSettings['no-create-info']) { + // Exporting views one by one + foreach ($this->views as $view) { + if (in_array($view, $this->dumpSettings['exclude-tables'], true)) { + continue; + } + $this->getViewStructure($view); } - $this->getViewStructure($view); } } @@ -430,13 +405,12 @@ private function exportViews() */ private function exportTriggers() { - // Exporting views one by one + // Exporting triggers one by one foreach ($this->triggers as $trigger) { $this->getTriggerStructure($trigger); } } - /** * Table structure extractor * @@ -446,28 +420,23 @@ private function exportTriggers() */ private function getTableStructure($tableName) { - $stmt = $this->typeAdapter->show_create_table($tableName); - foreach ($this->dbHandler->query($stmt) as $r) { - if (isset($r['Create Table'])) { - if (!$this->dumpSettings['no-create-info']) { - $this->compressManager->write( - "--" . PHP_EOL . - "-- Table structure for table `$tableName`" . PHP_EOL . - "--" . PHP_EOL . PHP_EOL - ); - if ($this->dumpSettings['add-drop-table']) { - $this->compressManager->write( - $this->typeAdapter->drop_table($tableName) - ); - } + if (!$this->dumpSettings['no-create-info']) { + $ret = "--" . PHP_EOL . + "-- Table structure for table `$tableName`" . PHP_EOL . + "--" . PHP_EOL . PHP_EOL; + $stmt = $this->typeAdapter->show_create_table($tableName); + foreach ($this->dbHandler->query($stmt) as $r) { + $this->compressManager->write($ret); + if ($this->dumpSettings['add-drop-table']) { $this->compressManager->write( - $this->typeAdapter->create_table($r) + $this->typeAdapter->drop_table($tableName) ); } - + $this->compressManager->write( + $this->typeAdapter->create_table($r) + ); break; } - throw new Exception("Error getting table structure, unknown output"); } $columnTypes = array(); @@ -477,7 +446,7 @@ private function getTableStructure($tableName) ); foreach($columns as $key => $col) { - $types = $this->parseColumnType($col); + $types = $this->typeAdapter->parseColumnType($col); $columnTypes[$col['Field']] = array( 'is_numeric'=> $types['is_numeric'], 'is_blob' => $types['is_blob'], @@ -488,33 +457,6 @@ private function getTableStructure($tableName) return; } - /** - * Decode column metadata and fill info structure. - * type, is_numeric and is_blob will always be available. - * - * @param array $colType Array returned from "SHOW COLUMNS FROM tableName" - * @return array - */ - private function parseColumnType($colType) - { - $colInfo = array(); - $colParts = explode(" ", $colType['Type']); - - if($fparen = strpos($colParts[0], "(")) - { - $colInfo['type'] = substr($colParts[0], 0, $fparen); - $colInfo['length'] = str_replace(")", "", substr($colParts[0], $fparen+1)); - $colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL; - } - else - { - $colInfo['type'] = $colParts[0]; - } - $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlTypes['numerical']); - $colInfo['is_blob'] = in_array($colInfo['type'], $this->mysqlTypes['blob']); - - return $colInfo; - } /** * View structure extractor @@ -525,24 +467,21 @@ private function parseColumnType($colType) */ private function getViewStructure($viewName) { - if (false === $this->dumpSettings['no-create-info']) { - $ret = "--" . PHP_EOL . - "-- Table structure for view `${viewName}`" . PHP_EOL . - "--" . PHP_EOL . PHP_EOL; - $this->compressManager->write($ret); - - $stmt = $this->typeAdapter->show_create_view($viewName); - foreach ($this->dbHandler->query($stmt) as $r) { - if ($this->dumpSettings['add-drop-table']) { - $this->compressManager->write( - $this->typeAdapter->drop_view($viewName) - ); - } + $ret = "--" . PHP_EOL . + "-- Table structure for view `${viewName}`" . PHP_EOL . + "--" . PHP_EOL . PHP_EOL; + $this->compressManager->write($ret); + $stmt = $this->typeAdapter->show_create_view($viewName); + foreach ($this->dbHandler->query($stmt) as $r) { + if ($this->dumpSettings['add-drop-table']) { $this->compressManager->write( - $this->typeAdapter->create_view($r) + $this->typeAdapter->drop_view($viewName) ); - break; } + $this->compressManager->write( + $this->typeAdapter->create_view($r) + ); + break; } } @@ -1029,6 +968,18 @@ public function drop_view() return PHP_EOL; } + /** + * Decode column metadata and fill info structure. + * type, is_numeric and is_blob will always be available. + * + * @param array $colType Array returned from "SHOW COLUMNS FROM tableName" + * @return array + */ + public function parseColumnType($colType) + { + return array(); + } + } class TypeAdapterPgsql extends TypeAdapterFactory @@ -1048,6 +999,33 @@ class TypeAdapterMysql extends TypeAdapterFactory private $dbHandler = null; + // Numerical Mysql types + public $mysqlTypes = array( + 'numerical' => array( + 'bit', + 'tinyint', + 'smallint', + 'mediumint', + 'int', + 'integer', + 'bigint', + 'real', + 'double', + 'float', + 'decimal', + 'numeric' + ), + 'blob' => array( + 'tinyblob', + 'blob', + 'mediumblob', + 'longblob', + 'binary', + 'varbinary', + 'bit' + ) + ); + public function __construct ($dbHandler) { $this->dbHandler = $dbHandler; @@ -1055,11 +1033,13 @@ public function __construct ($dbHandler) public function databases() { - if (func_num_args() != 1) { + if (func_num_args() != 2) { return ""; } $args = func_get_args(); + $databaseName = $args[0]; + $dumpSettings = $args[1]; $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'character_set_database';"); $characterSet = $resultSet->fetchColumn(1); @@ -1070,8 +1050,8 @@ public function databases() $resultSet->closeCursor(); $ret = ""; - if (false === $this->dumpSettings['add-drop-database']) { - $ret = $this->getDatabaseHeader(); + if (false === $dumpSettings['add-drop-database']) { // if wasn't included before... + $ret = $this->getDatabaseHeader(); // include headers now } $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `${args[0]}`". @@ -1354,4 +1334,33 @@ public function getDatabaseHeader() { "-- Current Database: `${args[0]}`" . PHP_EOL . "--" . PHP_EOL . PHP_EOL; } + + /** + * Decode column metadata and fill info structure. + * type, is_numeric and is_blob will always be available. + * + * @param array $colType Array returned from "SHOW COLUMNS FROM tableName" + * @return array + */ + public function parseColumnType($colType) + { + $colInfo = array(); + $colParts = explode(" ", $colType['Type']); + + if($fparen = strpos($colParts[0], "(")) + { + $colInfo['type'] = substr($colParts[0], 0, $fparen); + $colInfo['length'] = str_replace(")", "", substr($colParts[0], $fparen+1)); + $colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL; + } + else + { + $colInfo['type'] = $colParts[0]; + } + $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlTypes['numerical']); + $colInfo['is_blob'] = in_array($colInfo['type'], $this->mysqlTypes['blob']); + + return $colInfo; + } + } diff --git a/tests/test.php b/tests/test.php index c57bc3ea..c1039182 100644 --- a/tests/test.php +++ b/tests/test.php @@ -9,17 +9,21 @@ $dumpSettings = array( 'compress' => IMysqldump\Mysqldump::NONE, 'no-data' => false, + 'add-drop-table' => true, 'single-transaction' => false, 'lock-tables' => true, 'add-locks' => true, 'extended-insert' => false, - 'disable-foreign-keys-check' => false, + 'disable-keys' => true, + 'disable-foreign-keys-check' => true, 'skip-triggers' => false, 'add-drop-trigger' => true, 'databases' => false, 'add-drop-database' => false, - 'hex-blob' => true + 'hex-blob' => true, + 'no-create-info' => false, + 'where' => '' ); $dump = new IMysqldump\Mysqldump( From 0dea65e1bac747e0defbcd31550b12d60d49b7f6 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sat, 13 Sep 2014 10:29:28 +0000 Subject: [PATCH 25/29] we really don't need uppercase hex if that means being slower --- src/Ifsnop/Mysqldump/Mysqldump.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 1d51076c..cf96e878 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -526,12 +526,12 @@ private function escape($tableName, $row) $ret[] = "NULL"; } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) { if ($columnTypes[$colName]['type'] == 'bit') { - $ret[] = '0x' . strtoupper(bin2hex(chr($colValue))); + $ret[] = '0x' . bin2hex(chr($colValue)); } else { if (empty($colValue)) { $ret[] = "''"; } else { - $ret[] = '0x' . strtoupper(bin2hex($colValue)); + $ret[] = '0x' . bin2hex($colValue); } } } elseif ($columnTypes[$colName]['is_numeric']) { From c5c9c2eb66643c3bc371a3a7249f26bd1e8a3ee2 Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sat, 13 Sep 2014 10:41:59 +0000 Subject: [PATCH 26/29] fix hhvm bug (mysql column type returns between mysql drivers is not standard) --- src/Ifsnop/Mysqldump/Mysqldump.php | 29 +++++++++++++++++++++-------- tests/original.sql | 2 +- tests/test.php | 5 +++++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index cf96e878..835c9bb4 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -526,12 +526,12 @@ private function escape($tableName, $row) $ret[] = "NULL"; } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) { if ($columnTypes[$colName]['type'] == 'bit') { - $ret[] = '0x' . bin2hex(chr($colValue)); + $ret[] = "0x${colValue}"; } else { if (empty($colValue)) { $ret[] = "''"; } else { - $ret[] = '0x' . bin2hex($colValue); + $ret[] = "0x${colValue}"; } } } elseif ($columnTypes[$colName]['is_numeric']) { @@ -580,7 +580,20 @@ private function listValues($tableName) $onlyOnce = true; $lineSize = 0; - $stmt = "SELECT * FROM `$tableName`"; + $colStmt = array(); + + foreach($this->tableColumnTypes[$tableName] as $colName => $colType) { + if ($colType['type'] == 'bit' && $this->dumpSettings['hex-blob']) { + $colStmt[] = "LPAD(HEX(`${colName}`),2,'0') AS `${colName}`"; + } else if ($colType['is_blob'] && $this->dumpSettings['hex-blob']) { + $colStmt[] = "HEX(`${colName}`) AS `${colName}`"; + } else { + $colStmt[] = "`${colName}`"; + } + } + $colStmt = implode($colStmt, ","); + $stmt = "SELECT $colStmt FROM `$tableName`"; + if ($this->dumpSettings['where']) { $stmt .= " WHERE {$this->dumpSettings['where']}"; } @@ -1054,10 +1067,10 @@ public function databases() $ret = $this->getDatabaseHeader(); // include headers now } - $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `${args[0]}`". + $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `${databaseName}`". " /*!40100 DEFAULT CHARACTER SET " . $characterSet . - " COLLATE " . $collationDb . "*/;" . PHP_EOL . PHP_EOL . - "USE `${args[0]}`;" . PHP_EOL . PHP_EOL; + " COLLATE ${collationDb} */;" . PHP_EOL . PHP_EOL . + "USE `${databaseName}`;" . PHP_EOL . PHP_EOL; return $ret; } @@ -1081,7 +1094,7 @@ public function create_table($row) { $ret = ""; if (isset($row['Create Table'])) { - $ret = $row['Create Table'] . ";" . PHP_EOL . PHP_EOL; + $ret .= $row['Create Table'] . ";" . PHP_EOL . PHP_EOL; } else { throw new Exception("Error getting table code, unknown output"); } @@ -1140,7 +1153,7 @@ public function create_trigger($row) if ( false === $triggerStmtReplaced ) { $triggerStmtReplaced = $triggerStmt; } - $ret = "DELIMITER ;;" . PHP_EOL . + $ret .= "DELIMITER ;;" . PHP_EOL . $triggerStmtReplaced . "*/;;" . PHP_EOL . "DELIMITER ;" . PHP_EOL . PHP_EOL; } else { diff --git a/tests/original.sql b/tests/original.sql index ef9162ea..e98f5259 100644 --- a/tests/original.sql +++ b/tests/original.sql @@ -97,7 +97,7 @@ CREATE TABLE `test029` ( `id` int, `col` blob NOT NULL ); -INSERT INTO `test029` VALUES (1,0x0001020304050607080990919293949596979899); +INSERT INTO `test029` VALUES (1,0x00010203040506070809909192939495969798A9); INSERT INTO `test029` VALUES (2,''); DROP TABLE IF EXISTS `test033`; diff --git a/tests/test.php b/tests/test.php index c1039182..ca0fd9d2 100644 --- a/tests/test.php +++ b/tests/test.php @@ -1,4 +1,9 @@ " . bin2hex(chr($i)) . "<" . PHP_EOL; +} +*/ error_reporting(E_ALL); From afaa56c8ce8e6fcb5725d8501c8a794d615d13ce Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Sun, 14 Sep 2014 20:38:50 +0000 Subject: [PATCH 27/29] The call to PDO::query() has too many arguments starting with PDO::FETCH_ASSOC --- src/Ifsnop/Mysqldump/Mysqldump.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 835c9bb4..186b7905 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -441,9 +441,9 @@ private function getTableStructure($tableName) $columnTypes = array(); $columns = $this->dbHandler->query( - $this->typeAdapter->show_columns($tableName), - PDO::FETCH_ASSOC + $this->typeAdapter->show_columns($tableName) ); + $columns->setFetchMode(PDO::FETCH_ASSOC); foreach($columns as $key => $col) { $types = $this->typeAdapter->parseColumnType($col); From f67ff4d23392a1f76096e78ae4f6d16275b2b35b Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Mon, 15 Sep 2014 14:40:23 +0000 Subject: [PATCH 28/29] added some consts to documentation --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fe7681ef..238680c6 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,8 @@ Plain old PHP: - **exclude-tables** - Exclude these tables (array of table names) - **compress** - - Gzip, Bzip2, None + - Gzip, Bzip2, None. + - Could be specified using the declared consts: IMysqldump\Mysqldump::GZIP, IMysqldump\Mysqldump::BZIP2 or IMysqldump\Mysqldump::NONE - **no-data** - http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_no-data - **add-drop-table** From 7c50021be01f4dd62971911aa4f44397dd88ff7d Mon Sep 17 00:00:00 2001 From: Diego Torres Date: Tue, 16 Sep 2014 20:15:30 +0000 Subject: [PATCH 29/29] dump of triggers Closes #66 --- src/Ifsnop/Mysqldump/Mysqldump.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Ifsnop/Mysqldump/Mysqldump.php b/src/Ifsnop/Mysqldump/Mysqldump.php index 186b7905..95444c6c 100644 --- a/src/Ifsnop/Mysqldump/Mysqldump.php +++ b/src/Ifsnop/Mysqldump/Mysqldump.php @@ -311,7 +311,6 @@ private function getFooter() private function getDatabaseStructure() { // Listing all tables from database - $this->tables = array(); if (empty($this->dumpSettings['include-tables'])) { // include all tables for now, blacklisting happens later foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->db)) as $row) { @@ -332,7 +331,6 @@ private function getDatabaseStructure() } // Listing all views from database - $this->views = array(); if (empty($this->dumpSettings['include-tables'])) { // include all views for now, blacklisting happens later foreach ($this->dbHandler->query($this->typeAdapter->show_views($this->db)) as $row) { @@ -353,7 +351,6 @@ private function getDatabaseStructure() } // Listing all triggers from database - $this->triggers = array(); if (false === $this->dumpSettings['skip-triggers']) { foreach ($this->dbHandler->query($this->typeAdapter->show_triggers($this->db)) as $row) { array_push($this->triggers, $row['Trigger']);