Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
2bf5ae7
Fixes some calls to JavaScript function doAutoSubmit()
Sesquipedalian Feb 26, 2026
ec7afb0
More reliable way to get theme dir in Maintenance::__construct()
Sesquipedalian Feb 26, 2026
21191a0
Don't mess with redirects or canonical URLs during install or upgrade
Sesquipedalian Mar 1, 2026
6f6fcac
Removes unnecessary param for SMF\Db\Schema\Table::getInitializers()
Sesquipedalian Mar 4, 2026
c535f9e
Removes old comments when rebuilding Settings.php during upgrade
Sesquipedalian Mar 4, 2026
002a872
Fixes type error when setting Config::$db_port in upgrader
Sesquipedalian Mar 5, 2026
6c880ef
Fixes some type casting inside Config::set()
Sesquipedalian Mar 6, 2026
60d17ac
Loads minimal user data for User::$me in upgrader
Sesquipedalian Mar 8, 2026
e224d83
Only show box for detailed info about migration steps when requested
Sesquipedalian Mar 9, 2026
fb77f07
Uses JavaScript to update "time elapsed" counter in maintenance tools
Sesquipedalian Mar 9, 2026
f28b565
Fixes bizarre embedded template in critical error message
Sesquipedalian Mar 13, 2026
c451bb5
Handles generated columns correctly in Db\APIs\MySQL::backup_table()
Sesquipedalian Mar 19, 2026
f8c09f2
Maintenance\Cleanup\v3_0\OldFiles → Maintenance\Cleanup\OldFilesBase
Sesquipedalian Mar 20, 2026
31235c4
Upgrades board descriptions after upgrading smileys
Sesquipedalian Mar 21, 2026
c006611
Uses correct table names in PostgreSqlSequences migration step
Sesquipedalian Mar 29, 2026
5de83d4
Explicit null default for expire_time in Db\Schema\v2_1\BanGroups
Sesquipedalian Mar 29, 2026
4acc5bd
Fixes column name in SMF\Maintenance\Migration\v2_1\UserDrafts
Sesquipedalian Apr 17, 2026
b5296eb
Always uses MigrationBase::query() for migration db queries
Sesquipedalian Apr 21, 2026
5cecc0d
Prevents error when no members have PM labels
Sesquipedalian Apr 21, 2026
d17603b
Implements SMF\Db\Schema\Table::exists()
Sesquipedalian Apr 21, 2026
f5fdfc0
Always force a fresh download of maintenance tool JavaScript and CSS
Sesquipedalian Apr 22, 2026
836597b
Fixes broken loop in PostgreSqlSequences migration step
Sesquipedalian Apr 22, 2026
f04570e
Drop default for expire_time in ban_groups table before ALTER TABLE
Sesquipedalian Apr 22, 2026
96cc58d
Fixes misbegotten query in SMF\Maintenance\Migration\v2_1\Ipv6BanItem
Sesquipedalian Apr 24, 2026
639acd7
Rewrites SMF\Maintenance\Migration\v2_1\FixDates
Sesquipedalian Apr 24, 2026
b5db308
Uses consistent delimiter in getNextSubstep() url
Sesquipedalian Apr 24, 2026
0a0d22e
Use standard SHOW COLUMNS command in SMF\Db\APIs\MySQL::list_columns()
Sesquipedalian Apr 24, 2026
8291a00
Improves reporting of results of ALTER TABLE etc. in db APIs
Sesquipedalian Apr 30, 2026
53a8a65
Uses property hook to set SMF\Db\Schema\Table::$default_charset
Sesquipedalian May 14, 2026
a890800
Rewrites upgrader substeps for adding IPv6 support in 2.1
Sesquipedalian May 12, 2026
bf23cd9
Ensure db is loaded in ToolsBase::deleteOldSchemaAndMaintenanceFiles()
Sesquipedalian May 30, 2026
58778c8
Use 'localhost' as default database server during install
Sesquipedalian May 30, 2026
bc9d9ed
Fixes call to Db:list_columns() in Db::getTypeIndicators()
Sesquipedalian May 30, 2026
d4c3d79
Improves handling of `{literal:...}` in Db::quote()
Sesquipedalian May 30, 2026
1fb5089
Fixes query in SMF\Maintenance\Migration\v2_1\BoardDescriptions
Sesquipedalian Jun 9, 2026
5839e36
Fixes undefined array key error in SMF\Maintenance\Utf8ConverterStep
Sesquipedalian Jun 9, 2026
e6f7272
Adds missing cast in SMF\Db\APIs\PostgreSQL::replacement__callback()
Sesquipedalian Jun 9, 2026
9c07200
Revert to using UNHEX(HEX(...)) in Migration\v2_1\Ipv6BanItem query
Sesquipedalian Jun 20, 2026
4943c93
Handles empty IP columns in Ipv6Converter::convertStringColumnToInet()
Sesquipedalian Jun 20, 2026
aca9c74
If installer couldn't create the database, tries again with the prefix
Sesquipedalian Jun 27, 2026
223172a
Programmatically sets form element ID in maintenance tool templates
Sesquipedalian Jun 26, 2026
bae2aec
Handles timeouts more gracefully in maintenance tools
Sesquipedalian Jun 26, 2026
d0e3ab1
Sets explicit default null for inet columns
Sesquipedalian Jun 29, 2026
b9852b6
Don't try to convert smf_log_online.ip column; just drop and replace
Sesquipedalian Jul 6, 2026
d9edb56
Drops unused mod_prefs data
Sesquipedalian Apr 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions Languages/en_US/Maintenance.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,7 @@
$txt['maintenance_step'] = 'Step';
$txt['maintenance_overall_progress'] = 'Overall Progress';
$txt['maintenance_substep_progress'] = 'Step Progress';
$txt['maintenance_time_elasped_ms'] = 'Time Elapsed {m, plural,
one {# minute}
other {# minutes}
} and {s, plural,
one {# second}
other {# seconds}
}';
$txt['maintenance_time_elapsed'] = 'Time Elapsed: ';

// File Permissions.
$txt['chmod_linux_info'] = 'If you have a shell account, the following command can automatically correct permissions on these files';
Expand Down
2 changes: 1 addition & 1 deletion Sources/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,7 @@ public static function set(array $settings): void
}

// Make sure the paths are correct... at least try to fix them.
if (empty(self::$boarddir) || !is_dir(realpath(self::$boarddir))) {
if (empty(self::$boarddir) || !is_dir((string) realpath(self::$boarddir))) {
self::$boarddir = !empty($_SERVER['SCRIPT_FILENAME']) ? \dirname(realpath($_SERVER['SCRIPT_FILENAME'])) : \dirname(__DIR__);
}

Expand Down
100 changes: 69 additions & 31 deletions Sources/Db/APIs/MySQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,12 @@ public function quote(string $db_string, array $db_values, ?object $connection =
if (str_contains($db_string, '{')) {
// Do the quoting and escaping
$db_string = preg_replace_callback(
'~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~',
[
// The literal type can have arbitrary content.
'~{(literal):([^}]*)}~',
// Everything else needs to be a key in $db_values.
'~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~',
],
fn($matches) => $this->replacement__callback($matches, $db_values, $connection ?? $this->connection),
$db_string,
);
Expand Down Expand Up @@ -938,19 +943,29 @@ public function backup_table(string $table, string $backup_table): object|bool
],
);

// If this failed, we go old school.
if ($result) {
$columns = [];

// Do we have any generated columns to deal with?
foreach ($this->list_columns($table, true) as $column) {
// Skip generated columns in the insert statement.
if (empty($column['generation_expression'])) {
$columns[] = $column['name'];
}
}

$request = $this->query(
'INSERT INTO {raw:backup_table}
SELECT *
({raw:columns})
SELECT {raw:columns}
FROM {raw:table}',
[
'backup_table' => $backup_table,
'table' => $table,
'columns' => implode(', ', $columns),
],
);

// Old school or no school?
if ($request) {
return $request;
}
Expand Down Expand Up @@ -1047,6 +1062,13 @@ public function backup_table(string $table, string $backup_table): object|bool
);
}

// Restore the generation expressions on any generated columns.
foreach ($this->list_columns($table, true) as $column) {
if (!empty($column['generation_expression'])) {
$this->change_column($backup_table, $column['name'], $column);
}
}

return $request;
}

Expand Down Expand Up @@ -1373,15 +1395,15 @@ public function add_column(string $table_name, array $column_info, array $parame
$column_info['size'] = isset($column_info['size']) && is_numeric($column_info['size']) ? $column_info['size'] : null;

// Now add the thing!
$this->query(
$result = $this->query(
'ALTER TABLE ' . $short_table_name . '
ADD ' . $this->create_query_column($column_info) . (empty($column_info['auto']) ? '' : ' primary key'),
[
'security_override' => true,
],
);

return true;
return $result !== false;
}

/**
Expand Down Expand Up @@ -1913,7 +1935,7 @@ public function create_table(string $table_name, array $columns, array $indexes
}

// Create the table!
$this->query(
$result = $this->query(
$table_query,
[
'security_override' => true,
Expand Down Expand Up @@ -1954,7 +1976,7 @@ public function create_table(string $table_name, array $columns, array $indexes
$this->drop_table($short_table_name . '_old');
}

return true;
return $result !== false;
}

/**
Expand All @@ -1980,15 +2002,14 @@ public function drop_table(string $table_name, array $parameters = [], string $e
$tables = $this->list_tables($database);

if (\in_array($full_table_name, $tables)) {
$query = 'DROP TABLE ' . $short_table_name;
$this->query(
$query,
$result = $this->query(
'DROP TABLE ' . $short_table_name,
[
'security_override' => true,
],
);

return true;
return $result !== false;
}

// Otherwise do 'nout.
Expand Down Expand Up @@ -2032,14 +2053,14 @@ public function rename_table(string $old_name, string $new_name, bool $allowed_r
return false;
}

$this->query(
$result = $this->query(
'ALTER TABLE ' . $short_old_name . ' RENAME ' . $short_new_name,
[
'security_override' => true,
],
);

return true;
return $result !== false;
}

/**
Expand Down Expand Up @@ -2088,14 +2109,12 @@ public function list_columns(string $table_name, bool $detail = false, array $pa
$database = !empty($match[2]) ? $match[2] : $this->name;

$result = $this->query(
'SELECT column_name "Field", COLUMN_TYPE "Type", is_nullable "Null", COLUMN_KEY "Key" , column_default "Default", extra "Extra", generation_expression "generation_expression"
FROM information_schema.columns
WHERE table_name = {string:table_name}
AND table_schema = {string:db_name}
ORDER BY ordinal_position',
'SHOW COLUMNS
FROM {identifier:table_name}
IN {identifier:db}',
[
'db' => strtr($database, ['`' => '']),
'table_name' => $real_table_name,
'db_name' => $this->name,
],
);
$columns = [];
Expand All @@ -2109,8 +2128,7 @@ public function list_columns(string $table_name, bool $detail = false, array $pa

// Can we split out the size?
if (preg_match('~^(.+?)\s*\((\d+)\)$~', $row['Type'], $matches)) {
$type = $matches[1];
$size = $matches[2];
[$type, $size] = $this->calculate_type($matches[1], (int) $matches[2], true);
} elseif (preg_match('~^(.+?)\s+unsigned$~', $row['Type'], $matches)) {
$type = $matches[1];
$size = null;
Expand All @@ -2135,12 +2153,32 @@ public function list_columns(string $table_name, bool $detail = false, array $pa
unset($unsigned);
}

// If this is a generated column, look up its generation expression.
if (str_contains($row['Extra'], 'GENERATED')) {
$columns[$row['Field']]['generation_expression'] = $row['generation_expression'];
$result2 = $this->query(
'SELECT generation_expression
FROM information_schema.columns
WHERE column_name = {string:field}
AND table_name = {string:table_name}
AND table_schema = {string:db}',
[
'db' => strtr($database, ['`' => '']),
'table_name' => $real_table_name,
'field' => $row['Field'],
],
);

[$generation_expression] = $this->fetch_row($result2);

$this->free_result($result2);

$columns[$row['Field']]['generation_expression'] = $this->unescape_string($generation_expression);

$columns[$row['Field']]['stored'] = str_contains($row['Extra'], 'STORED');
}
}
}

$this->free_result($result);

return $columns;
Expand Down Expand Up @@ -2215,15 +2253,15 @@ public function remove_column(string $table_name, string $column_name, array $pa

foreach ($columns as $column) {
if ($column['name'] == $column_name) {
$this->query(
$result = $this->query(
'ALTER TABLE ' . $short_table_name . '
DROP COLUMN ' . $column_name,
[
'security_override' => true,
],
);

return true;
return $result !== false;
}
}

Expand All @@ -2245,28 +2283,28 @@ public function remove_index(string $table_name, string $index_name, array $para
// If the name is primary we want the primary key!
if ($index['type'] == 'primary' && $index_name == 'primary') {
// Dropping primary key?
$this->query(
$result = $this->query(
'ALTER TABLE ' . $short_table_name . '
DROP PRIMARY KEY',
[
'security_override' => true,
],
);

return true;
return $result !== false;
}

if ($index['name'] == $index_name) {
// Drop the bugger...
$this->query(
$result = $this->query(
'ALTER TABLE ' . $short_table_name . '
DROP INDEX ' . $index_name,
[
'security_override' => true,
],
);

return true;
return $result !== false;
}
}

Expand Down Expand Up @@ -2399,11 +2437,11 @@ public function setSqlMode(string $mode = 'default'): bool
$sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,PIPES_AS_CONCAT';
}

$this->query('SET SESSION sql_mode = {string:sql_mode}', [
$result = $this->query('SET SESSION sql_mode = {string:sql_mode}', [
'sql_mode' => $sql_mode,
]);

return true;
return $result !== false;
}

/**
Expand Down
Loading
Loading