PageRenderTime 51ms CodeModel.GetById 8ms RepoModel.GetById 1ms app.codeStats 0ms

/mysqldiff.php

https://github.com/caviola/mysqldiff
PHP | 708 lines | 557 code | 118 blank | 33 comment | 104 complexity | c43e06d5ef777b4f1b30b9fa772310d9 MD5 | raw file
  1. <?php
  2. /**
  3. * mysqldiff
  4. *
  5. * First of all: THIS IS AN ALPHA VERSION. DO NOT USE ON PRODUCTION!
  6. *
  7. * Compares the schema of two MySQL databases and produces a script
  8. * to "alter" the second schema to match the first one.
  9. *
  10. * Copyright (c) 2010-2011, Albert Almeida (caviola@gmail.com)
  11. * All rights reserved.
  12. *
  13. * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
  14. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  15. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  16. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  17. * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  18. * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  19. * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  20. *
  21. * https://github.com/caviola/mysqldiff
  22. */
  23. function drop_schema_db($db)
  24. {
  25. if (!$db->schema)
  26. return;
  27. mysqli_query($db->link, "drop database {$db->database}");
  28. }
  29. function create_schema_db($db)
  30. {
  31. if (!$db->schema)
  32. return;
  33. if (!mysqli_query($db->link, "create database {$db->database}"))
  34. error('Error of create database ' . mysqli_error($db->link));
  35. }
  36. function load_schema_db(&$db)
  37. {
  38. if (!$db->schema)
  39. return;
  40. $sql = explode(";", file_get_contents($db->schema));
  41. foreach ($sql as $q) {
  42. if (!trim($q))
  43. continue;
  44. if (preg_match('/^\s*\/\*.*\*\/\s*$/', $q))
  45. continue;
  46. if (preg_match('/^\s*drop /i', $q))
  47. continue;
  48. if (!mysqli_query($db->link, $q))
  49. error("Error in load schema db '$q'" . mysqli_error($db->link));
  50. }
  51. }
  52. function populate_schemata_info(&$db)
  53. {
  54. if (!($result = mysqli_query($db->link, "select * from information_schema.schemata where schema_name='$db->database'")))
  55. return FALSE;
  56. if ($info = mysqli_fetch_object($result)) {
  57. $db->charset = $info->DEFAULT_CHARACTER_SET_NAME;
  58. $db->collation = $info->DEFAULT_COLLATION_NAME;
  59. }
  60. }
  61. function list_tables($db)
  62. {
  63. if (!($result = mysqli_query($db->link, "select TABLE_NAME, ENGINE, TABLE_COLLATION, ROW_FORMAT, CHECKSUM, TABLE_COMMENT from information_schema.tables where table_schema='$db->database'")))
  64. return FALSE;
  65. $tables = array();
  66. while ($row = mysqli_fetch_object($result)) {
  67. $tables[$row->TABLE_NAME] = $row;
  68. }
  69. return $tables;
  70. }
  71. function list_columns($table, $db)
  72. {
  73. // Note the columns are returned in ORDINAL_POSITION ascending order.
  74. if (!($result = mysqli_query($db->link, "select * from information_schema.columns where table_schema='$db->database' and table_name='$table' order by ordinal_position")))
  75. return FALSE;
  76. $columns = array();
  77. while ($row = mysqli_fetch_object($result)) {
  78. $columns[$row->COLUMN_NAME] = $row;
  79. }
  80. return $columns;
  81. }
  82. function list_indexes($table, $db)
  83. {
  84. if (!($result = mysqli_query($db->link, "show indexes from `$table`")))
  85. return FALSE;
  86. $indexes = array();
  87. $prev_key_name = NULL;
  88. while ($row = mysqli_fetch_object($result)) {
  89. // Get the information about the index column.
  90. $index_column = (object) array(
  91. 'sub_part' => $row->Sub_part,
  92. 'seq' => $row->Seq_in_index,
  93. 'type' => $row->Index_type,
  94. 'collation' => $row->Collation,
  95. 'comment' => $row->Comment,
  96. );
  97. if ($row->Key_name != $prev_key_name) {
  98. // Add a new index to the list.
  99. $indexes[$row->Key_name] = (object) array(
  100. 'key_name' => $row->Key_name,
  101. 'table' => $row->Table,
  102. 'non_unique' => $row->Non_unique,
  103. 'columns' => array($row->Column_name => $index_column)
  104. );
  105. $prev_key_name = $row->Key_name;
  106. } else {
  107. // Add a new column to an existing index.
  108. $indexes[$row->Key_name]->columns[$row->Column_name] = $index_column;
  109. }
  110. }
  111. return $indexes;
  112. }
  113. function get_create_table_sql($name, $db)
  114. {
  115. if (!($result = mysqli_query($db->link, "show create table `$name`")))
  116. return FALSE;
  117. $row = mysqli_fetch_row($result);
  118. return $row[1];
  119. }
  120. function create_tables($db1, $tables1, $tables2)
  121. {
  122. global $options;
  123. $sql = '';
  124. $table_names = array_diff(array_keys($tables1), array_keys($tables2));
  125. foreach ($table_names as $t) {
  126. $sql .= get_create_table_sql($t, $db1) . ";\n\n";
  127. }
  128. fputs($options->ofh, $sql);
  129. }
  130. function format_default_value($value, $db)
  131. {
  132. if (strcasecmp($value, 'CURRENT_TIMESTAMP') == 0)
  133. return $value;
  134. elseif (is_string($value))
  135. return "'" . mysqli_real_escape_string($db->link, $value) . "'";
  136. else
  137. return $value;
  138. }
  139. function drop_tables($tables1, $tables2)
  140. {
  141. global $options;
  142. $sql = '';
  143. $table_names = array_diff(array_keys($tables2), array_keys($tables1));
  144. foreach ($table_names as $t) {
  145. $sql .= "DROP TABLE `$t`;\n";
  146. }
  147. if ($sql)
  148. $sql .= "\n";
  149. fputs($options->ofh, $sql);
  150. }
  151. function build_column_definition_sql($column, $db)
  152. {
  153. $result = $column->COLUMN_TYPE;
  154. if ($column->COLLATION_NAME)
  155. $result .= " COLLATE '$column->COLLATION_NAME'";
  156. $result .= strcasecmp($column->IS_NULLABLE, 'NO') == 0 ? ' NOT NULL' : ' NULL';
  157. if (isset($column->COLUMN_DEFAULT))
  158. $result .= ' DEFAULT ' . format_default_value($column->COLUMN_DEFAULT, $db);
  159. if ($column->EXTRA)
  160. $result .= " $column->EXTRA";
  161. if ($column->COLUMN_COMMENT)
  162. $result .= " COMMENT '" . mysqli_real_escape_string($db->link, $column->COLUMN_COMMENT) . "'";
  163. return $result;
  164. }
  165. function alter_table_add_column($column, $after_column, $table, $db)
  166. {
  167. global $options;
  168. $sql = "ALTER TABLE `$table` ADD COLUMN `$column->COLUMN_NAME` " .
  169. build_column_definition_sql($column, $db) .
  170. ($after_column ? " AFTER `$after_column`" : ' FIRST') .
  171. ";\n";
  172. fputs($options->ofh, $sql);
  173. }
  174. function alter_table_modify_column($column1, $column2, $after_column, $table, $db)
  175. {
  176. global $options;
  177. $modify = array();
  178. if ($column1->COLUMN_TYPE != $column2->COLUMN_TYPE)
  179. $modify['type'] = " $column1->COLUMN_TYPE";
  180. if ($column1->COLLATION_NAME != $column2->COLLATION_NAME)
  181. $modify['collation'] = " COLLATE $column1->COLLATION_NAME";
  182. if ($column1->IS_NULLABLE != $column2->IS_NULLABLE)
  183. $modify['null'] = strcasecmp($column1->IS_NULLABLE, 'NO') == 0 ? ' NOT NULL' : ' NULL';
  184. if ($column1->COLUMN_DEFAULT != $column2->COLUMN_DEFAULT) {
  185. // FALSE is an special value that indicates we should DROP this column's default value,
  186. // causing MySQL to assign it the "default default".
  187. $modify['default'] = isset($column1->COLUMN_DEFAULT) ? ' DEFAULT ' . format_default_value($column1->COLUMN_DEFAULT, $db) : FALSE;
  188. }
  189. if ($column1->EXTRA != $column2->EXTRA)
  190. $modify['extra'] = " $column1->EXTRA";
  191. if ($column1->COLUMN_COMMENT != $column2->COLUMN_COMMENT)
  192. $modify['comment'] = " COMMENT '$column1->COLUMN_COMMENT'";
  193. if ($column1->ORDINAL_POSITION != $column2->ORDINAL_POSITION)
  194. $modify['position'] = $after_column ? " AFTER `$after_column`" : ' FIRST';
  195. if ($modify) {
  196. $sql = "ALTER TABLE `$table` MODIFY `$column1->COLUMN_NAME`";
  197. $sql .= isset($modify['type']) ? $modify['type'] : " $column2->COLUMN_TYPE";
  198. if (isset($modify['collation']))
  199. $sql .= $modify['collation'];
  200. if (isset($modify['null']))
  201. $sql .= $modify['null'];
  202. else
  203. $sql .= strcasecmp($column2->IS_NULLABLE, 'NO') == 0 ? ' NOT NULL' : ' NULL';
  204. if (isset($modify['default']) && $modify['default'] !== FALSE) {
  205. $sql .= $modify['default'];
  206. } elseif (isset($column2->COLUMN_DEFAULT))
  207. $sql .= ' DEFAULT ' . format_default_value($column2->COLUMN_DEFAULT, $db);
  208. if (isset($modify['extra']))
  209. $sql .= $modify['extra'];
  210. elseif ($column2->EXTRA != '')
  211. $sql .= " $column2->EXTRA";
  212. if (isset($modify['comment']))
  213. $sql .= $modify['comment'];
  214. elseif ($column2->COLUMN_COMMENT != '')
  215. $sql .= " COMMENT '$column2->COLUMN_COMMENT'";
  216. if (isset($modify['position']))
  217. $sql .= $modify['position'];
  218. $sql .= ";\n";
  219. fputs($options->ofh, $sql);
  220. }
  221. }
  222. function alter_table_drop_columns($columns1, $columns2, $table)
  223. {
  224. global $options;
  225. $sql = '';
  226. $columns = array_diff_key($columns2, $columns1);
  227. foreach ($columns as $c) {
  228. $sql .= "ALTER TABLE `$table` DROP COLUMN `$c->COLUMN_NAME`;\n";
  229. }
  230. fputs($options->ofh, $sql);
  231. }
  232. function alter_tables_columns($db1, $db2)
  233. {
  234. global $options;
  235. $tables1 = list_tables($db1);
  236. $tables2 = list_tables($db2);
  237. $tables = array_intersect(array_keys($tables1), array_keys($tables2));
  238. foreach ($tables as $t) {
  239. $columns1 = list_columns($t, $db1);
  240. $columns2 = list_columns($t, $db2);
  241. $columns_index = array_keys($columns1);
  242. foreach ($columns1 as $c1) {
  243. $after_column = $c1->ORDINAL_POSITION == 1 ? NULL : $columns_index[$c1->ORDINAL_POSITION - 2];
  244. if (!isset($columns2[$c1->COLUMN_NAME]))
  245. alter_table_add_column($c1, $after_column, $t, $db2);
  246. else
  247. alter_table_modify_column($c1, $columns2[$c1->COLUMN_NAME], $after_column, $t, $db2);
  248. }
  249. if ($options->drop_columns)
  250. alter_table_drop_columns($columns1, $columns2, $t);
  251. }
  252. }
  253. function alter_tables($tables1, $tables2)
  254. {
  255. global $options;
  256. $sql = '';
  257. $table_names = array_intersect(array_keys($tables2), array_keys($tables1));
  258. foreach ($table_names as $t) {
  259. $t1 = $tables1[$t];
  260. $t2 = $tables2[$t];
  261. if ($t1->ENGINE != $t2->ENGINE)
  262. $sql .= "ALTER TABLE `$t` ENGINE=$t1->ENGINE;\n";
  263. if ($t1->TABLE_COLLATION != $t2->TABLE_COLLATION)
  264. $sql .= "ALTER TABLE `$t` COLLATE=$t1->TABLE_COLLATION;\n";
  265. if ($t1->ROW_FORMAT != $t2->ROW_FORMAT)
  266. $sql .= "ALTER TABLE `$t` ROW_FORMAT=$t1->ROW_FORMAT;\n";
  267. if ($t1->CHECKSUM != $t2->CHECKSUM)
  268. $sql .= "ALTER TABLE `$t` CHECKSUM=$t1->CHECKSUM;\n";
  269. /* if ($t1->TABLE_COMMENT != $t2->TABLE_COMMENT)
  270. $sql .= "ALTER TABLE `$t` COMMENT='$t1->TABLE_COMMENT';\n";
  271. */
  272. if ($sql)
  273. $sql .= "\n";
  274. }
  275. fputs($options->ofh, $sql);
  276. }
  277. function are_indexes_eq($index1, $index2)
  278. {
  279. if ($index1->non_unique != $index2->non_unique)
  280. return FALSE;
  281. if (count($index1->columns) != count($index2->columns))
  282. return FALSE;
  283. if (empty($index1->columns)) {
  284. return false;
  285. }
  286. foreach ((array) $index1->columns as $name => $column1) {
  287. if (!isset($index2->columns[$name]))
  288. return FALSE;
  289. if ($column1->seq != $index2->columns[$name]->seq)
  290. return FALSE;
  291. if ($column1->sub_part != $index2->columns[$name]->sub_part)
  292. return FALSE;
  293. if ($column1->type != $index2->columns[$name]->type)
  294. return FALSE;
  295. /* if ($column1->collation != $index2->columns[$name]->collation)
  296. return FALSE; */
  297. }
  298. return TRUE;
  299. }
  300. function build_drop_index_sql($index)
  301. {
  302. return $index->key_name == 'PRIMARY' ?
  303. "ALTER TABLE `$index->table` DROP PRIMARY KEY;" :
  304. "ALTER TABLE `$index->table` DROP INDEX $index->key_name;";
  305. }
  306. function build_create_index_sql($index)
  307. {
  308. $column_list = array();
  309. foreach ($index->columns as $name => $column) {
  310. $column_list[] = $name . ($column->sub_part ? "($column->sub_part)" : '');
  311. }
  312. $column_list = '(' . implode(',', $column_list) . ')';
  313. if ($index->key_name == 'PRIMARY')
  314. $result = "ALTER TABLE `$index->table` ADD PRIMARY KEY $column_list;";
  315. else {
  316. if (isset($index->type) && $index->type == 'FULLTEXT')
  317. $index_type = ' FULLTEXT';
  318. elseif (!$index->non_unique)
  319. $index_type = ' UNIQUE';
  320. else
  321. $index_type = '';
  322. $result = "CREATE$index_type INDEX $index->key_name ON `$index->table` $column_list;";
  323. }
  324. return $result;
  325. }
  326. function alter_table_add_indexes($idx1, $idx2)
  327. {
  328. global $options;
  329. $indexes = array_diff_key($idx1, $idx2);
  330. $sql = '';
  331. foreach ($indexes as $index_name => $index)
  332. $sql .= build_create_index_sql($index) . "\n";
  333. fputs($options->ofh, $sql);
  334. }
  335. function alter_table_drop_indexes($idx1, $idx2)
  336. {
  337. global $options;
  338. $indexes = array_diff_key($idx2, $idx1);
  339. $sql = '';
  340. foreach ($indexes as $index_name => $index)
  341. $sql .= build_drop_index_sql($index) . "\n";
  342. fputs($options->ofh, $sql);
  343. }
  344. function alter_table_alter_indexes($idx1, $idx2)
  345. {
  346. global $options;
  347. $sql = '';
  348. $indexes = (array) array_intersect_key((array) $idx1, (array) $idx2);
  349. foreach ($indexes as $index_name => $index)
  350. if (!are_indexes_eq($index, $idx2[$index_name])) {
  351. $sql .= build_drop_index_sql($idx2[$index_name]) . "\n";
  352. $sql .= build_create_index_sql($index) . "\n";
  353. }
  354. fputs($options->ofh, $sql);
  355. }
  356. function process_database($db1, $db2)
  357. {
  358. global $options;
  359. if (!$db2->schema)
  360. $sql = "USE `$db2->database`;\n";
  361. if ($db1->charset != $db2->charset)
  362. $sql .= "ALTER DATABASE `$db2->database` CHARACTER SET=$db1->charset;\n";
  363. if ($db1->collation != $db2->collation)
  364. $sql .= "ALTER DATABASE `$db2->database` COLLATE=$db1->collation;\n";
  365. $sql .= "\n";
  366. fputs($options->ofh, $sql);
  367. }
  368. function process_indexes($tables1, $tables2, $db1, $db2)
  369. {
  370. $tables = array_intersect_key((array) $tables1, (array) $tables2);
  371. foreach (array_keys((array) $tables) as $t) {
  372. $idx1 = list_indexes($t, $db1);
  373. $idx2 = list_indexes($t, $db2);
  374. alter_table_drop_indexes($idx1, $idx2);
  375. alter_table_add_indexes($idx1, $idx2);
  376. alter_table_alter_indexes($idx1, $idx2);
  377. }
  378. }
  379. function process_tables($db1, $db2)
  380. {
  381. global $options;
  382. $tables1 = list_tables($db1);
  383. $tables2 = list_tables($db2);
  384. create_tables($db1, $tables1, $tables2);
  385. if ($options->drop_tables)
  386. drop_tables($tables1, $tables2);
  387. alter_tables($tables1, $tables2);
  388. alter_tables_columns($db1, $db2);
  389. process_indexes($tables1, $tables2, $db1, $db2);
  390. }
  391. function usage()
  392. {
  393. echo <<<MSG
  394. THIS IS AN ALPHA VERSION. DO NOT USE ON PRODUCTION!
  395. Usage:
  396. php mysqldiff.php <options>
  397. Options:
  398. --schema-file1 <schema-file> Filename of the file which contain the db schema in sql
  399. Program will create a temp database and load schema
  400. --database1 <database-name> Name of source db.
  401. --host1 <hostname> Server hosting source db.
  402. --user1 <username> Username for connectiong to source db.
  403. --pwd1 <pwd> Password for connectiong to source db.
  404. --schema-file2 <schema-file> Filename of the file which contain the db schema in sql
  405. Program will create a temp database and load schema
  406. --database2 <database-name> Name of destination db.
  407. --host2 <hostname> Server hosting destination db.
  408. --user2 <username> Username for connectiong to destination db.
  409. --pwd2 <pwd> Password for connectiong to destination db.
  410. --drop-tables Whether to generate DROP TABLE statements
  411. for tables present in destination but not
  412. on source database.
  413. Note this can happen when you simply rename
  414. a table. Default is NOT TO DROP.
  415. --drop-columns Whether to generate ALTER TABLE...DROP COLUMN
  416. statements for columns present in destination
  417. but not on source database.
  418. Note this can happen when you simply rename
  419. a column. Default is NOT TO DROP.
  420. --output-file <filename> Filename to save the generated MySQL script.
  421. Default is to write to SDTOUT.
  422. --overwrite Overwrite the output file without asking for
  423. confirmation. Default is to ask.
  424. If source and destination databases share some connection data,
  425. you can specify them using:
  426. --database <database-name> Name of both dbs.
  427. --host <hostname> Server hosting both dbs.
  428. --user <username> Username for connectiong to both dbs.
  429. --pwd <pwd> Password for connectiong to both dbs.
  430. The default hostname is "localhost".
  431. Both passwords are empty by default.
  432. MSG;
  433. exit(0);
  434. }
  435. function error($msg)
  436. {
  437. fputs(STDERR, "mysqldiff: $msg\n");
  438. exit(1);
  439. }
  440. function prompt($msg)
  441. {
  442. echo $msg;
  443. return trim(fgets(STDIN));
  444. }
  445. $options = (object) array(
  446. 'drop_columns' => FALSE,
  447. 'drop_tables' => FALSE,
  448. 'new_table_data' => FALSE,
  449. 'db1' => (object) array(
  450. 'host' => 'localhost',
  451. 'pwd' => NULL,
  452. 'schema' => NULL
  453. ),
  454. 'db2' => (object) array(
  455. 'host' => 'localhost',
  456. 'pwd' => NULL,
  457. 'schema' => NULL
  458. ),
  459. 'output_file' => NULL,
  460. 'ofh' => fopen('php://stdout', 'w'), // output file handle
  461. );
  462. date_default_timezone_set('Europe/Zurich');
  463. $db1 = &$options->db1;
  464. $db2 = &$options->db2;
  465. if ($argc == 1)
  466. usage();
  467. // Parse command line arguments.
  468. for ($i = 1; $i < $argc; $i++) {
  469. switch ($argv[$i]) {
  470. case '--schema-file1':
  471. $db1->schema = $argv[++$i];
  472. break;
  473. case '--host1':
  474. $db1->host = $argv[++$i];
  475. break;
  476. case '--database1':
  477. $db1->database = $argv[++$i];
  478. break;
  479. case '--user1':
  480. $db1->user = $argv[++$i];
  481. break;
  482. case '--pwd1':
  483. $db1->pwd = $argv[++$i];
  484. break;
  485. case '--schema-file2':
  486. $db2->schema = $argv[++$i];
  487. break;
  488. case '--host2':
  489. $db2->host = $argv[++$i];
  490. break;
  491. case '--database2':
  492. $db2->database = $argv[++$i];
  493. break;
  494. case '--user2':
  495. $db2->user = $argv[++$i];
  496. break;
  497. case '--pwd2':
  498. $db2->pwd = $argv[++$i];
  499. break;
  500. case '--host':
  501. $db1->host = $db2->host = $argv[++$i];
  502. break;
  503. case '--database':
  504. $db1->database = $db2->database = $argv[++$i];
  505. break;
  506. case '--user':
  507. $db1->user = $db2->user = $argv[++$i];
  508. break;
  509. case '--pwd':
  510. $db1->pwd = $db2->pwd = $argv[++$i];
  511. break;
  512. case '--drop-columns':
  513. $options->drop_columns = TRUE;
  514. break;
  515. case '--drop-tables':
  516. $options->drop_tables = TRUE;
  517. break;
  518. case '--new-table-data':
  519. $options->new_table_data = TRUE;
  520. break;
  521. case '--output-file':
  522. $options->output_file = $argv[++$i];
  523. break;
  524. case '--overwrite':
  525. $options->overwrite = TRUE;
  526. break;
  527. case '--help':
  528. case '-h':
  529. usage();
  530. default:
  531. error("don't know what to do with \"{$argv[$i]}\"");
  532. }
  533. }
  534. /*
  535. $db1->database = 'diskstoragecatalog';
  536. $db2->database = 'diskstoragecatalog2';
  537. $db1->user = $db2->user = 'root';
  538. $options->output_dir = 'c:/temp/perico';
  539. $options->overwrite = TRUE;
  540. */
  541. if (!$db1->database && !$db1->schema)
  542. error("source database or schema file must be specified with --schema-file1, --database1 or --database");
  543. if ($db1->schema) {
  544. if (!file_exists($db1->schema))
  545. error("schema file 1 does not exist");
  546. $db1->database = "tmp_schema_" . uniqid();
  547. }
  548. if (!$db2->database && !$db2->schema)
  549. error("destination database or schema file must be specified with --schema-file2, --database2 or --database");
  550. if ($db2->schema) {
  551. if (!file_exists($db1->schema))
  552. error("schema file 2 does not exist");
  553. $db2->database = "tmp_schema_" . uniqid();
  554. }
  555. if ($db1->host == $db2->host && $db1->database == $db2->database && !$db1->schema && !$db2->schema)
  556. error("databases names must be different if they reside on the same host");
  557. if ($options->output_file) {
  558. if (file_exists($options->output_file) && !$options->overwrite) {
  559. if (prompt("Output file $options->output_file exists. Overwrite it (y/n)? ") != 'y')
  560. exit(0);
  561. }
  562. $options->ofh = @fopen($options->output_file, 'w') or error("error creating output file $options->output_file");
  563. }
  564. $db1->link = mysqli_connect($db1->host, $db1->user, $db1->pwd) or error('Connection 1 failed');
  565. create_schema_db($db1);
  566. mysqli_select_db($db1->link, $db1->database) or error(mysqli_error($db1->link));
  567. $db2->link = mysqli_connect($db2->host, $db2->user, $db2->pwd) or error('Connection 2 failed');
  568. create_schema_db($db2);
  569. mysqli_select_db($db2->link, $db2->database) or error(mysqli_error($db2->link));
  570. load_schema_db($db1);
  571. load_schema_db($db2);
  572. populate_schemata_info($db1);
  573. populate_schemata_info($db2);
  574. process_database($db1, $db2);
  575. process_tables($db1, $db2);
  576. drop_schema_db($db1);
  577. drop_schema_db($db2);