diff --git a/CRM/Core/BAO/SchemaHandler.php b/CRM/Core/BAO/SchemaHandler.php index b135036d645b..c81ed88043b5 100644 --- a/CRM/Core/BAO/SchemaHandler.php +++ b/CRM/Core/BAO/SchemaHandler.php @@ -557,7 +557,7 @@ public static function alterFieldLength($customFieldID, $tableName, $columnName, * Check if the table has an index matching the name. * * @param string $tableName - * @param array $indexName + * @param string $indexName * * @return bool */ @@ -654,15 +654,20 @@ public static function addIndexSignature($table, &$indices) { * @param bool $dropFalseIndices * If set - this function deletes false indices present in the DB which mismatches the expected * values of xml file so that civi re-creates them with correct values using createMissingIndices() function. + * @param array|FALSE $tables + * An optional array of tables - if provided the results will be restricted to these tables. * * @return array * index specifications */ - public static function getMissingIndices($dropFalseIndices = FALSE) { + public static function getMissingIndices($dropFalseIndices = FALSE, $tables = FALSE) { $requiredSigs = $existingSigs = []; // Get the indices defined (originally) in the xml files $requiredIndices = CRM_Core_DAO_AllCoreTables::indices(); $reqSigs = []; + if ($tables !== FALSE) { + $requiredIndices = array_intersect_key($requiredIndices, array_fill_keys($tables, TRUE)); + } foreach ($requiredIndices as $table => $indices) { $reqSigs[] = CRM_Utils_Array::collect('sig', $indices); } diff --git a/api/v3/System.php b/api/v3/System.php index c91583fc0f7e..2abbdf4bc44c 100644 --- a/api/v3/System.php +++ b/api/v3/System.php @@ -411,22 +411,56 @@ function _civicrm_api3_system_updatelogtables_spec(&$params) { * Update indexes. * * This adds any indexes that exist in the schema but not the database. + * + * @param array $params + * + * @return array */ -function civicrm_api3_system_updateindexes() { - CRM_Core_BAO_SchemaHandler::createMissingIndices(CRM_Core_BAO_SchemaHandler::getMissingIndices(TRUE)); +function civicrm_api3_system_updateindexes(array $params):array { + $tables = empty($params['tables']) ? FALSE : (array) $params['tables']; + CRM_Core_BAO_SchemaHandler::createMissingIndices(CRM_Core_BAO_SchemaHandler::getMissingIndices(TRUE, $tables)); return civicrm_api3_create_success(1); } +/** + * Declare metadata for api System.getmissingindices + * + * @param array $params + */ +function _civicrm_api3_system_updateindexes_spec(array &$params) { + $params['tables'] = [ + 'type' => CRM_Utils_Type::T_STRING, + 'api.default' => FALSE, + 'title' => ts('Optional tables filter'), + ]; +} + /** * Get an array of indices that should be defined but are not. * + * @param array $params + * * @return array */ -function civicrm_api3_system_getmissingindices() { - $indices = CRM_Core_BAO_SchemaHandler::getMissingIndices(FALSE); +function civicrm_api3_system_getmissingindices($params) { + $tables = empty($params['tables']) ? FALSE : (array) $params['tables']; + $indices = CRM_Core_BAO_SchemaHandler::getMissingIndices(FALSE, $tables); return civicrm_api3_create_success($indices); } +/** + * Declare metadata for api System.getmissingindices + * + * @param array $params + */ +function _civicrm_api3_system_getmissingindices_spec(&$params) { + $params['tables'] = [ + 'type' => CRM_Utils_Type::T_STRING, + 'api.default' => FALSE, + 'title' => ts('Optional tables filter'), + ]; +} + /** * Creates missing log tables. * diff --git a/tests/phpunit/CRM/Core/BAO/SchemaHandlerTest.php b/tests/phpunit/CRM/Core/BAO/SchemaHandlerTest.php index d8fb893cf596..ae788ba64f72 100644 --- a/tests/phpunit/CRM/Core/BAO/SchemaHandlerTest.php +++ b/tests/phpunit/CRM/Core/BAO/SchemaHandlerTest.php @@ -14,10 +14,21 @@ * * These tests create and drop indexes on the civicrm_uf_join table. The indexes * being added and dropped we assume will never exist. + * * @group headless */ class CRM_Core_BAO_SchemaHandlerTest extends CiviUnitTestCase { + /** + * Ensure any removed indices are put back. + * + * @throws \CRM_Core_Exception + */ + public function tearDown() { + parent::tearDown(); + $this->callAPISuccess('System', 'updateindexes', []); + } + /** * Test creating an index. * @@ -28,13 +39,13 @@ public function testCreateIndex() { $tables = ['civicrm_uf_join' => ['weight']]; CRM_Core_BAO_SchemaHandler::createIndexes($tables); CRM_Core_BAO_SchemaHandler::createIndexes($tables); - $dao = CRM_Core_DAO::executeQuery("SHOW INDEX FROM civicrm_uf_join"); + $dao = CRM_Core_DAO::executeQuery('SHOW INDEX FROM civicrm_uf_join'); $count = 0; while ($dao->fetch()) { - if ($dao->Column_name == 'weight') { + if ($dao->Column_name === 'weight') { $count++; - CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_uf_join DROP INDEX " . $dao->Key_name); + CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_uf_join DROP INDEX ' . $dao->Key_name); } } $this->assertEquals(1, $count); @@ -60,24 +71,22 @@ public function testCombinedIndex() { $tables = ['civicrm_uf_join' => [['weight', 'module']]]; CRM_Core_BAO_SchemaHandler::createIndexes($tables); - $dao = CRM_Core_DAO::executeQuery("SHOW INDEX FROM civicrm_uf_join"); + $dao = CRM_Core_DAO::executeQuery('SHOW INDEX FROM civicrm_uf_join'); $weightCount = 0; - $combinedCount = 0; $indexes = []; while ($dao->fetch()) { - if ($dao->Column_name == 'weight') { + if ($dao->Column_name === 'weight') { $weightCount++; $indexes[$dao->Key_name] = $dao->Key_name; } - if ($dao->Column_name == 'module') { - $combinedCount++; + if ($dao->Column_name === 'module') { $this->assertArrayHasKey($dao->Key_name, $indexes); } } foreach (array_keys($indexes) as $index) { - CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_uf_join DROP INDEX " . $index); + CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_uf_join DROP INDEX ' . $index); } $this->assertEquals(2, $weightCount); } @@ -132,7 +141,7 @@ public function columnTests() { * @dataProvider columnTests */ public function testCheckIfColumnExists($tableName, $columnName) { - if ($columnName == 'xxxx') { + if ($columnName === 'xxxx') { $this->assertFalse(CRM_Core_BAO_SchemaHandler::checkIfFieldExists($tableName, $columnName)); } else { @@ -154,9 +163,12 @@ public function foreignKeyTests() { * Test to see if we can drop foreign key * * @dataProvider foreignKeyTests + * + * @param string $tableName + * @param string $key */ public function testSafeDropForeignKey($tableName, $key) { - if ($key == 'FK_civicrm_mailing_recipients_id') { + if ($key === 'FK_civicrm_mailing_recipients_id') { $this->assertFalse(CRM_Core_BAO_SchemaHandler::safeRemoveFK('civicrm_mailing_recipients', $key)); } else { @@ -235,6 +247,41 @@ public function testReconcileMissingIndices() { $this->assertEmpty($missingIndices); } + /** + * Check there are no missing indices + * + * @throws \CRM_Core_Exception + */ + public function testGetMissingIndicesWithTableFilter() { + CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_contact DROP INDEX index_sort_name'); + CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_contribution DROP INDEX index_total_amount_receive_date'); + $missingIndices = $this->callAPISuccess('System', 'getmissingindices', [])['values']; + $expected = [ + 'civicrm_contact' => [ + [ + 'name' => 'index_sort_name', + 'field' => ['sort_name'], + 'localizable' => FALSE, + 'sig' => 'civicrm_contact::0::sort_name', + ], + ], + 'civicrm_contribution' => [ + [ + 'name' => 'index_total_amount_receive_date', + 'field' => ['total_amount', 'receive_date'], + 'localizable' => FALSE, + 'sig' => 'civicrm_contribution::0::total_amount::receive_date', + ], + ], + ]; + $this->assertEquals($expected, $missingIndices); + $missingIndices = $this->callAPISuccess('System', 'getmissingindices', ['tables' => ['civicrm_contact']])['values']; + $this->assertEquals(['civicrm_contact' => $expected['civicrm_contact']], $missingIndices); + $this->callAPISuccess('System', 'updateindexes', ['tables' => 'civicrm_contribution']); + $missingIndices = $this->callAPISuccess('System', 'getmissingindices', [])['values']; + $this->assertEquals(['civicrm_contact' => $expected['civicrm_contact']], $missingIndices); + } + /** * Check for partial indices */ @@ -309,7 +356,7 @@ public function testDropColumn() { // drop col1 CRM_Core_DAO::executeQuery(CRM_Core_BAO_SchemaHandler::buildFieldChangeSql($alterParams, FALSE)); - $create_table = CRM_Core_DAO::executeQuery("SHOW CREATE TABLE civicrm_test_drop_column"); + $create_table = CRM_Core_DAO::executeQuery('SHOW CREATE TABLE civicrm_test_drop_column'); while ($create_table->fetch()) { $this->assertNotContains('col1', $create_table->Create_Table); $this->assertContains('col2', $create_table->Create_Table); @@ -319,7 +366,7 @@ public function testDropColumn() { $alterParams['name'] = 'col2'; CRM_Core_DAO::executeQuery(CRM_Core_BAO_SchemaHandler::buildFieldChangeSql($alterParams, FALSE)); - $create_table = CRM_Core_DAO::executeQuery("SHOW CREATE TABLE civicrm_test_drop_column"); + $create_table = CRM_Core_DAO::executeQuery('SHOW CREATE TABLE civicrm_test_drop_column'); while ($create_table->fetch()) { $this->assertNotContains('col2', $create_table->Create_Table); } @@ -336,8 +383,8 @@ public function testBuildFieldChangeSql() { 'type' => 'text', ]; $sql = CRM_Core_BAO_SchemaHandler::buildFieldChangeSql($params, FALSE); - $this->assertEquals("ALTER TABLE big_table - ADD COLUMN `big_bob` text", trim($sql)); + $this->assertEquals('ALTER TABLE big_table + ADD COLUMN `big_bob` text', trim($sql)); $params['operation'] = 'modify'; $params['comment'] = 'super big';