GolangでMySQL/MariaDB(その2)
はじめに
先日、Golang で MySQL と MariaDB の各種データ型を扱った場合の評価を行いました。
今回は、日本語の識別子、データを扱った場合の評価を行います。
MySQL と MariaDB
データベース
前回と同様、Amazon RDS 上に構築した MySQL と MariaDB サーバを使用します。
テスト用のテーブル
対象とするデータ型
前回は、全てのデータ型を対象としましたが、今回は文字データを保持するものを対象とします。
名称 | 別名 | 説明 |
---|---|---|
char | 固定長文字列 | |
varchar | 可変長文字列 | |
binary | 固定長byte列 | |
varbinary | 可変長byte列 | |
tinytext | character large object 大きさ順 | |
text | ||
mediumtext | ||
longtext | ||
enum | 列挙 | |
set | 集合 | |
json | JSON文字列 |
MySQL と MriaDB は、text のバリエーションが多いのと enum や set があります。
テスト用のテーブル
ちょっとアレな感じの CREATE TABLE 文にします。
DROP TABLE 日ほンゴ表;
CREATE TABLE 日ほンゴ表 (
列1 char(10) COMMENT '固定長文字列',
れつ2 varchar(10) COMMENT '可変長文字列',
レツ3 tinytext COMMENT 'tinytext',
レツ4 text COMMENT 'text',
Retsu5 mediumtext COMMENT 'mediumtext',
列6 longtext COMMENT 'longtext',
列7 enum('小', '中', '大') COMMENT '列挙',
列8 set('あか', 'みどり', 'あお') COMMENT '集合',
列9 json COMMENT 'JSON文字列',
CONSTRAINT pk_日ほンゴ表 PRIMARY KEY(列1)
) COMMENT='日本語テスト';
RDS の MariaDB の characterset
前回の status コマンドの結果にも表示されていましたが、デフォルトで RDS 上に作成すると latin1 だったので、変更しておきます。
MariaDB [agra]> alter database agra character set utf8mb3;
Query OK, 1 row affected (0.01 sec)
MariaDB [agra]> status
--------------
mysql Ver 15.1 Distrib 5.5.68-MariaDB, for Linux (x86_64) using readline 5.1
Connection id: 175
Current database: agra
Current user: agra@■.■.■.■
SSL: Not in use
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server: MariaDB
Server version: 10.6.10-MariaDB-log managed by https://aws.amazon.com/rds/
Protocol version: 10
Connection: maria.■■■■.ap-northeast-1.rds.amazonaws.com via TCP/IP
Server characterset: latin1
Db characterset: utf8mb3
Client characterset: utf8mb3
Conn. characterset: utf8mb3
TCP port: 3306
Uptime: 1 hour 52 min 27 sec
Threads: 3 Questions: 4008 Slow queries: 0 Opens: 49 Open tables: 15 Queries per second avg: 0.594
--------------
Golang
処理系とライブラリ
前回と同様、Golang は、1.18.6。github.com/go-sql-driver/mysql は、v1.6.0 を使用します。
アプリケーション
前回と同様ですが、Golang 側の識別子も日本語にしてみます。ただし、Golang の識別子の先頭文字は、パッケージ外への export の可否の識別に使われるため、先頭文字は英大文字にしておきます。
package main
import (
"database/sql"
"log"
"os"
"reflect"
_ "github.com/go-sql-driver/mysql"
"github.com/google/go-cmp/cmp"
"github.com/jmoiron/sqlx"
)
// S日ほンゴ表 は、日本語テストテーブル
type S日ほンゴ表 struct {
F列1 sql.NullString `db:"列1"` // char(10)
Fれつ2 sql.NullString `db:"れつ2"` // varchar(10)
Fレツ3 sql.NullString `db:"レツ3"` // tinytext
Fレツ4 sql.NullString `db:"レツ4"` // text
FRetsu5 sql.NullString `db:"Retsu5"` // mediumtext
F列6 sql.NullString `db:"列6"` // longtext
F列7 sql.NullString `db:"列7"` // enum('小', '中', '大')
F列8 sql.NullString `db:"列8"` // set('あか', 'みどり', 'あお')
F列9 sql.NullString `db:"列9"` // json
}
// Key は、日本語テストテーブルのキー
type Key struct {
Col01 string `db:"col01"`
}
func main() {
dsn := os.Getenv("DSN")
db, err := sqlx.Open("mysql", dsn)
if err != nil {
log.Printf("sql.Open error %s", err)
}
key := Key{"コテイ長文字列"}
src := S日ほンゴ表{
F列1: sql.NullString{String: "コテイ長文字列", Valid: true},
Fれつ2: sql.NullString{String: "カヘン長文字列", Valid: true},
Fレツ3: sql.NullString{String: "あぁアアガAa漢〇€㈱ー~―‐-", Valid: true},
Fレツ4: sql.NullString{String: "あぁアアガAa漢〇€㈱ー~―‐-", Valid: true},
FRetsu5: sql.NullString{String: "あぁアアガAa漢〇€㈱ー~―‐-", Valid: true},
F列6: sql.NullString{String: "あぁアアガAa漢〇€㈱ー~―‐-", Valid: true},
F列7: sql.NullString{String: "小", Valid: true},
F列8: sql.NullString{String: "あか,みどり,あお", Valid: true},
F列9: sql.NullString{String: `{"日本語キー": "日本語文字列"}`, Valid: true},
}
_, err = db.NamedExec(`
INSERT INTO 日ほンゴ表 (
列1, れつ2, レツ3, レツ4, Retsu5, 列6, 列7, 列8, 列9
) VALUES (
:列1, :れつ2, :レツ3, :レツ4, :Retsu5, :列6, :列7, :列8, :列9
)`,
src,
)
if err != nil {
log.Printf("db.Exec error %s", err)
}
dst := S日ほンゴ表{}
query, args, err := db.BindNamed(`
SELECT
列1, れつ2, レツ3, レツ4, Retsu5, 列6, 列7, 列8, 列9
FROM 日ほンゴ表
WHERE 列1 = :col01`,
key,
)
if err != nil {
log.Printf("db.BindNamed error %s", err)
}
err = db.QueryRowx(query,
args...,
).StructScan(
&dst,
)
if err != nil {
log.Printf("db.QueryRow error %s", err)
}
if !reflect.DeepEqual(src, dst) {
// log.Printf("\nsrc = %#v\ndst = %#v\n", src, dst)
diff := cmp.Diff(src, dst)
if len(diff) > 0 {
log.Print(diff)
}
}
_, err = db.NamedExec(`
DELETE FROM 日ほンゴ表
WHERE 列1 = :col01`,
key,
)
if err != nil {
log.Printf("db.Exec error %s", err)
}
}
sqlx の問題(再掲)
以前の PostgreSQL の評価時に明らかになったのですが、sqlx の Named Query は、マルチバイト Unicode 対応できていません。
このため、このプログラムを動かすには、sqlx を修正する必要があります。今回の修正は、sqlx には プルリク 済みです(が、休眠しているようなので、取り込まれないかもしれません)。
この修正を適用して動かすためには、以下のリポジトリを clone して、go.mod で replace する必要があります。
おわりに
今回の評価では、MySQL と MariaDB に特有の日本語の問題は発見できませんでした。
Discussion