34 if (ini_get(
'memory_limit') !=
'-1') ini_set(
'memory_limit', -1);
35 error_reporting(E_ALL);
40 if (php_sapi_name() !=
'cli') trigger_error(
"You can only run this script from the command line\n", E_USER_ERROR);
42 $SYSTEM_ROOT = (isset($_SERVER[
'argv'][1])) ? $_SERVER[
'argv'][1] :
'';
43 if (empty($SYSTEM_ROOT)) {
44 echo
"ERROR: You need to supply the path to the System Root as the first argument\n";
48 if (!is_dir($SYSTEM_ROOT) || !is_readable($SYSTEM_ROOT.
'/core/include/init.inc')) {
49 echo
"ERROR: Path provided doesn't point to a Matrix installation's System Root. Please provide correct path and try again.\n";
53 require_once $SYSTEM_ROOT.
'/data/private/conf/db.inc';
54 require_once $SYSTEM_ROOT.
'/core/include/init.inc';
55 require_once $SYSTEM_ROOT.
'/core/lib/DAL/DAL.inc';
56 require_once $SYSTEM_ROOT.
'/core/lib/MatrixDAL/MatrixDAL.inc';
61 }
catch (Exception $e) {
62 echo
"Unable to connect to the db: " . $e->getMessage() .
"\n";
87 $skip_definition_checks = array (
88 'sq_ast_attr_val_concat',
89 'sq_rb_ast_attr_val_concat',
97 $dbtype = _getDbType();
99 $index_info = getIndexes();
100 $full_index_list = $index_info[
'index_list'];
101 $parallel_list = $index_info[
'parallel_list'];
102 $constraint_list = $index_info[
'constraint_list'];
104 pre_echo(
'Checking Indexes');
106 $sql_commands = array();
107 $bad_indexes = array();
108 $parallel_warnings = array();
109 $postgres_primary_key_warnings = array();
111 $packages = $GLOBALS[
'SQ_SYSTEM']->getInstalledPackages();
112 foreach ($packages as $_pkgid => $pkg_details) {
113 $pkg_name = $pkg_details[
'code_name'];
114 if ($pkg_name ==
'__core__') {
115 $file = $SYSTEM_ROOT.
'/core/assets/tables.xml';
117 $file = SQ_PACKAGES_PATH .
'/' . $pkg_name .
'/tables.xml';
124 if (!is_file($file)) {
127 $info = parse_tables_xml($file, $db_conf[
'db'][
'type']);
129 foreach($info[
'tables'] as $tablename => $table_info) {
130 $tables = array ($tablename);
131 if ($table_info[
'rollback']) {
132 $tables[] =
'rb_' . $tablename;
135 if (!empty($table_info[
'primary_key'])) {
136 foreach ($tables as $tablename) {
150 $expected_idx_name = $tablename .
'_pk';
151 $full_idx_names = array (
153 $tablename .
'_pkey',
154 'sq_' . $tablename .
'_pkey',
155 'sq_' . $tablename .
'_pk',
158 $tablename =
'sq_' . $tablename;
160 printName(
'Checking table ' . $tablename .
' for a primary key');
163 foreach ($full_idx_names as $_idx_pos => $idx_name) {
164 if (in_array($idx_name, array_keys($full_index_list[$tablename]))) {
170 $idx_columns = $table_info[
'primary_key'];
171 if (substr($tablename, 3, 3) ==
'rb_') {
172 array_unshift($idx_columns,
'sq_eff_from');
182 $temp_idx_cols = implode(
',', $idx_columns);
183 if (in_array($temp_idx_cols, $full_index_list[$tablename])) {
184 $idx_name = array_search($temp_idx_cols, $full_index_list[$tablename]);
195 if ($db_conf[
'db'][
'type'] ==
'pgsql') {
196 if ($idx_name !== $expected_idx_name) {
197 $postgres_primary_key_warnings[$tablename] = array (
198 'current' => $idx_name,
199 'expected' => $expected_idx_name,
200 'fields' => $idx_columns,
206 $create_index_statement = create_index_sql($tablename, $idx_columns, $tablename .
'_pk', NULL,
true);
209 printUpdateStatus(
'Missing');
210 $sql_commands[] = $create_index_statement;
213 check_index_parallel($tablename, $idx_name);
215 $index_definition = $full_index_list[$tablename][$idx_name];
216 $found_index_columns = explode(
',', $index_definition);
217 if ($found_index_columns === $idx_columns) {
218 printUpdateStatus(
'OK');
220 printUpdateStatus(
'Incorrect');
221 $bad_indexes[] = array(
'table_name' => $tablename,
'index_name' => $idx_name,
'expected' => implode(
',', $idx_columns),
'found' => $index_definition,
'primary_key' =>
true);
222 $sql_commands[] = $create_index_statement;
224 unset($full_index_list[$tablename][$idx_name]);
229 if (!empty($table_info[
'indexes'])) {
230 foreach ($table_info[
'indexes'] as $index_col => $index_info) {
235 if (isset($index_info[
'db_type'])) {
236 if ($index_info[
'db_type'] !== $db_conf[
'db'][
'type']) {
241 foreach ($tables as $tablename) {
242 $tablename =
'sq_' . $tablename;
243 $full_idx_name = $tablename .
'_' . $index_info[
'name'];
244 printName(
'Checking for index ' . $full_idx_name);
247 if (isset($full_index_list[$tablename][$full_idx_name])) {
256 $temp_idx_cols = implode(
',', $index_info[
'columns']);
257 if (in_array($temp_idx_cols, $full_index_list[$tablename])) {
258 $full_idx_name = array_search($temp_idx_cols, $full_index_list[$tablename]);
264 printUpdateStatus(
'Missing');
267 if (in_array($full_idx_name, $skip_definition_checks)) {
268 unset($full_index_list[$tablename][$full_idx_name]);
269 printUpdateStatus(
'OK');
273 check_index_parallel($tablename, $full_idx_name);
275 $index_definition = $full_index_list[$tablename][$full_idx_name];
276 $found_index_columns = explode(
',', $index_definition);
278 unset($full_index_list[$tablename][$full_idx_name]);
280 if ($found_index_columns === $index_info[
'columns']) {
281 printUpdateStatus(
'OK');
284 printUpdateStatus(
'Incorrect');
285 $bad_indexes[] = array(
'index_name' => $full_idx_name,
'expected' => implode(
',', $index_info[
'columns']),
'found' => $index_definition);
290 $sql_commands[] = create_index_sql($tablename, $index_info[
'columns'], $index_info[
'name'], $index_info[
'type']);
295 if (!empty($table_info[
'unique_key'])) {
296 foreach ($tables as $tablename) {
298 $tablename =
'sq_' . $tablename;
300 $idx_columns = $table_info[
'unique_key'];
301 if (substr($tablename, 3, 3) ==
'rb_') {
302 array_unshift($idx_columns,
'sq_eff_from');
310 $full_idx_name = $tablename .
'_' . $idx_columns[0] .
'_key';
311 printName(
'Checking for index ' . $full_idx_name);
313 $create_index_statement = create_index_sql($tablename, $idx_columns, $full_idx_name, NULL,
false,
true);
316 if (isset($full_index_list[$tablename][$full_idx_name])) {
325 $temp_idx_cols = implode(
',', $idx_columns);
326 if (in_array($temp_idx_cols, $full_index_list[$tablename])) {
327 $full_idx_name = array_search($temp_idx_cols, $full_index_list[$tablename]);
333 printUpdateStatus(
'Missing');
334 $sql_commands[] = $create_index_statement;
337 if (in_array($full_idx_name, $skip_definition_checks)) {
338 unset($full_index_list[$tablename][$full_idx_name]);
339 printUpdateStatus(
'OK');
343 check_index_parallel($tablename, $full_idx_name);
345 $index_definition = $full_index_list[$tablename][$full_idx_name];
346 $found_index_columns = explode(
',', $index_definition);
348 unset($full_index_list[$tablename][$full_idx_name]);
350 if ($found_index_columns === $idx_columns) {
351 printUpdateStatus(
'OK');
355 printUpdateStatus(
'Incorrect');
357 $bad_indexes[] = array(
'index_name' => $full_idx_name,
'expected' => implode(
',', $idx_columns),
'found' => $index_definition);
358 $sql_commands[] = $create_index_statement;
365 $extra_message_shown =
false;
366 $dbtype = _getDbType();
367 if ($dbtype ===
'pgsql') {
368 pre_echo(
'Checking locale settings');
369 $locale_query =
"select setting from pg_settings where name='lc_ctype'";
371 $locale = $locale_info[0][
'setting'];
372 if ($locale !==
'C') {
373 $extra_message_shown =
true;
374 $msg =
"Your database 'locale' setting is " . $locale .
"\n";
375 $msg .=
"You may get some performance improvements changing this to 'C'\n";
376 $msg .=
"However changing this requires re-initializing the whole db cluster.\n";
377 $msg .=
"See http://www.postgresql.org/docs/8.1/static/charset.html for more information.";
382 pre_echo(
'Check complete');
383 if (!empty($bad_indexes)) {
384 $extra_message_shown =
true;
385 $msg =
"Some indexes had incorrect definitions.\n";
386 $msg .=
"To fix these, you will need to drop the old indexes before re-adding them:\n\n";
387 foreach ($bad_indexes as $details) {
389 if ($dbtype ==
'oci') {
390 $details[
'index_name'] = strtoupper($details[
'index_name']);
401 ($dbtype ==
'pgsql' && isset($details[
'primary_key'])) ||
402 ($dbtype ==
'oci' && isset($constraint_list[$details[
'index_name']]))
404 $tablename = $details[
'table_name'];
405 if (substr($tablename, 0, 3) !=
'sq_') {
406 $tablename =
'sq_' . $tablename;
408 $msg .=
"ALTER TABLE " . $details[
'table_name'] .
" DROP CONSTRAINT " . $details[
'index_name'] .
";\n";
411 $msg .=
"DROP INDEX " . $details[
'index_name'] .
";\n";
416 if (!empty($sql_commands)) {
417 $extra_message_shown =
true;
418 $msg =
"Some expected indexes were missing or incorrect.\n";
419 $msg .=
"To fix the database, please run the following queries:\n\n" . implode(
"\n", $sql_commands);
423 if (!empty($parallel_warnings)) {
424 $extra_message_shown =
true;
425 $msg =
"Some indexes may have issues.\n";
426 $msg .=
"If these changes have been made deliberately, ignore this warning.\n\n";
427 $msg .=
"The following indexes have been made to run in parallel:\n";
428 foreach ($parallel_warnings as $row) {
429 foreach ($row as $idx_name => $parallel_setting) {
430 $msg .= $idx_name .
" (" . $parallel_setting .
")\n";
433 $msg .=
"This can lead to performance loss.\n\n";
435 $msg .=
"To reset these, run the following command(s):\n";
436 foreach ($parallel_warnings as $row) {
437 foreach ($row as $idx_name => $parallel_setting) {
438 $msg .=
"ALTER INDEX " . $idx_name .
" REBUILD PARALLEL 1;\n";
444 if (!empty($postgres_primary_key_warnings)) {
445 $extra_message_shown =
true;
446 $msg =
"Some tables have incorrect names for primary keys.\n";
447 $msg .=
"This won't cause any problems, but upgrade guides may be harder to follow\n";
448 $msg .=
"as they expect the names to be a certain way.\n";
449 $msg .=
"To fix these tables, run the following commands\n";
450 $msg .=
"They can take a while depending on the size of the table.\n\n";
452 foreach ($postgres_primary_key_warnings as $tablename => $details) {
453 $msg .=
"ALTER TABLE ONLY " . $tablename .
" DROP CONSTRAINT " . $details[
'current'] .
";\n";
454 $msg .=
"ALTER TABLE " . $tablename .
" ADD CONSTRAINT " . $details[
'expected'] .
" PRIMARY KEY (" . implode(
',', $details[
'fields']) .
");\n";
460 $show_extra_indexes_message =
false;
461 $extra_indexes_message =
"Extra indexes have been found on the following tables.\n";
462 $extra_indexes_message .=
"If these changes have been made deliberately, ignore this warning.\n";
463 $extra_indexes_message .=
"This can lead to performance loss.\n";
464 $extra_indexes_message .=
"To remove these, run the following command(s):\n\n";
465 foreach ($full_index_list as $tablename => $info) {
466 if (strpos($tablename,
'sq_rb_') !== FALSE) {
469 if (empty($info) === TRUE) {
472 $extra_message_shown =
true;
473 $show_extra_indexes_message =
true;
474 $extra_indexes_message .=
"These were found on table ".$tablename.
":\n";
475 foreach ($info as $index_name => $fields) {
476 $extra_indexes_message .=
"\tDROP INDEX ".$index_name.
";\n";
478 $extra_indexes_message .=
"\n";
481 if ($show_extra_indexes_message) {
482 pre_echo(trim($extra_indexes_message));
485 if (!$extra_message_shown) {
486 pre_echo(
'Everything has been checked and no problems were found.');
499 function parse_tables_xml($xml_file, $db_type)
502 $dbtype = _getDbType();
505 $root =
new SimpleXMLElement($xml_file, LIBXML_NOCDATA, TRUE);
506 }
catch (Exception $e) {
507 throw new Exception(
'Could not parse tables XML file: '.$e->getMessage());
510 if (($root->getName() !=
'schema') || !isset($root->tables) || !isset($root->sequences)) {
511 throw new Exception(
'Tables XML file is not valid.');
512 trigger_localised_error(
'SYS0012', E_USER_WARNING);
517 $info[
'tables'] = Array();
518 $info[
'sequences'] = Array();
522 foreach ($root->tables->table as $table) {
523 $table_name = (string)$table->attributes()->name;
525 $info[
'tables'][$table_name] = Array();
526 $info[
'tables'][$table_name][
'rollback'] = (($table->attributes()->{
'require_rollback'} == 1) ? TRUE : FALSE);
529 $info[
'tables'][$table_name][
'columns'] = Array();
531 foreach ($table->columns->column as $table_column) {
532 $column_name = (string)$table_column->attributes()->name;
534 $info[
'tables'][$table_name][
'columns'][$column_name] = Array();
535 $info[
'tables'][$table_name][
'columns'][$column_name][
'allow_null'] = (($table_column->attributes()->{
'allow_null'} == 1) ? TRUE : FALSE);
542 foreach ($table_column->children() as $column_var) {
543 switch (strtolower($column_var->getName())) {
547 if (is_null($type)) $type = (
string)$column_var;
549 case 'type_variations' :
551 foreach ($column_var->children() as $variation) {
552 if ($variation->getName() == $dbtype) {
553 $type = (string)$variation;
559 if (trim((
string)$column_var) !=
'') {
560 $default = (string)$column_var;
568 $info[
'tables'][$table_name][
'columns'][$column_name][
'type'] = $type;
569 $info[
'tables'][$table_name][
'columns'][$column_name][
'default'] = $default;
573 $info[
'tables'][$table_name][
'primary_key'] = Array();
574 $info[
'tables'][$table_name][
'unique_key'] = Array();
576 if (isset($table->keys) && (count($table->keys->children()) > 0)) {
577 foreach ($table->keys->children() as $table_key) {
578 $index_db_type = $table_key->attributes()->db;
579 if (!is_null($index_db_type) && ((
string)$index_db_type != $dbtype)) {
584 $key_columns = Array();
585 foreach ($table_key->column as $table_key_column) {
586 $col_name = (string)$table_key_column->attributes()->name;
587 $key_columns[] = $col_name;
590 if ($table_key->getName() ==
'primary_key') {
591 $info[
'tables'][$table_name][
'primary_key'][] = $col_name;
593 if ($table_key->getName() ==
'unique_key') {
594 $info[
'tables'][$table_name][
'unique_key'][] = $col_name;
603 if (!empty($table->indexes->index)) {
604 foreach ($table->indexes->index as $table_index) {
607 $index_cols = Array();
608 foreach ($table_index->column as $table_index_column) {
609 $index_cols[] = (string)$table_index_column->attributes()->name;
613 $index_name = isset($table_index->attributes()->name) ? (
string)$table_index->attributes()->name : reset($index_cols);
614 $index_type = isset($table_index->attributes()->type) ? (
string)$table_index->attributes()->type : NULL;
615 $index_db_type = isset($table_index->attributes()->db) ? (
string)$table_index->attributes()->db : NULL;
618 'name' => $index_name,
619 'columns' => $index_cols,
620 'type' => $index_type,
621 'db_type' => $index_db_type,
623 $info['tables'][$table_name]['indexes'][$index_name] = $index_info;
629 foreach ($root->sequences->sequence as $sequence) {
630 $sequence_name = (string)$sequence->attributes()->name;
631 $info[
'sequences'][] = $sequence_name;
651 function create_index_sql($tablename, $column, $index_name=null, $index_type=null, $primary_key=
false, $unique_key=
false)
653 if (substr($tablename, 0, 3) !=
'sq_') {
654 $tablename =
'sq_' . $tablename;
657 if (is_array($column)) {
658 $column = implode(
',', $column);
661 if (is_null($index_name)) {
662 $index_name = str_replace(
',',
'_', $column);
665 if (!$primary_key && !$unique_key) {
666 $sql =
'CREATE INDEX '.$tablename.
'_'.$index_name.
' ON '.$tablename;
667 if (!empty($index_type)) {
668 $sql .=
'('.$column.
') indextype is '.$index_type;
670 $sql .=
' ('.$column.
')';
680 $constraint_name = $tablename .
'_pk';
681 if (substr($constraint_name, 0, 3) ==
'sq_') {
682 $constraint_name = substr($constraint_name, 3);
685 return 'ALTER TABLE ' . $tablename .
' ADD CONSTRAINT ' . $constraint_name .
' PRIMARY KEY (' . $column .
');';
689 return 'CREATE UNIQUE INDEX ' . $index_name .
' ON ' . $tablename .
'(' . $column .
');';
698 function printName($name)
700 printf (
'%s%'.(60 - strlen($name)).
's', $name,
'');
705 function printUpdateStatus($status)
707 echo
"[ $status ]\n";
718 function getIndexes()
722 $dbtype = _getDbType();
726 $sql =
"SELECT u.table_name as tablename, u.index_name as indexname, DBMS_METADATA.GET_DDL('INDEX',u.index_name) AS indexdef, TRIM(u.degree) AS parallel, CASE WHEN c.constraint_name IS NULL THEN 0 ELSE 1 END AS constraint FROM USER_INDEXES u LEFT JOIN user_constraints c ON (u.index_name=c.constraint_name) WHERE u.TABLE_NAME LIKE 'SQ_%' ORDER BY u.table_name";
729 $sql =
'SELECT tablename, indexname, indexdef from pg_indexes where tablename like \'sq_%\'';
733 $constraint_list = array();
737 $parallel_list = array();
739 if ($sql !==
false) {
741 foreach($indexes as $key => $value) {
742 $tablename = strtolower($value[
'tablename']);
743 if (!isset($idx_list[$tablename])) {
744 $idx_list[$tablename] = array();
748 $idx_def = $value[
'indexdef'];
751 $idx_name = $value[
'indexname'];
757 preg_match(
'/index "' . $db_conf[
'db'][
'user'] .
'"."' . $idx_name .
'" on "' . $db_conf[
'db'][
'user'] .
'"."(.*?)" \((.*?)\)/i', $idx_def, $matches);
759 if (!empty($matches) && !empty($matches[2])) {
760 $idx_columns = str_replace(array(
' ',
'"'),
'', strtolower($matches[2]));
763 $idx_name = strtolower($idx_name);
769 if (empty($idx_columns) === TRUE) {
773 $idx_list[$tablename][$idx_name] = $idx_columns;
775 $parallel_list[$tablename][$idx_name] = $value[
'parallel'];
781 if ($value[
'constraint'] == 1) {
782 $constraint_list[strtoupper($idx_name)] = 1;
787 $idx_def = $value[
'indexdef'];
798 preg_match(
'/USING btree \((.*?)\)$/i', $idx_def, $matches);
799 if (!empty($matches[1])) {
801 $idx_columns = str_replace(
' ',
'', str_replace(
'"',
'', $matches[1]));
803 $idx_list[$tablename][strtolower($value[
'indexname'])] = $idx_columns;
809 'index_list' => $idx_list,
810 'parallel_list' => $parallel_list,
811 'constraint_list' => $constraint_list,
824 function _getDbType()
826 $dbtype = MatrixDAL::GetDbType();
828 if ($dbtype instanceof PDO) {
829 $dbtype = $dbtype->getAttribute(PDO::ATTR_DRIVER_NAME);
831 return strtolower($dbtype);
834 function check_index_parallel($tablename=null, $idx_name=null)
837 if ($tablename === null || $idx_name === null) {
841 $dbtype = _getDbType();
843 global $parallel_list;
844 global $parallel_warnings;
846 if ($dbtype ==
'oci') {
847 $parallel_check = $parallel_list[$tablename][$idx_name];
848 if ($parallel_check > 1) {
849 $parallel_warnings[] = array($idx_name => $parallel_check);