· 10 years ago · Nov 16, 2015, 05:21 PM
1<?php
2/**
3 * Mysqldump File Doc Comment
4 *
5 * PHP version 5
6 *
7 * @category Library
8 * @package Ifsnop\Mysqldump
9 * @author Michael J. Calkins <clouddueling@github.com>
10 * @author Diego Torres <ifsnop@github.com>
11 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
12 * @link https://github.com/ifsnop/mysqldump-php
13 *
14 */
15
16namespace Ifsnop\Mysqldump;
17
18use Exception;
19use PDO;
20use PDOException;
21
22/**
23 * Mysqldump Class Doc Comment
24 *
25 * @category Library
26 * @package Ifsnop\Mysqldump
27 * @author Michael J. Calkins <clouddueling@github.com>
28 * @author Diego Torres <ifsnop@github.com>
29 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
30 * @link https://github.com/ifsnop/mysqldump-php
31 *
32 */
33class Mysqldump
34{
35
36 // Same as mysqldump
37 const MAXLINESIZE = 1000000;
38
39 // Available compression methods as constants
40 const GZIP = 'Gzip';
41 const BZIP2 = 'Bzip2';
42 const NONE = 'None';
43
44 // Available connection strings
45 const UTF8 = 'utf8';
46 const UTF8MB4 = 'utf8mb4';
47
48 /**
49 * Database username
50 * @var string
51 */
52 public $user;
53 /**
54 * Database password
55 * @var string
56 */
57 public $pass;
58 /**
59 * Connection string for PDO
60 * @var string
61 */
62 public $dsn;
63 /**
64 * Destination filename, defaults to stdout
65 * @var string
66 */
67 public $fileName = 'php://output';
68
69 // Internal stuff
70 private $tables = array();
71 private $views = array();
72 private $triggers = array();
73 private $procedures = array();
74 private $dbHandler;
75 private $dbType;
76 private $compressManager;
77 private $typeAdapter;
78 private $dumpSettings = array();
79 private $pdoSettings = array();
80 private $version;
81 private $tableColumnTypes = array();
82 /**
83 * database name, parsed from dsn
84 * @var string
85 */
86 private $dbName;
87 /**
88 * host name, parsed from dsn
89 * @var string
90 */
91 private $host;
92 /**
93 * dsn string parsed as an array
94 * @var array
95 */
96 private $dsnArray = array();
97
98 /**
99 * Constructor of Mysqldump. Note that in the case of an SQLite database
100 * connection, the filename must be in the $db parameter.
101 *
102 * @param string $dsn PDO DSN connection string
103 * @param string $user SQL account username
104 * @param string $pass SQL account password
105 * @param array $dumpSettings SQL database settings
106 * @param array $pdoSettings PDO configured attributes
107 */
108 public function __construct(
109 $dsn = '',
110 $user = '',
111 $pass = '',
112 $dumpSettings = array(),
113 $pdoSettings = array()
114 ) {
115 $dumpSettingsDefault = array(
116 'include-tables' => array(),
117 'exclude-tables' => array(),
118 'compress' => Mysqldump::NONE,
119 'no-data' => false,
120 'add-drop-table' => false,
121 'single-transaction' => true,
122 'lock-tables' => true,
123 'add-locks' => true,
124 'extended-insert' => true,
125 'disable-keys' => true,
126 'where' => '',
127 'no-create-info' => false,
128 'skip-triggers' => false,
129 'add-drop-trigger' => true,
130 'routines' => false,
131 'hex-blob' => true, /* faster than escaped content */
132 'databases' => false,
133 'add-drop-database' => false,
134 'skip-tz-utc' => false,
135 'no-autocommit' => true,
136 'default-character-set' => Mysqldump::UTF8,
137 'skip-comments' => false,
138 'skip-dump-date' => false,
139 'init_commands' => array(),
140 /* deprecated */
141 'disable-foreign-keys-check' => true
142 );
143
144 $pdoSettingsDefault = array(
145 PDO::ATTR_PERSISTENT => true,
146 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
147 PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false
148 );
149
150 $this->user = $user;
151 $this->pass = $pass;
152 $this->parseDsn($dsn);
153 $this->pdoSettings = self::array_replace_recursive($pdoSettingsDefault, $pdoSettings);
154 $this->dumpSettings = self::array_replace_recursive($dumpSettingsDefault, $dumpSettings);
155
156 $this->dumpSettings['init_commands'][] = "SET NAMES " . $this->dumpSettings['default-character-set'];
157
158 if (false === $this->dumpSettings['skip-tz-utc']) {
159 $this->dumpSettings['init_commands'][] = "SET TIME_ZONE='+00:00'";
160 }
161
162 $diff = array_diff(array_keys($this->dumpSettings), array_keys($dumpSettingsDefault));
163 if (count($diff)>0) {
164 throw new Exception("Unexpected value in dumpSettings: (" . implode(",", $diff) . ")");
165 }
166
167 // Create a new compressManager to manage compressed output
168 $this->compressManager = CompressManagerFactory::create($this->dumpSettings['compress']);
169 }
170
171 /**
172 * Custom array_replace_recursive to be used if PHP < 5.3
173 * Replaces elements from passed arrays into the first array recursively
174 *
175 * @param array $array1 The array in which elements are replaced
176 * @param array $array2 The array from which elements will be extracted
177 *
178 * @return array Returns an array, or NULL if an error occurs.
179 */
180 public static function array_replace_recursive($array1, $array2)
181 {
182 if (function_exists('array_replace_recursive')) {
183 return array_replace_recursive($array1, $array2);
184 }
185
186 foreach ($array2 as $key => $value) {
187 if (is_array($value)) {
188 $array1[$key] = self::array_replace_recursive($array1[$key], $value);
189 } else {
190 $array1[$key] = $value;
191 }
192 }
193 return $array1;
194 }
195
196 /**
197 * Parse DSN string and extract dbname value
198 * Several examples of a DSN string
199 * mysql:host=localhost;dbname=testdb
200 * mysql:host=localhost;port=3307;dbname=testdb
201 * mysql:unix_socket=/tmp/mysql.sock;dbname=testdb
202 *
203 * @param string $dsn dsn string to parse
204 */
205 private function parseDsn($dsn)
206 {
207 if (empty($dsn) || (false === ($pos = strpos($dsn, ":")))) {
208 throw new Exception("Empty DSN string");
209 }
210
211 $this->dsn = $dsn;
212 $this->dbType = strtolower(substr($dsn, 0, $pos));
213
214 if (empty($this->dbType)) {
215 throw new Exception("Missing database type from DSN string");
216 }
217
218 $dsn = substr($dsn, $pos + 1);
219
220 foreach(explode(";", $dsn) as $kvp) {
221 $kvpArr = explode("=", $kvp);
222 $this->dsnArray[strtolower($kvpArr[0])] = $kvpArr[1];
223 }
224
225 if (empty($this->dsnArray['host']) &&
226 empty($this->dsnArray['unix_socket'])) {
227 throw new Exception("Missing host from DSN string");
228 }
229 $this->host = (!empty($this->dsnArray['host'])) ?
230 $this->dsnArray['host'] :
231 $this->dsnArray['unix_socket'];
232
233 if (empty($this->dsnArray['dbname'])) {
234 throw new Exception("Missing database name from DSN string");
235 }
236
237 $this->dbName = $this->dsnArray['dbname'];
238
239 return true;
240 }
241
242 /**
243 * Connect with PDO
244 *
245 * @return null
246 */
247 private function connect()
248 {
249 // Connecting with PDO
250 try {
251 switch ($this->dbType) {
252 case 'sqlite':
253 $this->dbHandler = @new PDO("sqlite:" . $this->dbName, null, null, $this->pdoSettings);
254 break;
255 case 'mysql':
256 case 'pgsql':
257 case 'dblib':
258 $this->dbHandler = @new PDO(
259 $this->dsn,
260 $this->user,
261 $this->pass,
262 $this->pdoSettings
263 );
264 // Execute init commands once connected
265 foreach($this->dumpSettings['init_commands'] as $stmt) {
266 $this->dbHandler->exec($stmt);
267 }
268 // Store server version
269 $this->version = $this->dbHandler->getAttribute(PDO::ATTR_SERVER_VERSION);
270 break;
271 default:
272 throw new Exception("Unsupported database type (" . $this->dbType . ")");
273 }
274 } catch (PDOException $e) {
275 throw new Exception(
276 "Connection to " . $this->dbType . " failed with message: " .
277 $e->getMessage()
278 );
279 }
280
281 $this->dbHandler->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_NATURAL);
282 $this->typeAdapter = TypeAdapterFactory::create($this->dbType, $this->dbHandler);
283 }
284
285 /**
286 * Main call
287 *
288 * @param string $filename Name of file to write sql dump to
289 * @return null
290 */
291 public function start($filename = '')
292 {
293 // Output file can be redefined here
294 if (!empty($filename)) {
295 $this->fileName = $filename;
296 }
297
298 // Connect to database
299 $this->connect();
300
301 // Create output file
302 $this->compressManager->open($this->fileName);
303
304 // Write some basic info to output file
305 $this->compressManager->write($this->getDumpFileHeader());
306
307 // Store server settings and use sanner defaults to dump
308 $this->compressManager->write(
309 $this->typeAdapter->backup_parameters($this->dumpSettings)
310 );
311
312 if ($this->dumpSettings['databases']) {
313 $this->compressManager->write(
314 $this->typeAdapter->getDatabaseHeader($this->dbName)
315 );
316 if ($this->dumpSettings['add-drop-database']) {
317 $this->compressManager->write(
318 $this->typeAdapter->add_drop_database($this->dbName)
319 );
320 }
321 }
322
323 // Get table, view and trigger structures from database
324 $this->getDatabaseStructure();
325
326 if ($this->dumpSettings['databases']) {
327 $this->compressManager->write(
328 $this->typeAdapter->databases($this->dbName)
329 );
330 }
331
332 // If there still are some tables/views in include-tables array,
333 // that means that some tables or views weren't found.
334 // Give proper error and exit.
335 if (0 < count($this->dumpSettings['include-tables'])) {
336 $name = implode(",", $this->dumpSettings['include-tables']);
337 throw new Exception("Table or View (" . $name . ") not found in database");
338 }
339
340 $this->exportTables();
341 $this->exportViews();
342 $this->exportTriggers();
343 $this->exportProcedures();
344
345 // Restore saved parameters
346 $this->compressManager->write(
347 $this->typeAdapter->restore_parameters($this->dumpSettings)
348 );
349 // Write some stats to output file
350 $this->compressManager->write($this->getDumpFileFooter());
351 // Close output file
352 $this->compressManager->close();
353 }
354
355 /**
356 * Returns header for dump file
357 *
358 * @return string
359 */
360 private function getDumpFileHeader()
361 {
362 $header = '';
363 if (!$this->dumpSettings['skip-comments']) {
364 // Some info about software, source and time
365 $header = "-- mysqldump-php https://github.com/ifsnop/mysqldump-php" . PHP_EOL .
366 "--" . PHP_EOL .
367 "-- Host: {$this->host}\tDatabase: {$this->dbName}" . PHP_EOL .
368 "-- ------------------------------------------------------" . PHP_EOL;
369
370 if (!empty($this->version)) {
371 $header .= "-- Server version \t" . $this->version . PHP_EOL;
372 }
373
374 $header .= "-- Date: " . date('r') . PHP_EOL . PHP_EOL;
375 }
376 return $header;
377 }
378
379 /**
380 * Returns footer for dump file
381 *
382 * @return string
383 */
384 private function getDumpFileFooter()
385 {
386 $footer = '';
387 if (!$this->dumpSettings['skip-comments']) {
388 $footer .= '-- Dump completed';
389 if (!$this->dumpSettings['skip-dump-date']) {
390 $footer .= ' on: ' . date('r');
391 }
392 $footer .= PHP_EOL;
393 }
394
395 return $footer;
396 }
397
398 /**
399 * Reads table and views names from database.
400 * Fills $this->tables array so they will be dumped later.
401 *
402 * @return null
403 */
404 private function getDatabaseStructure()
405 {
406 // Listing all tables from database
407 if (empty($this->dumpSettings['include-tables'])) {
408 // include all tables for now, blacklisting happens later
409 foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->dbName)) as $row) {
410 array_push($this->tables, current($row));
411 }
412 } else {
413 // include only the tables mentioned in include-tables
414 foreach ($this->dbHandler->query($this->typeAdapter->show_tables($this->dbName)) as $row) {
415 if (in_array(current($row), $this->dumpSettings['include-tables'], true)) {
416 array_push($this->tables, current($row));
417 $elem = array_search(
418 current($row),
419 $this->dumpSettings['include-tables']
420 );
421 unset($this->dumpSettings['include-tables'][$elem]);
422 }
423 }
424 }
425
426 // Listing all views from database
427 if (empty($this->dumpSettings['include-tables'])) {
428 // include all views for now, blacklisting happens later
429 foreach ($this->dbHandler->query($this->typeAdapter->show_views($this->dbName)) as $row) {
430 array_push($this->views, current($row));
431 }
432 } else {
433 // include only the tables mentioned in include-tables
434 foreach ($this->dbHandler->query($this->typeAdapter->show_views($this->dbName)) as $row) {
435 if (in_array(current($row), $this->dumpSettings['include-tables'], true)) {
436 array_push($this->views, current($row));
437 $elem = array_search(
438 current($row),
439 $this->dumpSettings['include-tables']
440 );
441 unset($this->dumpSettings['include-tables'][$elem]);
442 }
443 }
444 }
445
446 // Listing all triggers from database
447 if (false === $this->dumpSettings['skip-triggers']) {
448 foreach ($this->dbHandler->query($this->typeAdapter->show_triggers($this->dbName)) as $row) {
449 array_push($this->triggers, $row['Trigger']);
450 }
451 }
452
453 // Listing all procedures from database
454 if ($this->dumpSettings['routines']) {
455 foreach ($this->dbHandler->query($this->typeAdapter->show_procedures($this->dbName)) as $row) {
456 array_push($this->procedures, $row['procedure_name']);
457 }
458 }
459 }
460
461 /**
462 * Exports all the tables selected from database
463 *
464 * @return null
465 */
466 private function exportTables()
467 {
468 // Exporting tables one by one
469 foreach ($this->tables as $table) {
470 if (in_array($table, $this->dumpSettings['exclude-tables'], true)) {
471 continue;
472 }
473 $this->getTableStructure($table);
474 if (false === $this->dumpSettings['no-data']) {
475 $this->listValues($table);
476 }
477 }
478 }
479
480 /**
481 * Exports all the views found in database
482 *
483 * @return null
484 */
485 private function exportViews()
486 {
487 if (false === $this->dumpSettings['no-create-info']) {
488 // Exporting views one by one
489 foreach ($this->views as $view) {
490 if (in_array($view, $this->dumpSettings['exclude-tables'], true)) {
491 continue;
492 }
493 $this->tableColumnTypes[$view] = $this->getTableColumnTypes($view);
494 $this->getViewStructureTable($view);
495 }
496 foreach ($this->views as $view) {
497 if (in_array($view, $this->dumpSettings['exclude-tables'], true)) {
498 continue;
499 }
500 $this->getViewStructureView($view);
501 }
502 }
503 }
504
505 /**
506 * Exports all the triggers found in database
507 *
508 * @return null
509 */
510 private function exportTriggers()
511 {
512 // Exporting triggers one by one
513 foreach ($this->triggers as $trigger) {
514 $this->getTriggerStructure($trigger);
515 }
516 }
517
518 /**
519 * Exports all the procedures found in database
520 *
521 * @return null
522 */
523 private function exportProcedures()
524 {
525 // Exporting triggers one by one
526 foreach ($this->procedures as $procedure) {
527 $this->getProcedureStructure($procedure);
528 }
529 }
530
531 /**
532 * Table structure extractor
533 *
534 * @todo move specific mysql code to typeAdapter
535 * @param string $tableName Name of table to export
536 * @return null
537 */
538 private function getTableStructure($tableName)
539 {
540 if (!$this->dumpSettings['no-create-info']) {
541 $ret = '';
542 if (!$this->dumpSettings['skip-comments']) {
543 $ret = "--" . PHP_EOL .
544 "-- Table structure for table `$tableName`" . PHP_EOL .
545 "--" . PHP_EOL . PHP_EOL;
546 }
547 $stmt = $this->typeAdapter->show_create_table($tableName);
548 foreach ($this->dbHandler->query($stmt) as $r) {
549 $this->compressManager->write($ret);
550 if ($this->dumpSettings['add-drop-table']) {
551 $this->compressManager->write(
552 $this->typeAdapter->drop_table($tableName)
553 );
554 }
555 $this->compressManager->write(
556 $this->typeAdapter->create_table($r, $this->dumpSettings)
557 );
558 break;
559 }
560 }
561 $this->tableColumnTypes[$tableName] = $this->getTableColumnTypes($tableName);
562 return;
563 }
564
565 /**
566 * Store column types to create data dumps and for Stand-In tables
567 *
568 * @param string $tableName Name of table to export
569 * @return array type column types detailed
570 */
571
572 private function getTableColumnTypes($tableName) {
573 $columnTypes = array();
574 $columns = $this->dbHandler->query(
575 $this->typeAdapter->show_columns($tableName)
576 );
577 $columns->setFetchMode(PDO::FETCH_ASSOC);
578
579 foreach($columns as $key => $col) {
580 $types = $this->typeAdapter->parseColumnType($col);
581 $columnTypes[$col['Field']] = array(
582 'is_numeric'=> $types['is_numeric'],
583 'is_blob' => $types['is_blob'],
584 'type' => $types['type'],
585 'type_sql' => $col['Type']
586 );
587 }
588
589 return $columnTypes;
590 }
591
592 /**
593 * View structure extractor, create table (avoids cyclic references)
594 *
595 * @todo move mysql specific code to typeAdapter
596 * @param string $viewName Name of view to export
597 * @return null
598 */
599 private function getViewStructureTable($viewName)
600 {
601 if (!$this->dumpSettings['skip-comments']) {
602 $ret = "--" . PHP_EOL .
603 "-- Stand-In structure for view `${viewName}`" . PHP_EOL .
604 "--" . PHP_EOL . PHP_EOL;
605 $this->compressManager->write($ret);
606 }
607 $stmt = $this->typeAdapter->show_create_view($viewName);
608
609 // create views as tables, to resolve dependencies
610 foreach ($this->dbHandler->query($stmt) as $r) {
611 if ($this->dumpSettings['add-drop-table']) {
612 $this->compressManager->write(
613 $this->typeAdapter->drop_view($viewName)
614 );
615 }
616
617 $this->compressManager->write(
618 $this->createStandInTable($viewName)
619 );
620 break;
621 }
622 }
623
624 /**
625 * Write a create table statement for the table Stand-In, show create
626 * table would return a create algorithm when used on a view
627 *
628 * @param string $viewName Name of view to export
629 * @return string create statement
630 */
631 function createStandInTable($viewName) {
632 $ret = array();
633 foreach($this->tableColumnTypes[$viewName] as $k => $v) {
634 $ret[] = "`${k}` ${v['type_sql']}";
635 }
636 $ret = implode(PHP_EOL . ",", $ret);
637
638 $ret = "CREATE TABLE IF NOT EXISTS `$viewName` (" .
639 PHP_EOL . $ret . PHP_EOL . ");" . PHP_EOL;
640
641 return $ret;
642 }
643
644 /**
645 * View structure extractor, create view
646 *
647 * @todo move mysql specific code to typeAdapter
648 * @param string $viewName Name of view to export
649 * @return null
650 */
651 private function getViewStructureView($viewName)
652 {
653 if (!$this->dumpSettings['skip-comments']) {
654 $ret = "--" . PHP_EOL .
655 "-- View structure for view `${viewName}`" . PHP_EOL .
656 "--" . PHP_EOL . PHP_EOL;
657 $this->compressManager->write($ret);
658 }
659 $stmt = $this->typeAdapter->show_create_view($viewName);
660
661 // create views, to resolve dependencies
662 // replacing tables with views
663 foreach ($this->dbHandler->query($stmt) as $r) {
664 // because we must replace table with view, we should delete it
665 $this->compressManager->write(
666 $this->typeAdapter->drop_view($viewName)
667 );
668 $this->compressManager->write(
669 $this->typeAdapter->create_view($r)
670 );
671 break;
672 }
673 }
674
675 /**
676 * Trigger structure extractor
677 *
678 * @param string $triggerName Name of trigger to export
679 * @return null
680 */
681 private function getTriggerStructure($triggerName)
682 {
683 $stmt = $this->typeAdapter->show_create_trigger($triggerName);
684 foreach ($this->dbHandler->query($stmt) as $r) {
685 if ($this->dumpSettings['add-drop-trigger']) {
686 $this->compressManager->write(
687 $this->typeAdapter->add_drop_trigger($triggerName)
688 );
689 }
690 $this->compressManager->write(
691 $this->typeAdapter->create_trigger($r)
692 );
693 return;
694 }
695 }
696
697 /**
698 * Procedure structure extractor
699 *
700 * @param string $procedureName Name of procedure to export
701 * @return null
702 */
703 private function getProcedureStructure($procedureName)
704 {
705 if (!$this->dumpSettings['skip-comments']) {
706 $ret = "--" . PHP_EOL .
707 "-- Dumping routines for database '" . $this->dbName . "'" . PHP_EOL .
708 "--" . PHP_EOL . PHP_EOL;
709 $this->compressManager->write($ret);
710 }
711 $stmt = $this->typeAdapter->show_create_procedure($procedureName);
712 foreach ($this->dbHandler->query($stmt) as $r) {
713 $this->compressManager->write(
714 $this->typeAdapter->create_procedure($r, $this->dumpSettings)
715 );
716 return;
717 }
718 }
719
720 /**
721 * Escape values with quotes when needed
722 *
723 * @param string $tableName Name of table which contains rows
724 * @param array $row Associative array of column names and values to be quoted
725 *
726 * @return string
727 */
728 private function escape($tableName, $row)
729 {
730 $ret = array();
731 $columnTypes = $this->tableColumnTypes[$tableName];
732 foreach ($row as $colName => $colValue) {
733 if (is_null($colValue)) {
734 $ret[] = "NULL";
735 } elseif ($this->dumpSettings['hex-blob'] && $columnTypes[$colName]['is_blob']) {
736 if ($columnTypes[$colName]['type'] == 'bit' || !empty($colValue)) {
737 $ret[] = "0x${colValue}";
738 } else {
739 $ret[] = "''";
740 }
741 } elseif ($columnTypes[$colName]['is_numeric']) {
742 $ret[] = $colValue;
743 } else {
744 $ret[] = $this->dbHandler->quote($colValue);
745 }
746 }
747 return $ret;
748 }
749
750 /**
751 * Table rows extractor
752 *
753 * @param string $tableName Name of table to export
754 *
755 * @return null
756 */
757 private function listValues($tableName)
758 {
759 $this->prepareListValues($tableName);
760
761 $onlyOnce = true;
762 $lineSize = 0;
763
764 $colStmt = $this->getColumnStmt($tableName);
765 $stmt = "SELECT $colStmt FROM `$tableName`";
766
767 if ($this->dumpSettings['where']) {
768 $stmt .= " WHERE {$this->dumpSettings['where']}";
769 }
770 $resultSet = $this->dbHandler->query($stmt);
771 $resultSet->setFetchMode(PDO::FETCH_ASSOC);
772
773 foreach ($resultSet as $row) {
774 $vals = $this->escape($tableName, $row);
775 if ($onlyOnce || !$this->dumpSettings['extended-insert']) {
776 $lineSize += $this->compressManager->write(
777 "INSERT INTO `$tableName` VALUES (" . implode(",", $vals) . ")"
778 );
779 $onlyOnce = false;
780 } else {
781 $lineSize += $this->compressManager->write(",(" . implode(",", $vals) . ")");
782 }
783 if (($lineSize > self::MAXLINESIZE) ||
784 !$this->dumpSettings['extended-insert']) {
785 $onlyOnce = true;
786 $lineSize = $this->compressManager->write(";" . PHP_EOL);
787 }
788 }
789 $resultSet->closeCursor();
790
791 if (!$onlyOnce) {
792 $this->compressManager->write(";" . PHP_EOL);
793 }
794
795 $this->endListValues($tableName);
796 }
797
798 /**
799 * Table rows extractor, append information prior to dump
800 *
801 * @param string $tableName Name of table to export
802 *
803 * @return null
804 */
805 function prepareListValues($tableName)
806 {
807 if (!$this->dumpSettings['skip-comments']) {
808 $this->compressManager->write(
809 "--" . PHP_EOL .
810 "-- Dumping data for table `$tableName`" . PHP_EOL .
811 "--" . PHP_EOL . PHP_EOL
812 );
813 }
814
815 if ($this->dumpSettings['single-transaction']) {
816 $this->dbHandler->exec($this->typeAdapter->setup_transaction());
817 $this->dbHandler->exec($this->typeAdapter->start_transaction());
818 }
819
820 if ($this->dumpSettings['lock-tables']) {
821 $this->typeAdapter->lock_table($tableName);
822 }
823
824 if ($this->dumpSettings['add-locks']) {
825 $this->compressManager->write(
826 $this->typeAdapter->start_add_lock_table($tableName)
827 );
828 }
829
830 if ($this->dumpSettings['disable-keys']) {
831 $this->compressManager->write(
832 $this->typeAdapter->start_add_disable_keys($tableName)
833 );
834 }
835
836 // Disable autocommit for faster reload
837 if ($this->dumpSettings['no-autocommit']) {
838 $this->compressManager->write(
839 $this->typeAdapter->start_disable_autocommit()
840 );
841 }
842
843 return;
844 }
845
846 /**
847 * Table rows extractor, close locks and commits after dump
848 *
849 * @param string $tableName Name of table to export
850 *
851 * @return null
852 */
853 function endListValues($tableName)
854 {
855 if ($this->dumpSettings['disable-keys']) {
856 $this->compressManager->write(
857 $this->typeAdapter->end_add_disable_keys($tableName)
858 );
859 }
860
861 if ($this->dumpSettings['add-locks']) {
862 $this->compressManager->write(
863 $this->typeAdapter->end_add_lock_table($tableName)
864 );
865 }
866
867 if ($this->dumpSettings['single-transaction']) {
868 $this->dbHandler->exec($this->typeAdapter->commit_transaction());
869 }
870
871 if ($this->dumpSettings['lock-tables']) {
872 $this->typeAdapter->unlock_table($tableName);
873 }
874
875 // Commit to enable autocommit
876 if ($this->dumpSettings['no-autocommit']) {
877 $this->compressManager->write(
878 $this->typeAdapter->end_disable_autocommit()
879 );
880 }
881
882 $this->compressManager->write(PHP_EOL);
883
884 return;
885 }
886
887 /**
888 * Build SQL List of all columns on current table
889 *
890 * @param string $tableName Name of table to get columns
891 *
892 * @return string SQL sentence with columns
893 */
894 function getColumnStmt($tableName)
895 {
896 $colStmt = array();
897 foreach($this->tableColumnTypes[$tableName] as $colName => $colType) {
898 if ($colType['type'] == 'bit' && $this->dumpSettings['hex-blob']) {
899 $colStmt[] = "LPAD(HEX(`${colName}`),2,'0') AS `${colName}`";
900 } else if ($colType['is_blob'] && $this->dumpSettings['hex-blob']) {
901 $colStmt[] = "HEX(`${colName}`) AS `${colName}`";
902 } else {
903 $colStmt[] = "`${colName}`";
904 }
905 }
906 $colStmt = implode($colStmt, ",");
907
908 return $colStmt;
909 }
910}
911
912/**
913 * Enum with all available compression methods
914 *
915 */
916abstract class CompressMethod
917{
918 public static $enums = array(
919 "None",
920 "Gzip",
921 "Bzip2"
922 );
923
924 /**
925 * @param string $c
926 * @return boolean
927 */
928 public static function isValid($c)
929 {
930 return in_array($c, self::$enums);
931 }
932}
933
934abstract class CompressManagerFactory
935{
936 /**
937 * @param string $c
938 * @return CompressBzip2|CompressGzip|CompressNone
939 */
940 public static function create($c)
941 {
942 $c = ucfirst(strtolower($c));
943 if (! CompressMethod::isValid($c)) {
944 throw new Exception("Compression method ($c) is not defined yet");
945 }
946
947 $method = __NAMESPACE__ . "\\" . "Compress" . $c;
948
949 return new $method;
950 }
951}
952
953class CompressBzip2 extends CompressManagerFactory
954{
955 private $fileHandler = null;
956
957 public function __construct()
958 {
959 if (! function_exists("bzopen")) {
960 throw new Exception("Compression is enabled, but bzip2 lib is not installed or configured properly");
961 }
962 }
963
964 /**
965 * @param string $filename
966 */
967 public function open($filename)
968 {
969 $this->fileHandler = bzopen($filename, "w");
970 if (false === $this->fileHandler) {
971 throw new Exception("Output file is not writable");
972 }
973
974 return true;
975 }
976
977 public function write($str)
978 {
979 if (false === ($bytesWritten = bzwrite($this->fileHandler, $str))) {
980 throw new Exception("Writting to file failed! Probably, there is no more free space left?");
981 }
982 return $bytesWritten;
983 }
984
985 public function close()
986 {
987 return bzclose($this->fileHandler);
988 }
989}
990
991class CompressGzip extends CompressManagerFactory
992{
993 private $fileHandler = null;
994
995 public function __construct()
996 {
997 if (! function_exists("gzopen")) {
998 throw new Exception("Compression is enabled, but gzip lib is not installed or configured properly");
999 }
1000 }
1001
1002 /**
1003 * @param string $filename
1004 */
1005 public function open($filename)
1006 {
1007 $this->fileHandler = gzopen($filename, "wb");
1008 if (false === $this->fileHandler) {
1009 throw new Exception("Output file is not writable");
1010 }
1011
1012 return true;
1013 }
1014
1015 public function write($str)
1016 {
1017 if (false === ($bytesWritten = gzwrite($this->fileHandler, $str))) {
1018 throw new Exception("Writting to file failed! Probably, there is no more free space left?");
1019 }
1020 return $bytesWritten;
1021 }
1022
1023 public function close()
1024 {
1025 return gzclose($this->fileHandler);
1026 }
1027}
1028
1029class CompressNone extends CompressManagerFactory
1030{
1031 private $fileHandler = null;
1032
1033 /**
1034 * @param string $filename
1035 */
1036 public function open($filename)
1037 {
1038 $this->fileHandler = fopen($filename, "wb");
1039 if (false === $this->fileHandler) {
1040 throw new Exception("Output file is not writable");
1041 }
1042
1043 return true;
1044 }
1045
1046 public function write($str)
1047 {
1048 if (false === ($bytesWritten = fwrite($this->fileHandler, $str))) {
1049 throw new Exception("Writting to file failed! Probably, there is no more free space left?");
1050 }
1051 return $bytesWritten;
1052 }
1053
1054 public function close()
1055 {
1056 return fclose($this->fileHandler);
1057 }
1058}
1059
1060/**
1061 * Enum with all available TypeAdapter implementations
1062 *
1063 */
1064abstract class TypeAdapter
1065{
1066 public static $enums = array(
1067 "Sqlite",
1068 "Mysql"
1069 );
1070
1071 /**
1072 * @param string $c
1073 * @return boolean
1074 */
1075 public static function isValid($c)
1076 {
1077 return in_array($c, self::$enums);
1078 }
1079}
1080
1081/**
1082 * TypeAdapter Factory
1083 *
1084 */
1085abstract class TypeAdapterFactory
1086{
1087 /**
1088 * @param string $c Type of database factory to create (Mysql, Sqlite,...)
1089 * @param PDO $dbHandler
1090 */
1091 public static function create($c, $dbHandler = null)
1092 {
1093 $c = ucfirst(strtolower($c));
1094 if (! TypeAdapter::isValid($c)) {
1095 throw new Exception("Database type support for ($c) not yet available");
1096 }
1097 $method = __NAMESPACE__ . "\\" . "TypeAdapter" . $c;
1098 return new $method($dbHandler);
1099 }
1100
1101 /**
1102 * function databases Add sql to create and use database
1103 * @todo make it do something with sqlite
1104 */
1105 public function databases()
1106 {
1107 return "";
1108 }
1109
1110 public function show_create_table($tableName)
1111 {
1112 return "SELECT tbl_name as 'Table', sql as 'Create Table' " .
1113 "FROM sqlite_master " .
1114 "WHERE type='table' AND tbl_name='$tableName'";
1115 }
1116
1117 /**
1118 * function create_table Get table creation code from database
1119 * @todo make it do something with sqlite
1120 */
1121 public function create_table($row, $dumpSettings)
1122 {
1123 return "";
1124 }
1125
1126 public function show_create_view($viewName)
1127 {
1128 return "SELECT tbl_name as 'View', sql as 'Create View' " .
1129 "FROM sqlite_master " .
1130 "WHERE type='view' AND tbl_name='$viewName'";
1131 }
1132
1133 /**
1134 * function create_view Get view creation code from database
1135 * @todo make it do something with sqlite
1136 */
1137 public function create_view($row)
1138 {
1139 return "";
1140 }
1141
1142 /**
1143 * function show_create_trigger Get trigger creation code from database
1144 * @todo make it do something with sqlite
1145 */
1146 public function show_create_trigger($triggerName)
1147 {
1148 return "";
1149 }
1150
1151 /**
1152 * function create_trigger Modify trigger code, add delimiters, etc
1153 * @todo make it do something with sqlite
1154 */
1155 public function create_trigger($triggerName)
1156 {
1157 return "";
1158 }
1159
1160 /**
1161 * function create_procedure Modify procedure code, add delimiters, etc
1162 * @todo make it do something with sqlite
1163 */
1164 public function create_procedure($procedureName, $dumpSettings)
1165 {
1166 return "";
1167 }
1168
1169 public function show_tables()
1170 {
1171 return "SELECT tbl_name FROM sqlite_master WHERE type='table'";
1172 }
1173
1174 public function show_views()
1175 {
1176 return "SELECT tbl_name FROM sqlite_master WHERE type='view'";
1177 }
1178
1179 public function show_triggers()
1180 {
1181 return "SELECT name FROM sqlite_master WHERE type='trigger'";
1182 }
1183
1184 public function show_columns()
1185 {
1186 if (func_num_args() != 1) {
1187 return "";
1188 }
1189
1190 $args = func_get_args();
1191
1192 return "pragma table_info(${args[0]})";
1193 }
1194
1195 public function show_procedures()
1196 {
1197 return "";
1198 }
1199
1200 public function setup_transaction()
1201 {
1202 return "";
1203 }
1204
1205 public function start_transaction()
1206 {
1207 return "BEGIN EXCLUSIVE";
1208 }
1209
1210 public function commit_transaction()
1211 {
1212 return "COMMIT";
1213 }
1214
1215 public function lock_table()
1216 {
1217 return "";
1218 }
1219
1220 public function unlock_table()
1221 {
1222 return "";
1223 }
1224
1225 public function start_add_lock_table()
1226 {
1227 return PHP_EOL;
1228 }
1229
1230 public function end_add_lock_table()
1231 {
1232 return PHP_EOL;
1233 }
1234
1235 public function start_add_disable_keys()
1236 {
1237 return PHP_EOL;
1238 }
1239
1240 public function end_add_disable_keys()
1241 {
1242 return PHP_EOL;
1243 }
1244
1245 public function start_disable_foreign_keys_check()
1246 {
1247 return PHP_EOL;
1248 }
1249
1250 public function end_disable_foreign_keys_check()
1251 {
1252 return PHP_EOL;
1253 }
1254
1255 public function add_drop_database()
1256 {
1257 return PHP_EOL;
1258 }
1259
1260 public function add_drop_trigger()
1261 {
1262 return PHP_EOL;
1263 }
1264
1265 public function drop_table()
1266 {
1267 return PHP_EOL;
1268 }
1269
1270 public function drop_view()
1271 {
1272 return PHP_EOL;
1273 }
1274
1275 /**
1276 * Decode column metadata and fill info structure.
1277 * type, is_numeric and is_blob will always be available.
1278 *
1279 * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
1280 * @return array
1281 */
1282 public function parseColumnType($colType)
1283 {
1284 return array();
1285 }
1286
1287 public function backup_parameters()
1288 {
1289 return PHP_EOL;
1290 }
1291
1292 public function restore_parameters()
1293 {
1294 return PHP_EOL;
1295 }
1296}
1297
1298class TypeAdapterPgsql extends TypeAdapterFactory
1299{
1300}
1301
1302class TypeAdapterDblib extends TypeAdapterFactory
1303{
1304}
1305
1306class TypeAdapterSqlite extends TypeAdapterFactory
1307{
1308}
1309
1310class TypeAdapterMysql extends TypeAdapterFactory
1311{
1312
1313 private $dbHandler = null;
1314
1315 // Numerical Mysql types
1316 public $mysqlTypes = array(
1317 'numerical' => array(
1318 'bit',
1319 'tinyint',
1320 'smallint',
1321 'mediumint',
1322 'int',
1323 'integer',
1324 'bigint',
1325 'real',
1326 'double',
1327 'float',
1328 'decimal',
1329 'numeric'
1330 ),
1331 'blob' => array(
1332 'tinyblob',
1333 'blob',
1334 'mediumblob',
1335 'longblob',
1336 'binary',
1337 'varbinary',
1338 'bit'
1339 )
1340 );
1341
1342 public function __construct ($dbHandler)
1343 {
1344 $this->dbHandler = $dbHandler;
1345 }
1346
1347 public function databases()
1348 {
1349 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1350 $args = func_get_args();
1351 $databaseName = $args[0];
1352
1353 $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'character_set_database';");
1354 $characterSet = $resultSet->fetchColumn(1);
1355 $resultSet->closeCursor();
1356
1357 $resultSet = $this->dbHandler->query("SHOW VARIABLES LIKE 'collation_database';");
1358 $collationDb = $resultSet->fetchColumn(1);
1359 $resultSet->closeCursor();
1360 $ret = "";
1361
1362 $ret .= "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `${databaseName}`".
1363 " /*!40100 DEFAULT CHARACTER SET ${characterSet} " .
1364 " COLLATE ${collationDb} */;" . PHP_EOL . PHP_EOL .
1365 "USE `${databaseName}`;" . PHP_EOL . PHP_EOL;
1366
1367 return $ret;
1368 }
1369
1370 public function show_create_table($tableName)
1371 {
1372 return "SHOW CREATE TABLE `$tableName`";
1373 }
1374
1375 public function show_create_view($viewName)
1376 {
1377 return "SHOW CREATE VIEW `$viewName`";
1378 }
1379
1380 public function show_create_trigger($triggerName)
1381 {
1382 return "SHOW CREATE TRIGGER `$triggerName`";
1383 }
1384
1385 public function show_create_procedure($procedureName)
1386 {
1387 return "SHOW CREATE PROCEDURE `$procedureName`";
1388 }
1389
1390 public function create_table($row, $dumpSettings)
1391 {
1392 if (!isset($row['Create Table'])) {
1393 throw new Exception("Error getting table code, unknown output");
1394 }
1395
1396 $ret = "/*!40101 SET @saved_cs_client = @@character_set_client */;" . PHP_EOL .
1397 "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
1398 $row['Create Table'] . ";" . PHP_EOL .
1399 "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL .
1400 PHP_EOL;
1401 return $ret;
1402 }
1403
1404 public function create_view($row)
1405 {
1406 $ret = "";
1407 if (!isset($row['Create View'])) {
1408 throw new Exception("Error getting view structure, unknown output");
1409 }
1410
1411 $triggerStmt = $row['Create View'];
1412
1413 $triggerStmtReplaced1 = str_replace(
1414 "CREATE ALGORITHM",
1415 "/*!50001 CREATE ALGORITHM",
1416 $triggerStmt
1417 );
1418 $triggerStmtReplaced2 = str_replace(
1419 " DEFINER=",
1420 " */" . PHP_EOL . "/*!50013 DEFINER=",
1421 $triggerStmtReplaced1
1422 );
1423 $triggerStmtReplaced3 = str_replace(
1424 " VIEW ",
1425 " */" . PHP_EOL . "/*!50001 VIEW ",
1426 $triggerStmtReplaced2
1427 );
1428 if (false === $triggerStmtReplaced1 ||
1429 false === $triggerStmtReplaced2 ||
1430 false === $triggerStmtReplaced3) {
1431 $triggerStmtReplaced = $triggerStmt;
1432 } else {
1433 $triggerStmtReplaced = $triggerStmtReplaced3 . " */;";
1434 }
1435
1436 $ret .= $triggerStmtReplaced . PHP_EOL . PHP_EOL;
1437 return $ret;
1438 }
1439
1440 public function create_trigger($row)
1441 {
1442 $ret = "";
1443 if (!isset($row['SQL Original Statement'])) {
1444 throw new Exception("Error getting trigger code, unknown output");
1445 }
1446
1447 $triggerStmt = $row['SQL Original Statement'];
1448 $triggerStmtReplaced = str_replace(
1449 "CREATE DEFINER",
1450 "/*!50003 CREATE*/ /*!50017 DEFINER",
1451 $triggerStmt
1452 );
1453 $triggerStmtReplaced = str_replace(
1454 " TRIGGER",
1455 "*/ /*!50003 TRIGGER",
1456 $triggerStmtReplaced
1457 );
1458 if ( false === $triggerStmtReplaced ) {
1459 $triggerStmtReplaced = $triggerStmt;
1460 }
1461
1462 $ret .= "DELIMITER ;;" . PHP_EOL .
1463 $triggerStmtReplaced . "*/;;" . PHP_EOL .
1464 "DELIMITER ;" . PHP_EOL . PHP_EOL;
1465 return $ret;
1466 }
1467
1468 public function create_procedure($row, $dumpSettings)
1469 {
1470 $ret = "";
1471 if (!isset($row['Create Procedure'])) {
1472 throw new Exception("Error getting procedure code, unknown output. " .
1473 "Please check 'https://bugs.mysql.com/bug.php?id=14564'");
1474 }
1475 $procedureStmt = $row['Create Procedure'];
1476
1477 $ret .= "/*!50003 DROP PROCEDURE IF EXISTS `" .
1478 $row['Procedure'] . "` */;" . PHP_EOL .
1479 "/*!40101 SET @saved_cs_client = @@character_set_client */;" . PHP_EOL .
1480 "/*!40101 SET character_set_client = " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL .
1481 "DELIMITER ;;" . PHP_EOL .
1482 $procedureStmt . " ;;" . PHP_EOL .
1483 "DELIMITER ;" . PHP_EOL .
1484 "/*!40101 SET character_set_client = @saved_cs_client */;" . PHP_EOL . PHP_EOL;
1485
1486 return $ret;
1487 }
1488
1489 public function show_tables()
1490 {
1491 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1492 $args = func_get_args();
1493 return "SELECT TABLE_NAME AS tbl_name " .
1494 "FROM INFORMATION_SCHEMA.TABLES " .
1495 "WHERE TABLE_TYPE='BASE TABLE' AND TABLE_SCHEMA='${args[0]}'";
1496 }
1497
1498 public function show_views()
1499 {
1500 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1501 $args = func_get_args();
1502 return "SELECT TABLE_NAME AS tbl_name " .
1503 "FROM INFORMATION_SCHEMA.TABLES " .
1504 "WHERE TABLE_TYPE='VIEW' AND TABLE_SCHEMA='${args[0]}'";
1505 }
1506
1507 public function show_triggers()
1508 {
1509 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1510 $args = func_get_args();
1511 return "SHOW TRIGGERS FROM `${args[0]}`;";
1512 }
1513
1514 public function show_columns()
1515 {
1516 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1517 $args = func_get_args();
1518 return "SHOW COLUMNS FROM `${args[0]}`;";
1519 }
1520
1521 public function show_procedures()
1522 {
1523 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1524 $args = func_get_args();
1525 return "SELECT SPECIFIC_NAME AS procedure_name " .
1526 "FROM INFORMATION_SCHEMA.ROUTINES " .
1527 "WHERE ROUTINE_TYPE='PROCEDURE' AND ROUTINE_SCHEMA='${args[0]}'";
1528 }
1529
1530 public function setup_transaction()
1531 {
1532 return "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
1533 }
1534
1535 public function start_transaction()
1536 {
1537 return "START TRANSACTION";
1538 }
1539
1540 public function commit_transaction()
1541 {
1542 return "COMMIT";
1543 }
1544
1545 public function lock_table()
1546 {
1547 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1548 $args = func_get_args();
1549 return $this->dbHandler->exec("LOCK TABLES `${args[0]}` READ LOCAL");
1550
1551 }
1552
1553 public function unlock_table()
1554 {
1555 return $this->dbHandler->exec("UNLOCK TABLES");
1556 }
1557
1558 public function start_add_lock_table()
1559 {
1560 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1561 $args = func_get_args();
1562
1563 return "LOCK TABLES `${args[0]}` WRITE;" . PHP_EOL;
1564 }
1565
1566 public function end_add_lock_table()
1567 {
1568 return "UNLOCK TABLES;" . PHP_EOL;
1569 }
1570
1571 public function start_add_disable_keys()
1572 {
1573 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1574 $args = func_get_args();
1575 return "/*!40000 ALTER TABLE `${args[0]}` DISABLE KEYS */;" .
1576 PHP_EOL;
1577 }
1578
1579 public function end_add_disable_keys()
1580 {
1581 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1582 $args = func_get_args();
1583 return "/*!40000 ALTER TABLE `${args[0]}` ENABLE KEYS */;" .
1584 PHP_EOL;
1585 }
1586
1587 public function start_disable_autocommit()
1588 {
1589 return "SET autocommit=0;" . PHP_EOL;
1590 }
1591
1592 public function end_disable_autocommit()
1593 {
1594 return "COMMIT;" . PHP_EOL;
1595 }
1596
1597 public function add_drop_database()
1598 {
1599 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1600 $args = func_get_args();
1601
1602 return "/*!40000 DROP DATABASE IF EXISTS `${args[0]}`*/;" .
1603 PHP_EOL . PHP_EOL;
1604 }
1605
1606 public function add_drop_trigger()
1607 {
1608 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1609 $args = func_get_args();
1610 return "DROP TRIGGER IF EXISTS `${args[0]}`;" . PHP_EOL;
1611 }
1612
1613 public function drop_table()
1614 {
1615 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1616 $args = func_get_args();
1617 return "DROP TABLE IF EXISTS `${args[0]}`;" . PHP_EOL;
1618 }
1619
1620 public function drop_view()
1621 {
1622 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1623 $args = func_get_args();
1624 return "DROP TABLE IF EXISTS `${args[0]}`;" . PHP_EOL .
1625 "/*!50001 DROP VIEW IF EXISTS `${args[0]}`*/;" . PHP_EOL;
1626 }
1627
1628 public function getDatabaseHeader()
1629 {
1630 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1631 $args = func_get_args();
1632 return "--" . PHP_EOL .
1633 "-- Current Database: `${args[0]}`" . PHP_EOL .
1634 "--" . PHP_EOL . PHP_EOL;
1635 }
1636
1637 /**
1638 * Decode column metadata and fill info structure.
1639 * type, is_numeric and is_blob will always be available.
1640 *
1641 * @param array $colType Array returned from "SHOW COLUMNS FROM tableName"
1642 * @return array
1643 */
1644 public function parseColumnType($colType)
1645 {
1646 $colInfo = array();
1647 $colParts = explode(" ", $colType['Type']);
1648
1649 if($fparen = strpos($colParts[0], "("))
1650 {
1651 $colInfo['type'] = substr($colParts[0], 0, $fparen);
1652 $colInfo['length'] = str_replace(")", "", substr($colParts[0], $fparen+1));
1653 $colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL;
1654 }
1655 else
1656 {
1657 $colInfo['type'] = $colParts[0];
1658 }
1659 $colInfo['is_numeric'] = in_array($colInfo['type'], $this->mysqlTypes['numerical']);
1660 $colInfo['is_blob'] = in_array($colInfo['type'], $this->mysqlTypes['blob']);
1661
1662 return $colInfo;
1663 }
1664
1665 public function backup_parameters()
1666 {
1667 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1668 $args = func_get_args();
1669 $dumpSettings = $args[0];
1670 $ret = "/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" . PHP_EOL .
1671 "/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;" . PHP_EOL .
1672 "/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" . PHP_EOL .
1673 "/*!40101 SET NAMES " . $dumpSettings['default-character-set'] . " */;" . PHP_EOL;
1674
1675 if (false === $dumpSettings['skip-tz-utc']) {
1676 $ret .= "/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;" . PHP_EOL .
1677 "/*!40103 SET TIME_ZONE='+00:00' */;" . PHP_EOL;
1678 }
1679
1680 $ret .= "/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;" . PHP_EOL .
1681 "/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;" . PHP_EOL .
1682 "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;" . PHP_EOL .
1683 "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;" . PHP_EOL .PHP_EOL;
1684
1685 return $ret;
1686 }
1687
1688 public function restore_parameters()
1689 {
1690 $this->check_parameters(func_num_args(), $expected_num_args = 1, __METHOD__);
1691 $args = func_get_args();
1692 $dumpSettings = $args[0];
1693 $ret = "";
1694
1695 if (false === $dumpSettings['skip-tz-utc']) {
1696 $ret .= "/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;" . PHP_EOL;
1697 }
1698
1699 $ret .= "/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;" . PHP_EOL .
1700 "/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;" . PHP_EOL .
1701 "/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;" . PHP_EOL .
1702 "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;" . PHP_EOL .
1703 "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;" . PHP_EOL .
1704 "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;" . PHP_EOL .
1705 "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;" . PHP_EOL . PHP_EOL;
1706
1707 return $ret;
1708 }
1709
1710 private function check_parameters($num_args, $expected_num_args, $method_name)
1711 {
1712 if ( $num_args != $expected_num_args ) {
1713 throw new Exception("Unexpected parameter passed to $method_name");
1714 }
1715 return;
1716 }
1717}