Repository navigation
[3.0] Rebuilds an index whose column the same pass has just changed - #9623
Merged
Sesquipedalian merged 1 commit intoSep 5, 2026
Conversation
normalize() changes the columns first and rebuilds the indexes afterwards, and it decides which indexes to rebuild partly by asking whether any of the columns they cover are about to change. That question was being asked of the wrong values: both lists hold their column names as values rather than as keys, so array_keys() on either gives 0, 1, 2 and the intersection with the columns to change -- which are keyed by name -- is always empty. No index was ever rebuilt for this reason. On PostgreSQL that loses the index. A change of column type there is carried out by adding a temporary column, copying into it, dropping the original and renaming, and dropping the original takes its indexes with it. Nothing then puts them back, so a 2.1 forum upgrading to 3.0 arrives without the indexes on mail_queue.time_sent, scheduled_tasks.next_time, member_logins.time, log_subscribed.end_time, log_reported.time_started and log_reported_comments.time_sent -- every one of them a column widened for 2038, and the first two of them read by the cron on every page load that runs it. Running the upgrade a second time restores them, since by then the columns are already the right type and nothing drops the indexes again. That is what makes this hard to notice: the schema an admin ends up with depends on how many times they ran the upgrader. MySQL is unaffected. It changes a column in place, so an index over that column survives whether or not anything asks for it to be rebuilt. The bare names are compared now. MySQL reports a prefix length as part of the column name, so that is stripped before comparing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
This was referenced Sep 5, 2026
Sesquipedalian
approved these changes
Sep 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
A PostgreSQL forum upgraded from 2.1 to 3.0 arrives without six of the indexes a fresh install has, all of them on columns widened for 2038:
mail_queuetime_sentscheduled_tasksnext_timemember_loginstimelog_subscribedend_timelog_reportedtime_startedlog_reported_commentstime_sentThe first two are read by the cron on every page load that runs it.
normalize()changes columns first and rebuilds indexes afterwards, and decides which indexes to rebuild partly by asking whether any of the columns they cover are about to change. That question was being asked of the wrong values:Both lists hold their column names as values — the definition as
['name' => ...]arrays, the database as plain strings — soarray_keys()on either gives0, 1, 2.$columns_to_changeis keyed by column name. The intersection is always empty, and no index has ever been rebuilt for this reason.On PostgreSQL that loses the index. A change of column type there is carried out by adding a temporary column, copying into it, dropping the original and renaming:
Nothing then puts the index back.
MySQL is unaffected: it changes a column in place, so an index over that column survives whether or not anything asks for it to be rebuilt.
What makes this hard to notice is that running the upgrade a second time restores them, because by then the columns are already the right type and nothing drops the indexes again. The schema an admin ends up with depends on how many times they ran the upgrader.
The bare names are compared now. MySQL reports a prefix length as part of the column name (
body(15)), so that is stripped first.How this was found, and how it was verified
.docker/rerun-upgrade.sh(#9622) upgrades a 2.1 baseline, upgrades it again, and reports what the second run changed. It should change nothing. On PostgreSQL it reported these six indexes appearing on the second run, which is what led here. The statement order above was then read out of the PostgreSQL log withlog_statement='ddl'turned on.Against the 2.1.7 baseline from the 2.1 development environment:
rerun-upgrade.shschema CHANGEDschema unchangedrerun-upgrade.shschema unchangedschema unchangedNot reachable from the unit suite
Schema\Column::__construct()callsDb::$db->calculate_type(), so the schema classes cannot be instantiated without a connection, andnormalize()does nothing but read and alter a live database. Verified by running real upgrades on both engines instead.Issues References (Fixes|Related|Closes)