dushaoshuai / dushaoshuai.github.io

https://www.shuai.host
0 stars 0 forks source link

Go: gorm: 使用自定义类型作为 Where() 方法的第一个参数,导致 bug #145

Open dushaoshuai opened 2 months ago

dushaoshuai commented 2 months ago

背景

在 gorm 中,可以使用 Where() 方法来添加条件:

// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;

Where() 方法的第一个参数是 any 类型,Where() 方法根据第一个参数的动态类型-也就是我们传递的实参的类型,来决定如何构建 SQL 语句。

问题

下面的代码可以说明我碰到的问题,完整代码在这里

type notString string

const (
    col1 notString = "col1"
)

func Test_not_a_string_type(t *testing.T) {
    db := gorm_learn.SQLiteInMemoryDB(context.Background()).
        Session(&gorm.Session{DryRun: true})

    var val map[string]any

    notStrCond := col1 + " IS NOT NULL"

    err := db.Table("some_table").
        Where(notStrCond).
        Find(&val).
        Error
    if err != nil {
        t.Error(err)
    }
    // [0.030ms] [rows:0] SELECT * FROM `some_table` WHERE `some_table`. = "col1 IS NOT NULL"
    //    not_a_string_type_test.go:35: model value required

    fmt.Println()
    // "col1 IS NOT NULL": col1 IS NOT NULL: not_a_string_type_test.notString
    fmt.Printf("%q: %v: %T\n", notStrCond, notStrCond, notStrCond)

    fmt.Println()
    var a any = notStrCond
    _, ok := a.(string)
    fmt.Printf("notString is string?: %v\n", ok) // notString is string?: false
}

在上面的代码中,虽然 notString 类型的 underlying type 是 string,但是它是和 string 完全不同的新类型。常量 col1notString 类型,变量 notStrCond 也是 notString 类型。变量 notStrCond 被传递给 Where() 方法作为第一个参数。

我潜意识里认为自己传递的是 string 类型的参数,但是 gorm 并不认识这个自定义类型 notString,导致最后生成的 SQL 语句错误。

解决方法

在将 notStrCond 传递给 Where() 时,将其转换为 string 类型:Where(string(notStrCond))