24 error_reporting(E_ALL);
25 ini_set(
'memory_limit',
'256M');
95 $destination_dsn = Array(
106 if (php_sapi_name() !=
'cli') trigger_error(
"You can only run this script from the command line\n", E_USER_ERROR);
108 $SYSTEM_ROOT = (isset($_SERVER[
'argv'][1])) ? $_SERVER[
'argv'][1] :
'';
109 if (empty($SYSTEM_ROOT) || !is_dir($SYSTEM_ROOT)) {
110 echo
"ERROR: You need to supply the path to the source System Root as the first argument\n";
117 if (empty($source_dsn) || empty($destination_dsn)) {
118 echo
"ERROR: Please fill in the source & destination dsn's\n";
122 if (empty($source_dsn[
'DSN']) || empty($destination_dsn[
'DSN'])) {
123 echo
"ERROR: Please fill in the source & destination dsn's\n";
127 require_once $SYSTEM_ROOT.
'/fudge/dev/dev.inc';
128 require_once $SYSTEM_ROOT.
'/fudge/db_extras/db_extras.inc';
129 require_once $SYSTEM_ROOT.
'/core/include/general.inc';
130 require_once $SYSTEM_ROOT.
'/core/lib/DAL/DAL.inc';
131 require_once $SYSTEM_ROOT.
'/core/lib/MatrixDAL/MatrixDAL.inc';
140 $dest_db = serialize($destination_dsn);
141 $source_db = serialize($source_dsn);
146 }
catch (Exception $e) {
147 echo
"Unable to connect to the source db: " . $e->getMessage() .
"\n";
153 }
catch (Exception $e) {
154 echo
"Unable to connect to the destination db: " . $e->getMessage() .
"\n";
168 $dest_exists = checkDestinationDb();
170 echo
"Before running the conversion script, you need to run step_02.php from the installer to create the destination tables.\n";
175 $SYSTEM_ROOT .
'/core/assets/tables.xml',
181 $packages = scandir($SYSTEM_ROOT .
'/packages');
182 foreach ($packages as $package) {
183 $xml_path = $SYSTEM_ROOT .
'/packages/' . $package .
'/tables.xml';
184 if (is_file($xml_path)) {
185 $xml_files[] = $xml_path;
195 $nulls_convert_list = array(
209 foreach ($xml_files as $xml_filename) {
210 $info = parse_tables_xml($xml_filename, $dest_db);
220 foreach ($info[
'sequences'] as $sequence) {
221 $sequence_values[$sequence] = getSequenceValue($sequence);
233 pre_echo(
'Dropping destination sequences');
235 $del_seqs = getSequences();
237 foreach ($del_seqs as $sequence) {
238 printName(
'Dropping: '.strtolower($sequence));
239 $sql =
'DROP SEQUENCE ' . $sequence;
242 printUpdateStatus(
'Failure, unable to run query: ' . $sql);
245 printUpdateStatus(
'OK');
248 pre_echo(
'Dropping destination indexes');
250 $del_indexes = getIndexes();
251 foreach($del_indexes as $index) {
252 if (substr($index, 0, 3) ==
'sq_') {
253 printName(
'Dropping: '.$index);
254 $sql =
'DROP INDEX ' . $index;
257 printUpdateStatus(
'Failure, unable to run query: ' . $sql);
260 printUpdateStatus(
'OK');
266 pre_echo(
'Truncating destination tables');
268 foreach ($info[
'tables'] as $tablename => $table_info) {
269 printName(
'Truncating: sq_'.$tablename);
270 $sql =
'TRUNCATE TABLE sq_'.$tablename;
273 printUpdateStatus(
'Failure, unable to run query: ' . $sql);
276 printUpdateStatus(
'OK');
284 foreach ($info[
'tables'] as $tablename => $table_info) {
285 if ($tablename ===
'sch_idx') {
286 pre_echo(
'Skipping search table - you will need to re-index');
290 pre_echo(
'Starting table: sq_'.$tablename);
292 $columns = array_keys($table_info[
'columns']);
293 $sql = generateSQL($tablename, $columns);
300 if (!isset($table_info[
'primary_key'])) {
301 $msg =
"This table (sq_${tablename}) cannot be converted since it doesn't have a primary key.\n";
302 $msg .=
"A primary key is required to guarantee the chunking of data doesn't\n";
303 $msg .=
"miss anything and all data is fetched correctly from the table.\n";
304 $msg .=
"Skipping table.\n";
317 $num_to_fetch = 10000;
319 $primary_key = implode(
',', $table_info[
'primary_key']);
321 $source_sql =
'SELECT * FROM sq_' . $tablename .
' ORDER BY ' . $primary_key;
323 $fetch_source_sql = db_extras_modify_limit_clause($source_sql, $source_dsn[
'type'], $num_to_fetch, $start);
327 if (empty($source_data)) {
328 printName(
'Table is empty');
329 printUpdateStatus(
'OK');
340 $count = count($source_data);
342 while (!empty($source_data)) {
351 printName(
'Inserting Data (' . number_format($start) .
' - ' . number_format($start + $count) .
' rows)');
353 printUpdateStatus(
"..",
"\r");
355 foreach($source_data as $key => $data) {
356 foreach ($data as $data_key => $data_value) {
358 if (is_numeric($data_key)) {
368 if (!in_array($data_key, $columns)) {
376 if (in_array($tablename, $nulls_convert_list)) {
377 if ($source_dsn[
'type'] ===
'oci' && $data_value === NULL) {
388 if (is_resource($data_value)) {
389 $stream = $data_value;
390 $data_value = stream_get_contents($stream);
400 if ($trans_count % 10000 == 0) {
407 printName(
'Inserting Data (' . number_format($start) .
' - ' . number_format($start + $count) .
' rows)');
408 printUpdateStatus(
'OK',
"\r");
421 if (count($source_data) < $num_to_fetch) {
425 $start += $num_to_fetch;
427 $fetch_source_sql = db_extras_modify_limit_clause($source_sql, $source_dsn[
'type'], $num_to_fetch, $start);
431 $count = count($source_data);
434 printUpdateStatus(null,
"\n");
443 pre_echo(
'Rebuilding Indexes');
445 foreach($info[
'tables'] as $tablename => $table_info) {
446 if (!empty($table_info[
'indexes'])) {
447 foreach ($table_info[
'indexes'] as $index_col => $index_info) {
448 printName(
'Creating index sq_'.$tablename.
'_'.$index_info[
'name']);
449 $sql = create_index_sql($tablename, $index_info[
'columns'], $index_info[
'name'], $index_info[
'type']);
452 printUpdateStatus(
'Failure, unable to run query: ' . $sql);
455 printUpdateStatus(
'OK');
457 if ($table_info[
'rollback']) {
458 printName(
'Creating index sq_rb_'.$tablename.
'_'.$index_info[
'name']);
459 $sql = create_index_sql(
'rb_'.$tablename, $index_info[
'columns'], $index_info[
'name'], $index_info[
'type']);
462 printUpdateStatus(
'Failure, unable to run query: ' . $sql);
465 printUpdateStatus(
'OK');
471 pre_echo(
'Rebuilding Sequences');
473 foreach ($info[
'sequences'] as $sequence) {
474 $new_seq_start = $sequence_values[$sequence];
476 printName(
'Creating sq_'.$sequence.
'_seq ('.$new_seq_start.
')');
477 $sql =
'CREATE SEQUENCE sq_'.$sequence.
'_seq START WITH '.$new_seq_start;
480 printUpdateStatus(
'Failure, unable to run query: ' . $sql);
483 printUpdateStatus(
'OK');
496 pre_echo(
'Conversion is complete');
499 $rebakeCmd =
"/usr/bin/php ${SYSTEM_ROOT}/scripts/dev/rebake.php ${SYSTEM_ROOT}";
500 echo
"rebake.php now needs to run. Do this now [Y/n] ? ";
501 $response = strtolower(trim(fgets(STDIN)));
502 if (empty($response) === TRUE) {
506 if ($response !=
'y') {
507 pre_echo(
"You will need to run ${rebakeCmd} manually.\n");
511 echo
"Running rebake.php now .. \n";
514 exec($rebakeCmd, $output, $rc);
515 pre_echo(
"Rebake returned the following:\n".implode(
"\n", $output).
"\n");
516 pre_echo(
'This script is now finished.');
527 function parse_tables_xml($xml_file, $dest_db)
530 $root =
new SimpleXMLElement($xml_file, LIBXML_NOCDATA, TRUE);
531 }
catch (Exception $e) {
532 throw new Exception(
'Could not parse tables XML file: '.$e->getMessage());
535 if (($root->getName() !=
'schema') || !isset($root->tables) || !isset($root->sequences)) {
536 throw new Exception(
'Tables XML file is not valid.');
537 trigger_localised_error(
'SYS0012', E_USER_WARNING);
542 $info[
'tables'] = Array();
543 $info[
'sequences'] = Array();
547 foreach ($root->tables->table as $table) {
548 $table_name = (string)$table->attributes()->name;
550 $info[
'tables'][$table_name] = Array();
551 $info[
'tables'][$table_name][
'rollback'] = (($table->attributes()->{
'require_rollback'} == 1) ? TRUE : FALSE);
554 $info[
'tables'][$table_name][
'columns'] = Array();
556 foreach ($table->columns->column as $table_column) {
557 $column_name = (string)$table_column->attributes()->name;
559 $info[
'tables'][$table_name][
'columns'][$column_name] = Array();
560 $info[
'tables'][$table_name][
'columns'][$column_name][
'allow_null'] = (($table_column->attributes()->{
'allow_null'} == 1) ? TRUE : FALSE);
567 foreach ($table_column->children() as $column_var) {
568 switch (strtolower($column_var->getName())) {
572 if (is_null($type)) $type = (
string)$column_var;
574 case 'type_variations' :
576 foreach ($column_var->children() as $variation) {
577 if ($variation->getName() == _getDbType(
false, $dest_db)) {
578 $type = (string)$variation;
584 if (trim((
string)$column_var) !=
'') {
585 $default = (string)$column_var;
593 $info[
'tables'][$table_name][
'columns'][$column_name][
'type'] = $type;
594 $info[
'tables'][$table_name][
'columns'][$column_name][
'default'] = $default;
598 $info[
'tables'][$table_name][
'primary_key'] = Array();
599 $info[
'tables'][$table_name][
'unique_key'] = Array();
601 if (isset($table->keys) && (count($table->keys->children()) > 0)) {
602 foreach ($table->keys->children() as $table_key) {
603 $index_db_type = $table_key->attributes()->db;
604 if (!is_null($index_db_type) && ((
string)$index_db_type != _getDbType(
false, $dest_db))) {
609 $key_columns = Array();
610 foreach ($table_key->column as $table_key_column) {
611 $col_name = (string)$table_key_column->attributes()->name;
612 $key_columns[] = $col_name;
615 if ($table_key->getName() ==
'primary_key') {
616 $info[
'tables'][$table_name][
'primary_key'][] = $col_name;
618 if ($table_key->getName() ==
'unique_key') {
619 $info[
'tables'][$table_name][
'unique_key'][] = $col_name;
628 if (!empty($table->indexes->index)) {
629 foreach ($table->indexes->index as $table_index) {
632 $index_cols = Array();
633 foreach ($table_index->column as $table_index_column) {
634 $index_cols[] = (string)$table_index_column->attributes()->name;
638 $index_name = isset($table_index->attributes()->name) ? (
string)$table_index->attributes()->name : reset($index_cols);
639 $index_type = isset($table_index->attributes()->type) ? (
string)$table_index->attributes()->type : NULL;
640 $index_db_type = isset($table_index->attributes()->db) ? (
string)$table_index->attributes()->db : NULL;
643 'name' => $index_name,
644 'columns' => $index_cols,
645 'type' => $index_type,
646 'db_type' => $index_db_type,
648 $info['tables'][$table_name]['indexes'][$index_name] = $index_info;
654 foreach ($root->sequences->sequence as $sequence) {
655 $sequence_name = (string)$sequence->attributes()->name;
656 $info[
'sequences'][] = $sequence_name;
672 function generateSQL($tablename, $columns)
676 $sql =
'INSERT INTO sq_'.$tablename .
' (' . implode(
', ', $columns) .
') values (:' . implode(
', :', $columns) .
')';
693 function create_index_sql($tablename, $column, $index_name=null, $index_type=null)
695 if (is_array($column)) {
696 $column = implode(
',', $column);
699 if (is_null($index_name)) {
700 $index_name = str_replace(
',',
'_', $column);
703 $dbtype = _getDbType();
705 $sql =
'CREATE INDEX sq_'.$tablename.
'_'.$index_name.
' ON sq_'.$tablename;
707 if (!empty($index_type)) {
708 $sql .=
'('.$column.
')';
709 if ($dbtype ==
'oci') {
710 $sql .=
' indextype is '.$index_type;
713 $sql .=
' ('.$column.
')';
724 function printName($name)
726 printf (
'%s%'.(60 - strlen($name)).
's', $name,
'');
731 function printUpdateStatus($status, $newline=
"\n")
733 if ($status !== null) {
748 function checkDestinationDb()
750 $dbtype = _getDbType();
757 $sql =
'select table_name from user_tables';
760 $sql =
'select table_name from information_schema.tables WHERE table_schema NOT IN (\'pg_catalog\', \'information_schema\')';
764 if (empty($dest_tables)) {
776 function getSequences()
778 $dbtype = _getDbType();
782 $sql =
'SELECT sequence_name FROM user_sequences';
785 $sql =
'SELECT relname FROM pg_catalog.pg_statio_user_sequences WHERE schemaname = \'public\'';
789 $sequence_names = array();
791 foreach($sequences as $key => $value) {
792 $sequence_names[] = $value[0];
794 return $sequence_names;
806 function getSequenceValue($sequence=
'')
808 $dbtype = _getDbType();
812 $sql =
'SELECT sq_'.$sequence.
'_seq.nextval FROM DUAL';
815 $sql =
'SELECT nextval(\'sq_' . $sequence .
'_seq\') AS nextval';
819 return $value[0][
'nextval'];
828 function getIndexes()
830 $dbtype = _getDbType();
834 $sql =
'SELECT index_name FROM user_indexes';
837 $sql =
'SELECT indexname from pg_indexes where tablename like \'sq_%\' and indexdef not ilike \'create unique index %\'';
842 if ($sql !==
false) {
844 foreach($indexes as $key => $value) {
845 $del_idx[] = strtolower($value[0]);
860 function _getDbType()
862 $dbtype = MatrixDAL::GetDbType();
864 if ($dbtype instanceof PDO) {
865 $dbtype = $dbtype->getAttribute(PDO::ATTR_DRIVER_NAME);
867 return strtolower($dbtype);