Skip to content

fix: strip the table prefix correctly when rebuilding a SQLite3 table - #10509

Open
karlgray wants to merge 1 commit into
codeigniter4:developfrom
karlgray:fix-sqlite-table-rebuild-fk-prefix
Open

fix: strip the table prefix correctly when rebuilding a SQLite3 table#10509
karlgray wants to merge 1 commit into
codeigniter4:developfrom
karlgray:fix-sqlite-table-rebuild-fk-prefix

Conversation

@karlgray

@karlgray karlgray commented Aug 30, 2026

Copy link
Copy Markdown

Fixes #10508
SQLite cannot alter or drop a column in place, so SQLite3\Table rebuilds the whole table and recreates its foreign keys from the metadata it collected. That metadata holds prefixed table names, and createTable() stripped the prefix with trim($name, $this->db->DBPrefix).

trim()'s second argument is a set of characters, not a prefix. It removes any of those characters from either end of the string, repeatedly, so with the prefix db_ it turns db_bandit_fk into andit_fk — the leading b of the table's own name is eaten as well, and characters are stripped from the end too.

The rebuilt table's foreign keys then reference tables that do not exist. Nothing fails at that point, because foreign key enforcement is off for the duration of the rebuild; the error surfaces at the next write to the referenced table, naming a table that appears nowhere in the schema. Whether a given table is affected depends on which characters its name happens to begin and end with, so most tables come through untouched.

Strip the prefix the way fromTable() in the same class already does.

The existing tests could not catch this: AlterTableTest builds its own connection without a DBPrefix, and the damage is invisible until the constraint is used. The regression test therefore sets a prefix and names the referenced table so that it begins with a character the prefix also contains, which is what makes the bug reproduce.

Description
Explain what you have changed, and why.

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

SQLite cannot alter or drop a column in place, so `SQLite3\Table` rebuilds
the whole table and recreates its foreign keys from the metadata it
collected. That metadata holds prefixed table names, and `createTable()`
stripped the prefix with `trim($name, $this->db->DBPrefix)`.

`trim()`'s second argument is a set of characters, not a prefix. It removes
any of those characters from either end of the string, repeatedly, so with
the prefix `db_` it turns `db_bandit_fk` into `andit_fk` — the leading `b`
of the table's own name is eaten as well, and characters are stripped from
the end too.

The rebuilt table's foreign keys then reference tables that do not exist.
Nothing fails at that point, because foreign key enforcement is off for the
duration of the rebuild; the error surfaces at the next write to the
referenced table, naming a table that appears nowhere in the schema. Whether
a given table is affected depends on which characters its name happens to
begin and end with, so most tables come through untouched.

Strip the prefix the way `fromTable()` in the same class already does.

The existing tests could not catch this: `AlterTableTest` builds its own
connection without a `DBPrefix`, and the damage is invisible until the
constraint is used. The regression test therefore sets a prefix and names
the referenced table so that it begins with a character the prefix also
contains, which is what makes the bug reproduce.
@mergeable

mergeable Bot commented Aug 30, 2026

Copy link
Copy Markdown

Hi there, karlgray! 👋

Thank you for sending this PR!

We expect the following in all Pull Requests (PRs).

Important

We expect all code changes or bug-fixes to be accompanied by one or more tests added to our test suite to prove the code works.

If pull requests do not comply with the above, they will likely be closed. Since we are a team of volunteers, we don't have any more time to work
on the framework than you do. Please make it as painless for your contributions to be included as possible.

See https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md

Sincerely, the mergeable bot 🤖

@michalsn michalsn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, it looks good. Thank you!

Comment on lines +339 to +341
// trim() takes a set of characters, not a prefix, so it would also
// strip those characters from the end of the name, and from the
// start beyond the prefix itself.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// trim() takes a set of characters, not a prefix, so it would also
// strip those characters from the end of the name, and from the
// start beyond the prefix itself.

We don't really need this comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: trim() takes a character set, not a prefix, so with db_ set the rebuilt table's foreign keys point at tables that don't exist.

2 participants