Skip to content

fix(Migrator): make HasColumn match the exact column via pragma_table_info#235

Open
h2zi wants to merge 2 commits into
go-gorm:masterfrom
h2zi:fix-hascolumn-false-positive
Open

fix(Migrator): make HasColumn match the exact column via pragma_table_info#235
h2zi wants to merge 2 commits into
go-gorm:masterfrom
h2zi:fix-hascolumn-false-positive

Conversation

@h2zi

@h2zi h2zi commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What this PR does

HasColumn matches the column name as a LIKE substring against the raw table DDL, which reports columns as present when their name merely appears somewhere in the DDL text:

CREATE TABLE plaincols (id integer, first_name text);
-- HasColumn("plaincols", "name") returns true: "first_name text" contains "name "

CREATE TABLE `defv` (`id` integer, `cfg` text DEFAULT 'name value');
-- HasColumn("defv", "name") returns true: the default value contains "name "

When this false positive hits during AutoMigrate, the driver skips AddColumn for a genuinely missing column and later queries fail with no such column.

The fix

Query pragma_table_info instead, which returns the actual columns of the table:

SELECT count(*) FROM pragma_table_info(?) WHERE name = ? COLLATE NOCASE

COLLATE NOCASE keeps the comparison case-insensitive, as the LIKE patterns were. This matches how HasTable and GetIndexes already query metadata instead of searching SQL text.

Tests

TestHasColumnNoFalsePositive covers both false-positive scenarios above plus the positive cases.

🤖 Generated with Claude Code

…_info

HasColumn matched the column name as a LIKE substring against the table
DDL, so it reported columns as present when their name merely appeared
somewhere in the DDL text:

- an unquoted DDL with a first_name column makes HasColumn("name")
  return true ("first_name text" contains "name ")
- a string default like DEFAULT 'name value' does the same

AutoMigrate then skips AddColumn for the missing column and later
queries fail with "no such column". Query pragma_table_info instead,
which returns the actual columns; COLLATE NOCASE keeps the comparison
case-insensitive like LIKE was. This matches how HasTable and
GetIndexes already query metadata instead of searching the SQL text.
Copilot AI review requested due to automatic review settings July 7, 2026 14:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes false positives in the SQLite dialector’s Migrator.HasColumn by switching from substring matching against sqlite_master.sql to querying actual table metadata via pragma_table_info, preventing AutoMigrate from incorrectly skipping AddColumn when a column name only appears in DDL text (e.g., as part of another column name or a default string literal).

Changes:

  • Replace HasColumn implementation to count exact column-name matches from pragma_table_info(?) using COLLATE NOCASE.
  • Add TestHasColumnNoFalsePositive to cover prior false-positive scenarios and confirm true positives.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
migrator.go Updates HasColumn to check real column metadata via pragma_table_info instead of searching raw DDL text.
migrator_test.go Adds a regression test ensuring HasColumn does not return true for substring/default-value matches.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread migrator_test.go
Comment on lines +14 to +17
db, err := gorm.Open(Open("file:hascolumn_exact?mode=memory&cache=shared"), &gorm.Config{})
if err != nil {
t.Fatalf("gorm.Open: %v", err)
}

@h2zi h2zi Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — fixed in 225bd54: the pool is now closed via t.Cleanup, so the shared in-memory database is torn down between -count=N runs. Verified with go test -count=2 -run TestHasColumnNoFalsePositive.

Address review feedback: the named shared in-memory database lives as
long as a connection remains open, leaking schema across repeated runs
(go test -count=N).
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.

2 participants