[ticket/17525] Migration to rename actual database indexes if any

Also generate short table names rather than use hardcoded map

PHPBB-17525
This commit is contained in:
rxu 2025-07-01 00:11:36 +07:00
parent 4da8591dd8
commit 58b3e5dee0
No known key found for this signature in database
GPG key ID: 955F0567380E586A
2 changed files with 94 additions and 111 deletions

View file

@ -124,107 +124,51 @@ class table_helper
}
/**
* Maps short table names for the purpose of prefixing tables' index names.
* Maps table names to thair short names for the purpose of prefixing tables' index names.
*
* @param array $additional_tables Additional table names without prefix to add to the map.
* @param string $table_prefix Tables prefix.
* @param array $table_names Table names with prefix to add to the map.
* @param string $table_prefix Tables prefix.
*
* @return array<string, string> Pairs of table names and their short name representations.
* @psalm-return array{string, string}
*/
public static function map_short_table_names(array $additional_tables = [], string $table_prefix = ''): array
public static function map_short_table_names(array $table_names = [], string $table_prefix = ''): array
{
$short_table_names_map = [
"{$table_prefix}acl_groups" => 'aclgrps',
"{$table_prefix}acl_options" => 'aclopts',
"{$table_prefix}acl_roles" => 'aclrls',
"{$table_prefix}acl_roles_data" => 'aclrlsdt',
"{$table_prefix}acl_users" => 'aclusrs',
"{$table_prefix}attachments" => 'atchmnts',
"{$table_prefix}backups" => 'bckps',
"{$table_prefix}bans" => 'bans',
"{$table_prefix}bbcodes" => 'bbcds',
"{$table_prefix}bookmarks" => 'bkmrks',
"{$table_prefix}bots" => 'bots',
"{$table_prefix}captcha_answers" => 'cptchans',
"{$table_prefix}captcha_questions" => 'cptchqs',
"{$table_prefix}config" => 'cnfg',
"{$table_prefix}config_text" => 'cnfgtxt',
"{$table_prefix}confirm" => 'cnfrm',
"{$table_prefix}disallow" => 'dslw',
"{$table_prefix}drafts" => 'drfts',
"{$table_prefix}ext" => 'ext',
"{$table_prefix}extension_groups" => 'extgrps',
"{$table_prefix}extensions" => 'exts',
"{$table_prefix}forums" => 'frms',
"{$table_prefix}forums_access" => 'frmsacs',
"{$table_prefix}forums_track" => 'frmstrck',
"{$table_prefix}forums_watch" => 'frmswtch',
"{$table_prefix}groups" => 'grps',
"{$table_prefix}icons" => 'icns',
"{$table_prefix}lang" => 'lang',
"{$table_prefix}log" => 'log',
"{$table_prefix}login_attempts" => 'lgnatmpts',
"{$table_prefix}migrations" => 'mgrtns',
"{$table_prefix}moderator_cache" => 'mdrtche',
"{$table_prefix}modules" => 'mdls',
"{$table_prefix}notification_emails"=> 'ntfemls',
"{$table_prefix}notification_push" => 'ntfpsh',
"{$table_prefix}notification_types" => 'ntftps',
"{$table_prefix}notifications" => 'nftcns',
"{$table_prefix}oauth_accounts" => 'oauthacnts',
"{$table_prefix}oauth_states" => 'oauthsts',
"{$table_prefix}oauth_tokens" => 'oauthtkns',
"{$table_prefix}poll_options" => 'pllopts',
"{$table_prefix}poll_votes" => 'pllvts',
"{$table_prefix}posts" => 'psts',
"{$table_prefix}privmsgs" => 'pms',
"{$table_prefix}privmsgs_folder" => 'pmsfldr',
"{$table_prefix}privmsgs_rules" => 'pmsrls',
"{$table_prefix}privmsgs_to" => 'pmsto',
"{$table_prefix}profile_fields" => 'prflds',
"{$table_prefix}profile_fields_data"=> 'prfldt',
"{$table_prefix}profile_fields_lang"=> 'prfldlng',
"{$table_prefix}profile_lang" => 'prflng',
"{$table_prefix}push_subscriptions" => 'pshsbscrs',
"{$table_prefix}qa_confirm" => 'qacnfm',
"{$table_prefix}ranks" => 'rnks',
"{$table_prefix}reports" => 'rprts',
"{$table_prefix}reports_reasons" => 'rprtrsns',
"{$table_prefix}search_results" => 'srchrslts',
"{$table_prefix}search_wordlist" => 'wrdlst',
"{$table_prefix}search_wordmatch" => 'wrdmtch',
"{$table_prefix}sessions" => 'ssns',
"{$table_prefix}sessions_keys" => 'ssnkeys',
"{$table_prefix}sitelist" => 'sitelst',
"{$table_prefix}smilies" => 'smls',
"{$table_prefix}sphinx" => 'sphnx',
"{$table_prefix}storage" => 'strg',
"{$table_prefix}styles" => 'stls',
"{$table_prefix}teampage" => 'teampg',
"{$table_prefix}topics" => 'tpcs',
"{$table_prefix}topics_posted" => 'tpcspstd',
"{$table_prefix}topics_track" => 'tpcstrck',
"{$table_prefix}topics_watch" => 'tpkswtch',
"{$table_prefix}user_group" => 'usrgrp',
"{$table_prefix}user_notifications" => 'usrntfs',
"{$table_prefix}users" => 'usrs',
"{$table_prefix}warnings" => 'wrns',
"{$table_prefix}words" => 'wrds',
"{$table_prefix}zebra" => 'zbra',
];
// Add table prefix to additional tables
if (!empty($table_prefix) && !empty($additional_tables))
$short_table_names_map = [];
foreach ($table_names as $table_name)
{
foreach ($additional_tables as $key => $value)
{
$additional_tables["{$table_prefix}{$key}"] = $value;
unset($additional_tables[$key]);
}
$short_table_names_map[$table_name] = self::generate_shortname(str_replace($table_prefix, '', $table_name));
}
return array_merge($short_table_names_map, $additional_tables);
return $short_table_names_map;
}
/**
* Generates short table names for the purpose of prefixing tables' index names.
*
* @param string $table_name Table name with prefix to generate its short name.
*
* @return string Short table name.
*/
public static function generate_shortname(string $table_name = ''): string
{
// Only shorten actually long names
if (strlen($table_name) > 4)
{
// Remove vowels
$table_name = preg_replace('/([^aeiou_])([aeiou]+)/i', '$1', $table_name);
// Remove underscores
$table_name = str_replace('_', '', $table_name);
// Remove repeated consonants and their combinations (like 'ss', 'flfl' and similar)
$table_name = preg_replace('/(.+)\\1+/i', '$1', $table_name);
// Restrict short name length to 10 chars
$table_name = substr($table_name, 0, 10);
}
return $table_name;
}
/**

View file

@ -13,10 +13,10 @@
namespace phpbb\db\migration\data\v400;
use phpbb\db\migration\migration;
use phpbb\db\migration\container_aware_migration;
use phpbb\db\doctrine\table_helper;
class rename_duplicated_index_names extends migration
class rename_duplicated_index_names extends container_aware_migration
{
public static function depends_on()
{
@ -27,27 +27,25 @@ class rename_duplicated_index_names extends migration
public function update_schema()
{
$rename_index = $table_keys = [];
$db_table_schema = $this->get_schema();
foreach ($db_table_schema as $table_name => $table_data)
{
if (isset($table_data['KEYS']))
{
foreach ($table_data['KEYS'] as $key_name => $key_data)
{
$table_keys[$table_name][] = $key_name;
}
}
}
$rename_index = [];
$is_prefixed_index = false;
$tables_index_names = $this->get_tables_index_names();
$short_table_names = table_helper::map_short_table_names(array_keys($tables_index_names), $this->table_prefix);
$short_table_names = table_helper::map_short_table_names([], $this->table_prefix);
foreach ($table_keys as $table_name => $key_names)
foreach ($tables_index_names as $table_name => $key_names)
{
foreach ($key_names as $key_name)
{
$key_name_new = $short_table_names[$table_name] . '_' . $key_name;
$rename_index[$table_name][$key_name] = $key_name_new;
$rename_index[$table_name][$table_name . '_' . $key_name] = $key_name_new;
$prefixless_table_name = strpos($table_name, $this->table_prefix) === 0 ? substr($table_name, strlen($this->table_prefix)) : $table_name;
// Check if there's at least one index name is prefixed, otherwise we operate on generated database schema
$is_prefixed_index = $is_prefixed_index || (strpos($key_name, $table_name) === 0);
// If key name is prefixed by its table name (with or without tables prefix), remove that key name prefix.
$cleaned_key_name = !$is_prefixed_index ? $key_name : str_replace(strpos($key_name, $table_name) === 0 ? $table_name . '_' : $prefixless_table_name . '_', '', $key_name);
$key_name_new = $short_table_names[$table_name] . '_' . $cleaned_key_name;
$rename_index[$table_name][$key_name !== $cleaned_key_name ? $key_name : $cleaned_key_name] = $key_name_new;
}
}
@ -88,4 +86,45 @@ class rename_duplicated_index_names extends migration
return $schema_generator->get_schema();
}
public function get_tables_index_names()
{
$table_keys = [];
$doctrine = $this->container->get('dbal.conn.doctrine');
$schema_manager = $doctrine->createSchemaManager();
$table_names = $schema_manager->listTableNames();
if (!empty($table_names))
{
foreach ($table_names as $table_name)
{
$indices = $schema_manager->listTableIndexes($table_name);
$index_names = array_keys(
array_filter($indices, function (\Doctrine\DBAL\Schema\Index $index)
{
return !$index->isPrimary();
})
);
if (!empty($index_names))
{
$table_keys[$table_name] = $index_names;
}
}
}
else
{
$db_table_schema = $this->get_schema();
foreach ($db_table_schema as $table_name => $table_data)
{
if (isset($table_data['KEYS']))
{
$table_keys[$table_name] = array_keys($table_data['KEYS']);
}
}
}
return $table_keys;
}
}