3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * This file contains functions for creating and altering CiviCRM-tables structure.
22 * 'name' => TABLE_NAME,
23 * 'attributes' => ATTRIBUTES,
26 * 'name' => FIELD_NAME,
27 * // can be field, index, constraint
28 * 'type' => FIELD_SQL_TYPE,
29 * 'class' => FIELD_CLASS_TYPE,
30 * 'primary' => BOOLEAN,
31 * 'required' => BOOLEAN,
32 * 'searchable' => TRUE,
33 * 'fk_table_name' => FOREIGN_KEY_TABLE_NAME,
34 * 'fk_field_name' => FOREIGN_KEY_FIELD_NAME,
35 * 'comment' => COMMENT,
36 * 'default' => DEFAULT, )
40 class CRM_Core_BAO_SchemaHandler
{
43 * Create a CiviCRM-table
45 * @param array $params
48 * TRUE if successfully created, FALSE otherwise
51 public static function createTable($params) {
52 $sql = self
::buildTableSQL($params);
53 // do not i18n-rewrite
54 CRM_Core_DAO
::executeQuery($sql, [], TRUE, NULL, FALSE, FALSE);
56 if (CRM_Core_Config
::singleton()->logging
) {
58 $logging = new CRM_Logging_Schema();
59 $logging->fixSchemaDifferencesFor($params['name']);
62 // always do a trigger rebuild for this table
63 Civi
::service('sql_triggers')->rebuild($params['name'], TRUE);
69 * @param array $params
73 public static function buildTableSQL($params) {
74 $sql = "CREATE TABLE {$params['name']} (";
75 if (isset($params['fields']) &&
76 is_array($params['fields'])
80 foreach ($params['fields'] as $field) {
81 $sql .= self
::buildFieldSQL($field, $separator, $prefix);
84 foreach ($params['fields'] as $field) {
85 $sql .= self
::buildPrimaryKeySQL($field, $separator, $prefix);
87 foreach ($params['fields'] as $field) {
88 $sql .= self
::buildSearchIndexSQL($field, $separator, $prefix);
90 if (isset($params['indexes'])) {
91 foreach ($params['indexes'] as $index) {
92 $sql .= self
::buildIndexSQL($index, $separator, $prefix);
95 foreach ($params['fields'] as $field) {
96 $sql .= self
::buildForeignKeySQL($field, $separator, $prefix, $params['name']);
99 $sql .= "\n) {$params['attributes']};";
104 * @param array $params
110 public static function buildFieldSQL($params, $separator, $prefix) {
113 $sql .= str_repeat(' ', 8);
115 $sql .= "`{$params['name']}` {$params['type']}";
117 if (!empty($params['required'])) {
121 if (!empty($params['attributes'])) {
122 $sql .= " {$params['attributes']}";
125 if (!empty($params['default']) &&
126 $params['type'] != 'text'
128 $sql .= " DEFAULT {$params['default']}";
131 if (!empty($params['comment'])) {
132 $sql .= " COMMENT '{$params['comment']}'";
139 * @param array $params
143 * @return NULL|string
145 public static function buildPrimaryKeySQL($params, $separator, $prefix) {
147 if (!empty($params['primary'])) {
149 $sql .= str_repeat(' ', 8);
151 $sql .= "PRIMARY KEY ( {$params['name']} )";
157 * @param array $params
160 * @param bool $indexExist
162 * @return NULL|string
164 public static function buildSearchIndexSQL($params, $separator, $prefix, $indexExist = FALSE) {
168 if ($params['type'] == 'text') {
172 //create index only for searchable fields during ADD,
173 //create index only if field is become searchable during MODIFY,
174 //drop index only if field is no longer searchable and it does not reference
175 //a forgein key (and indexExist is true)
176 if (!empty($params['searchable']) && !$indexExist) {
178 $sql .= str_repeat(' ', 8);
180 $sql .= "INDEX_{$params['name']} ( {$params['name']} )";
182 elseif (empty($params['searchable']) && empty($params['fk_table_name']) && $indexExist) {
184 $sql .= str_repeat(' ', 8);
185 $sql .= "DROP INDEX INDEX_{$params['name']}";
191 * @param array $params
197 public static function buildIndexSQL(&$params, $separator, $prefix) {
200 $sql .= str_repeat(' ', 8);
201 if ($params['unique']) {
202 $sql .= 'UNIQUE INDEX';
203 $indexName = 'unique';
207 $indexName = 'index';
211 foreach ($params as $name => $value) {
212 if (substr($name, 0, 11) == 'field_name_') {
213 $indexName .= "_{$value}";
214 $indexFields .= " $value,";
217 $indexFields = substr($indexFields, 0, -1);
219 $sql .= " $indexName ( $indexFields )";
224 * @param string $tableName
225 * @param string $fkTableName
229 public static function changeFKConstraint($tableName, $fkTableName) {
230 $fkName = "{$tableName}_entity_id";
231 if (strlen($fkName) >= 48) {
232 $fkName = substr($fkName, 0, 32) . "_" . substr(md5($fkName), 0, 16);
235 ALTER TABLE {$tableName}
236 DROP FOREIGN KEY `FK_{$fkName}`;";
238 CRM_Core_DAO
::executeQuery($dropFKSql);
241 ALTER TABLE {$tableName}
242 ADD CONSTRAINT `FK_{$fkName}` FOREIGN KEY (`entity_id`) REFERENCES {$fkTableName} (`id`) ON DELETE CASCADE;";
243 // CRM-7007: do not i18n-rewrite this query
244 CRM_Core_DAO
::executeQuery($addFKSql, [], TRUE, NULL, FALSE, FALSE);
250 * @param array $params
253 * @param string $tableName
255 * @return NULL|string
257 public static function buildForeignKeySQL($params, $separator, $prefix, $tableName) {
259 if (!empty($params['fk_table_name']) && !empty($params['fk_field_name'])) {
261 $sql .= str_repeat(' ', 8);
263 $fkName = "{$tableName}_{$params['name']}";
264 if (strlen($fkName) >= 48) {
265 $fkName = substr($fkName, 0, 32) . "_" . substr(md5($fkName), 0, 16);
268 $sql .= "CONSTRAINT FK_$fkName FOREIGN KEY ( `{$params['name']}` ) REFERENCES {$params['fk_table_name']} ( {$params['fk_field_name']} ) ";
269 $sql .= CRM_Utils_Array
::value('fk_attributes', $params);
277 * @param array $params
278 * @param bool $indexExist
279 * @param bool $triggerRebuild
283 public static function alterFieldSQL($params, $indexExist = FALSE, $triggerRebuild = TRUE) {
284 CRM_Core_Error
::deprecatedFunctionWarning('function no longer in use / supported');
285 // lets suppress the required flag, since that can cause sql issue
286 $params['required'] = FALSE;
288 $sql = self
::buildFieldChangeSql($params, $indexExist);
290 // CRM-7007: do not i18n-rewrite this query
291 CRM_Core_DAO
::executeQuery($sql, [], TRUE, NULL, FALSE, FALSE);
293 $config = CRM_Core_Config
::singleton();
294 if ($config->logging
) {
295 // CRM-16717 not sure why this was originally limited to add.
296 // For example custom tables can have field length changes - which need to flow through to logging.
297 // Are there any modifies we DON'T was to call this function for (& shouldn't it be clever enough to cope?)
298 if ($params['operation'] == 'add' ||
$params['operation'] == 'modify') {
299 $logging = new CRM_Logging_Schema();
300 $logging->fixSchemaDifferencesFor($params['table_name'], [trim(strtoupper($params['operation'])) => [$params['name']]]);
304 if ($triggerRebuild) {
305 Civi
::service('sql_triggers')->rebuild($params['table_name'], TRUE);
312 * Delete a CiviCRM-table.
314 * @param string $tableName
315 * Name of the table to be created.
317 public static function dropTable($tableName) {
318 $sql = "DROP TABLE $tableName";
319 CRM_Core_DAO
::executeQuery($sql);
323 * @param string $tableName
324 * @param string $columnName
326 * @param bool $isUpgradeMode
329 public static function dropColumn($tableName, $columnName, $l18n = FALSE, $isUpgradeMode = FALSE) {
330 if (self
::checkIfFieldExists($tableName, $columnName)) {
331 $sql = "ALTER TABLE $tableName DROP COLUMN $columnName";
333 CRM_Core_DAO
::executeQuery($sql);
336 CRM_Core_DAO
::executeQuery($sql, [], TRUE, NULL, FALSE, FALSE);
338 $domain = new CRM_Core_DAO_Domain();
340 if ($domain->locales
) {
341 $locales = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $domain->locales
);
342 CRM_Core_I18n_Schema
::rebuildMultilingualSchema($locales, NULL, $isUpgradeMode);
348 * @param string $tableName
349 * @param bool $dropUnique
351 public static function changeUniqueToIndex($tableName, $dropUnique = TRUE) {
353 $sql = "ALTER TABLE $tableName
354 DROP INDEX `unique_entity_id` ,
355 ADD INDEX `FK_{$tableName}_entity_id` ( `entity_id` )";
358 $sql = " ALTER TABLE $tableName
359 DROP INDEX `FK_{$tableName}_entity_id` ,
360 ADD UNIQUE INDEX `unique_entity_id` ( `entity_id` )";
362 CRM_Core_DAO
::executeQuery($sql);
369 * Tables to create index for in the format:
370 * array('civicrm_entity_table' => 'entity_id')
372 * array('civicrm_entity_table' => array('entity_id', 'entity_table'))
373 * The latter will create a combined index on the 2 keys (in order).
375 * Side note - when creating combined indexes the one with the most variation
376 * goes first - so entity_table always goes after entity_id.
378 * It probably makes sense to consider more sophisticated options at some point
379 * but at the moment this is only being as enhanced as fast as the test is.
381 * @todo add support for length & multilingual on combined keys.
383 * @param string $createIndexPrefix
384 * @param array $substrLengths
386 public static function createIndexes($tables, $createIndexPrefix = 'index', $substrLengths = []) {
388 $domain = new CRM_Core_DAO_Domain();
390 $locales = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $domain->locales
);
392 // if we're multilingual, cache the information on internationalised fields
393 static $columns = NULL;
394 if (!CRM_Utils_System
::isNull($locales) and $columns === NULL) {
395 $columns = CRM_Core_I18n_SchemaStructure
::columns();
398 foreach ($tables as $table => $fields) {
399 $query = "SHOW INDEX FROM $table";
400 $dao = CRM_Core_DAO
::executeQuery($query);
402 $currentIndexes = [];
403 while ($dao->fetch()) {
404 $currentIndexes[] = $dao->Key_name
;
407 // now check for all fields if the index exists
408 foreach ($fields as $field) {
409 $fieldName = implode('_', (array) $field);
411 if (is_array($field)) {
412 // No support for these for combined indexes as yet - add a test when you
418 // handle indices over substrings, CRM-6245
419 // $lengthName is appended to index name, $lengthSize is the field size modifier
420 $lengthName = isset($substrLengths[$table][$fieldName]) ?
"_{$substrLengths[$table][$fieldName]}" : '';
421 $lengthSize = isset($substrLengths[$table][$fieldName]) ?
"({$substrLengths[$table][$fieldName]})" : '';
425 "index_{$fieldName}{$lengthName}",
426 "FK_{$table}_{$fieldName}{$lengthName}",
427 "UI_{$fieldName}{$lengthName}",
428 "{$createIndexPrefix}_{$fieldName}{$lengthName}",
431 // skip to the next $field if one of the above $names exists; handle multilingual for CRM-4126
432 foreach ($names as $name) {
433 $regex = '/^' . preg_quote($name) . '(_[a-z][a-z]_[A-Z][A-Z])?$/';
434 if (preg_grep($regex, $currentIndexes)) {
439 // the index doesn't exist, so create it
440 // if we're multilingual and the field is internationalised, do it for every locale
441 // @todo remove is_array check & add multilingual support for combined indexes and add a test.
442 // Note combined indexes currently using this function are on fields like
443 // entity_id + entity_table which are not multilingual.
444 if (!is_array($field) && !CRM_Utils_System
::isNull($locales) and isset($columns[$table][$fieldName])) {
445 foreach ($locales as $locale) {
446 $queries[] = "CREATE INDEX {$createIndexPrefix}_{$fieldName}{$lengthName}_{$locale} ON {$table} ({$fieldName}_{$locale}{$lengthSize})";
450 $queries[] = "CREATE INDEX {$createIndexPrefix}_{$fieldName}{$lengthName} ON {$table} (" . implode(',', (array) $field) . "{$lengthSize})";
455 // run the queries without i18n-rewriting
456 $dao = new CRM_Core_DAO();
457 foreach ($queries as $query) {
458 $dao->query($query, FALSE);
463 * Get indexes for tables
464 * @param array $tables
465 * array of table names to find indexes for
467 * @return array('tableName' => array('index1', 'index2'))
469 public static function getIndexes($tables) {
471 foreach ($tables as $table) {
472 $query = "SHOW INDEX FROM $table";
473 $dao = CRM_Core_DAO
::executeQuery($query);
476 while ($dao->fetch()) {
477 $tableIndexes[$dao->Key_name
]['name'] = $dao->Key_name
;
478 $tableIndexes[$dao->Key_name
]['field'][] = $dao->Column_name
.
479 ($dao->Sub_part ?
'(' . $dao->Sub_part
. ')' : '');
480 $tableIndexes[$dao->Key_name
]['unique'] = ($dao->Non_unique
== 0 ?
1 : 0);
482 $indexes[$table] = $tableIndexes;
488 * Drop an index if one by that name exists.
490 * @param string $tableName
491 * @param string $indexName
493 public static function dropIndexIfExists($tableName, $indexName) {
494 if (self
::checkIfIndexExists($tableName, $indexName)) {
495 CRM_Core_DAO
::executeQuery("DROP INDEX $indexName ON $tableName");
500 * @param int $customFieldID
501 * @param string $tableName
502 * @param string $columnName
507 public static function alterFieldLength($customFieldID, $tableName, $columnName, $length) {
508 // first update the custom field tables
510 UPDATE civicrm_custom_field
515 1 => [$length, 'Integer'],
516 2 => [$customFieldID, 'Integer'],
518 CRM_Core_DAO
::executeQuery($sql, $params);
521 SELECT is_required, default_value
522 FROM civicrm_custom_field
525 $dao = CRM_Core_DAO
::executeQuery($sql, $params);
530 if ($dao->is_required
) {
531 $clause = " NOT NULL";
534 if (!empty($dao->default_value
)) {
535 $clause .= " DEFAULT '{$dao->default_value}'";
537 // now modify the column
539 ALTER TABLE {$tableName}
540 MODIFY {$columnName} varchar( $length )
543 CRM_Core_DAO
::executeQuery($sql);
546 CRM_Core_Error
::fatal(ts('Could Not Find Custom Field Details for %1, %2, %3',
557 * Check if the table has an index matching the name.
559 * @param string $tableName
560 * @param string $indexName
564 public static function checkIfIndexExists($tableName, $indexName) {
565 $result = CRM_Core_DAO
::executeQuery(
566 "SHOW INDEX FROM $tableName WHERE key_name = %1 AND seq_in_index = 1",
567 [1 => [$indexName, 'String']]
569 if ($result->fetch()) {
576 * Check if the table has a specified column.
578 * @param string $tableName
579 * @param string $columnName
580 * @param bool $i18nRewrite
581 * Whether to rewrite the query on multilingual setups.
585 public static function checkIfFieldExists($tableName, $columnName, $i18nRewrite = TRUE) {
586 $query = "SHOW COLUMNS FROM $tableName LIKE '%1'";
587 $dao = CRM_Core_DAO
::executeQuery($query, [1 => [$columnName, 'Alphanumeric']], TRUE, NULL, FALSE, $i18nRewrite);
588 $result = $dao->fetch() ?
TRUE : FALSE;
593 * Check if a foreign key Exists
594 * @param string $table_name
595 * @param string $constraint_name
596 * @return bool TRUE if FK is found
598 public static function checkFKExists($table_name, $constraint_name) {
599 $config = CRM_Core_Config
::singleton();
600 $dbUf = DB
::parseDSN($config->dsn
);
602 SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
603 WHERE TABLE_SCHEMA = %1
605 AND CONSTRAINT_NAME = %3
606 AND CONSTRAINT_TYPE = 'FOREIGN KEY'
609 1 => [$dbUf['database'], 'String'],
610 2 => [$table_name, 'String'],
611 3 => [$constraint_name, 'String'],
613 $dao = CRM_Core_DAO
::executeQuery($query, $params, TRUE, NULL, FALSE, FALSE);
622 * Remove a foreign key from a table if it exists.
625 * @param $constraint_name
629 public static function safeRemoveFK($table_name, $constraint_name) {
630 if (self
::checkFKExists($table_name, $constraint_name)) {
631 CRM_Core_DAO
::executeQuery("ALTER TABLE {$table_name} DROP FOREIGN KEY {$constraint_name}", [], TRUE, NULL, FALSE, FALSE);
638 * Add index signature hash to DAO file calculation.
640 * @param string $table table name
641 * @param array $indices index array spec
643 public static function addIndexSignature($table, &$indices) {
644 foreach ($indices as $indexName => $index) {
645 $indices[$indexName]['sig'] = $table . "::" .
646 (array_key_exists('unique', $index) ?
$index['unique'] : 0) . "::" .
647 implode("::", $index['field']);
652 * Compare the indices specified in the XML files with those in the DB.
654 * @param bool $dropFalseIndices
655 * If set - this function deletes false indices present in the DB which mismatches the expected
656 * values of xml file so that civi re-creates them with correct values using createMissingIndices() function.
657 * @param array|FALSE $tables
658 * An optional array of tables - if provided the results will be restricted to these tables.
661 * index specifications
663 public static function getMissingIndices($dropFalseIndices = FALSE, $tables = FALSE) {
664 $requiredSigs = $existingSigs = [];
665 // Get the indices defined (originally) in the xml files
666 $requiredIndices = CRM_Core_DAO_AllCoreTables
::indices();
668 if ($tables !== FALSE) {
669 $requiredIndices = array_intersect_key($requiredIndices, array_fill_keys($tables, TRUE));
671 foreach ($requiredIndices as $table => $indices) {
672 $reqSigs[] = CRM_Utils_Array
::collect('sig', $indices);
674 CRM_Utils_Array
::flatten($reqSigs, $requiredSigs);
676 // Get the indices in the database
677 $existingIndices = CRM_Core_BAO_SchemaHandler
::getIndexes(array_keys($requiredIndices));
679 foreach ($existingIndices as $table => $indices) {
680 CRM_Core_BAO_SchemaHandler
::addIndexSignature($table, $indices);
681 $extSigs[] = CRM_Utils_Array
::collect('sig', $indices);
683 CRM_Utils_Array
::flatten($extSigs, $existingSigs);
686 $missingSigs = array_diff($requiredSigs, $existingSigs);
688 //CRM-20774 - Drop index key which exist in db but the value varies.
689 $existingKeySigs = array_intersect_key($missingSigs, $existingSigs);
690 if ($dropFalseIndices && !empty($existingKeySigs)) {
691 foreach ($existingKeySigs as $sig) {
692 $sigParts = explode('::', $sig);
693 foreach ($requiredIndices[$sigParts[0]] as $index) {
694 if ($index['sig'] == $sig && !empty($index['name'])) {
695 self
::dropIndexIfExists($sigParts[0], $index['name']);
702 // Get missing indices
703 $missingIndices = [];
704 foreach ($missingSigs as $sig) {
705 $sigParts = explode('::', $sig);
706 if (array_key_exists($sigParts[0], $requiredIndices)) {
707 foreach ($requiredIndices[$sigParts[0]] as $index) {
708 if ($index['sig'] == $sig) {
709 $missingIndices[$sigParts[0]][] = $index;
715 return $missingIndices;
719 * Create missing indices.
721 * @param array $missingIndices as returned by getMissingIndices()
723 public static function createMissingIndices($missingIndices) {
725 foreach ($missingIndices as $table => $indexList) {
726 foreach ($indexList as $index) {
727 $queries[] = "CREATE " .
728 (array_key_exists('unique', $index) && $index['unique'] ?
'UNIQUE ' : '') .
729 "INDEX {$index['name']} ON {$table} (" .
730 implode(", ", $index['field']) .
735 /* FIXME potential problem if index name already exists, so check before creating */
736 $dao = new CRM_Core_DAO();
737 foreach ($queries as $query) {
738 $dao->query($query, FALSE);
743 * Build the sql to alter the field.
745 * @param array $params
746 * @param bool $indexExist
750 public static function buildFieldChangeSql($params, $indexExist) {
751 $sql = str_repeat(' ', 8);
752 $sql .= "ALTER TABLE {$params['table_name']}";
753 return $sql . self
::getFieldAlterSQL($params, $indexExist);
757 * Get the sql to alter an individual field.
759 * This will need to have an ALTER TABLE statement appended but by getting
760 * by individual field we can do one or many.
762 * @param array $params
763 * @param bool $indexExist
767 public static function getFieldAlterSQL($params, $indexExist) {
769 switch ($params['operation']) {
772 $sql .= self
::buildFieldSQL($params, $separator, "ADD COLUMN ");
774 $sql .= self
::buildPrimaryKeySQL($params, $separator, "ADD PRIMARY KEY ");
775 $sql .= self
::buildSearchIndexSQL($params, $separator, "ADD INDEX ");
776 $sql .= self
::buildForeignKeySQL($params, $separator, "ADD ", $params['table_name']);
781 $sql .= self
::buildFieldSQL($params, $separator, "MODIFY ");
783 $sql .= self
::buildSearchIndexSQL($params, $separator, "ADD INDEX ", $indexExist);
787 $sql .= " DROP COLUMN `{$params['name']}`";
788 if (!empty($params['primary'])) {
789 $sql .= ", DROP PRIMARY KEY";
791 if (!empty($params['fk_table_name'])) {
792 $sql .= ", DROP FOREIGN KEY FK_{$params['fkName']}";
800 * Performs the utf8mb4 migration.
802 * @param bool $revert
803 * Being able to revert if primarily for unit testing.
807 public static function migrateUtf8mb4($revert = FALSE) {
808 $newCharSet = $revert ?
'utf8' : 'utf8mb4';
809 $newCollation = $revert ?
'utf8_unicode_ci' : 'utf8mb4_unicode_ci';
810 $newBinaryCollation = $revert ?
'utf8_bin' : 'utf8mb4_bin';
812 $dao = new CRM_Core_DAO();
813 $database = $dao->_database
;
814 CRM_Core_DAO
::executeQuery("ALTER DATABASE $database CHARACTER SET = $newCharSet COLLATE = $newCollation");
815 $dao = CRM_Core_DAO
::executeQuery("SHOW TABLE STATUS WHERE Engine = 'InnoDB' AND Name LIKE 'civicrm\_%'");
816 while ($dao->fetch()) {
817 $tables[$dao->Name
] = [
818 'Engine' => $dao->Engine
,
821 $dsn = defined('CIVICRM_LOGGING_DSN') ? DB
::parseDSN(CIVICRM_LOGGING_DSN
) : DB
::parseDSN(CIVICRM_DSN
);
822 $logging_database = $dsn['database'];
823 $dao = CRM_Core_DAO
::executeQuery("SHOW TABLE STATUS FROM `$logging_database` WHERE Engine <> 'MyISAM' AND Name LIKE 'log\_civicrm\_%'");
824 while ($dao->fetch()) {
825 $tables["$logging_database.{$dao->Name}"] = [
826 'Engine' => $dao->Engine
,
829 foreach ($tables as $table => $param) {
830 $query = "ALTER TABLE $table";
831 $dao = CRM_Core_DAO
::executeQuery("SHOW FULL COLUMNS FROM $table", [], TRUE, NULL, FALSE, FALSE);
834 $tableCollation = $newCollation;
835 while ($dao->fetch()) {
836 if (!$dao->Collation ||
$dao->Collation
=== $newCollation ||
$dao->Collation
=== $newBinaryCollation) {
839 if (strpos($dao->Collation
, 'utf8') !== 0) {
843 if (strpos($dao->Collation
, '_bin') !== FALSE) {
844 $tableCollation = $newBinaryCollation;
847 $tableCollation = $newCollation;
849 if ($dao->Null === 'YES') {
856 if ($dao->Default !== NULL) {
858 $default = "DEFAULT %$index";
859 $params[$index] = [$dao->Default, 'String'];
861 elseif ($dao->Null === 'YES') {
862 $default = 'DEFAULT NULL';
865 $params[$index] = [$dao->Comment
, 'String'];
866 $query .= " MODIFY `{$dao->Field}` {$dao->Type} CHARACTER SET $newCharSet COLLATE $tableCollation $null $default {$dao->Extra} COMMENT %$index,";
868 $query .= " CHARACTER SET = $newCharSet COLLATE = $tableCollation";
869 if ($param['Engine'] === 'InnoDB') {
870 $query .= ' ROW_FORMAT = Dynamic';
872 // Disable i18n rewrite.
873 CRM_Core_DAO
::executeQuery($query, $params, TRUE, NULL, FALSE, FALSE);
879 * Get the database collation.
883 public static function getDBCollation() {
884 return CRM_Core_DAO
::singleValueQuery('SELECT @@collation_database');
888 * Get the database collation.
892 public static function getDBCharset() {
893 return CRM_Core_DAO
::singleValueQuery('SELECT @@character_set_database');