DRYな備忘録

Don't Repeat Yourself.

【Go言語】sql driverの違いによるCHARとVARCHARの挙動の違い【特にpostgresのlib/pq】

Go言語からSQLを使うときは

Go言語からSQL(or SQL-like)データベースを使うときは、Go言語標準のsql - The Go Programming Languageが定めるinterfaceを実装したdriverを実際は使っています.

たとえばdriverはここに列挙されてたりします.

driverの違いによる挙動の違い

なるべく無いように作ってるとは思うんですが、今回PostgreSQLを使っていて、そのdriverであるlib/pq · GitHub、とメジャーなMySQL driverであるgo-sql-driver/mysql · GitHubの間に、CHAR型、VARCHAR型、TEXT型の挙動の違いがあって、軽く「ガッデム!!」となったので備忘録.

MySQL driverの使うコード

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

/*
CREATE TABLE foo (
    hoge CHAR(16),
    fuga VARCHAR(16),
    piyo TEXT
);
*/

func main() {
    db, _ := sql.Open("mysql", "/test")
    db.Exec(
        "INSERT INTO foo (hoge, fuga, piyo) VALUES (?, ?, ?)",
        "ほげ", "ふが", "ぴよ",
    )

    var hoge, fuga, piyo string
    db.QueryRow(
        "SELECT hoge, fuga, piyo FROM foo LIMIT 1",
    ).Scan(&hoge, &fuga, &piyo)

    fmt.Printf("hoge: ^%s$\nfuga: ^%s$\npiyo: ^%s$\n", hoge, fuga, piyo)
}

Postgres driverの使うコード

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/lib/pq"
)

/*
CREATE TABLE foo (
    hoge CHAR(16),
    fuga VARCHAR(16),
    piyo TEXT
);
*/

func main() {
    db, _ := sql.Open("postgres", "host=localhost dbname=test sslmode=disable")
    db.Exec(
        "INSERT INTO foo (hoge, fuga, piyo) VALUES ($1, $2, $3)",
        "ほげ", "ふが", "ぴよ",
    )

    var hoge, fuga, piyo string
    db.QueryRow(
        "SELECT hoge, fuga, piyo FROM foo LIMIT 1",
    ).Scan(&hoge, &fuga, &piyo)

    fmt.Printf("hoge: ^%s$\nfuga: ^%s$\npiyo: ^%s$\n", hoge, fuga, piyo)
}

その結果の違い

mysql

hoge: ^ほげ$
fuga: ^ふが$
piyo: ^ぴよ$

postgres

hoge: ^ほげ              $
fuga: ^ふが$
piyo: ^ぴよ$

go-sql-driver/mysqlを使った方は、CHAR型であってもTrimして返しており、一方lib/pqはCHAR型は空白含めて返していることが分かります.

どちらがあるべき姿なんすかね.

雑感