Skip to content

fix(migrator): drop duplicate PRIMARY KEY clause in AlterColumn - #249

Open
libvoid wants to merge 1 commit into
go-gorm:masterfrom
libvoid:fix
Open

fix(migrator): drop duplicate PRIMARY KEY clause in AlterColumn#249
libvoid wants to merge 1 commit into
go-gorm:masterfrom
libvoid:fix

Conversation

@libvoid

@libvoid libvoid commented Aug 19, 2026

Copy link
Copy Markdown
  • Do only one thing
  • Non breaking API changes
  • Tested

What did this pull request do?

AlterColumn rewrites a column's definition using FullDataTypeOf, which inlines PRIMARY KEY AUTOINCREMENT for autoincrement fields.

If the table's DDL already declared that column's primary key as a separate PRIMARY KEY (`col`) table constraint, that clause was never removed producing a CREATE TABLE with two primary key definitions and failing with "table ... has more than one primary key".

We need to look for a matching standalone PRIMARY KEY clause referencing that column alone and drop it. Composite primary keys are left untouched, since they parse to more than one column.

This commit fixes a regression introduced since 139bd30 (v1.5.4)

User Case Description

I hit this bug in our project when trying to update the DB schema.


Here is a MCVE:

module repro

go 1.26

require (
	gorm.io/driver/sqlite v1.6.0
	gorm.io/gorm v1.31.2
)
package main

import (
	"fmt"
	"os"

	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

type Host struct {
	ID       uint `gorm:"primaryKey"`
	SSHKeyID uint
	HopID    uint
}

func main() {
	os.Remove("repro.db")

	db, err := gorm.Open(sqlite.Open("repro.db"), &gorm.Config{})
	if err != nil {
		panic(err)
	}

	legacyDDL := "CREATE TABLE `hosts` (" +
		"`id` integer," +
		"`ssh_key_id` integer," +
		"`hop_id` integer," +
		"PRIMARY KEY (`id`)," +
		"CONSTRAINT `fk_hosts_hop` FOREIGN KEY (`hop_id`) REFERENCES `hosts`(`id`)" +
		")"

	if err := db.Exec(legacyDDL).Error; err != nil {
		panic(err)
	}

	// field forces GORM to rebuild the table, inlining "PRIMARY KEY AUTOINCREMENT" onto the id column.
	err = db.Migrator().AlterColumn(&Host{}, "ID")
	if err != nil {
		fmt.Println("BUG REPRODUCED:", err)
		os.Exit(1)
	}

	fmt.Println("OK")
}
go get gorm.io/driver/sqlite@master
go mod tidy
go run -v main.go
go mod edit -replace gorm.io/driver/sqlite=github.com/libvoid/sqlite@fix
go mod tidy
go run main.go

AlterColumn rewrites a column's definition using FullDataTypeOf, which inlines "PRIMARY KEY AUTOINCREMENT" for autoincrement fields.

If the table's DDL already declared that column's primary key as a separate "PRIMARY KEY (`col`)" table constraint, that clause was never removed
producing a CREATE TABLE with two primary key definitions and failing with "table ... has more than one primary key".

We need to look for a matching standalone PRIMARY KEY clause referencing that column alone and drop it.
Composite primary keys are left untouched, since they parse to more than one column.

This commit fixes a regression introduced since 139bd30 (v1.5.4)
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.

1 participant