GolangでSQLServer(その2)
はじめに
先日、Golang で SQLServer の各種データ型を扱った場合の評価を行いました。
今回は、日本語の識別子、データを扱った場合の評価を行います。
SQLServer
データベース
前回と同様、Amazon RDS 上に構築した SQLServer サーバを使用します。
テスト用のテーブル
対象とするデータ型
前回は、全てのデータ型を対象としましたが、今回は文字データを保持するものを対象とします。
型名 | 範囲 | 容量 | 説明 |
---|---|---|---|
char[(n)] | 1~8000byte | 固定長文字列 | |
varchar[(n)] | 1~8000byte | 可変長文字列 | |
text | 2^31-1byte | 可変長文字列 | |
nchar[(n)] | 1~8000byte | n は 1~4000文字。固定長文字列 | |
nvarchar[(n|max)] | 1~8000byte または 2GB | n は 1~4000文字。可変長文字列 | |
ntext | ~ 2^30-1byte | 可変長Unicodeデータ | |
xml [([CONTENT|DOCUMENT]xml_schema_collection)] | ~2GB | XMLデータ |
SQLServer も基本は、N ありと N なしの固定長文字列、可変長文字列、文字型ラージオブジェクトになります。
テスト用のテーブル
ちょっとアレな感じの CREATE TABLE 文にします。
CREATE TABLE 日ほンゴ表 (
列1 char(30),
れつ2 varchar(30),
レツ3 text,
レツ4 nchar(10),
Retsu5 nvarchar(10),
列6 ntext,
列7 xml,
CONSTRAINT pk_日ほンゴ表 PRIMARY KEY(列1)
);
go
RDS にデフォルトで SQLServer を構築すると、以下のように Latin1 になってしまいます。
$ sqlcmd -U agra -S sqlserver.■■■■.ap-northeast-1.rds.amazonaws.com
Password:
1> use agra;
2> go
Changed database context to 'agra'.
1> SELECT name, collation_name FROM sys.columns WHERE object_id = OBJECT_ID(N'dbo.日ほンゴ表');
2> go
name collation_name
-------------- ----------------------------
列1 SQL_Latin1_General_CP1_CI_AS
れつ2 SQL_Latin1_General_CP1_CI_AS
レツ3 SQL_Latin1_General_CP1_CI_AS
レツ4 SQL_Latin1_General_CP1_CI_AS
Retsu5 SQL_Latin1_General_CP1_CI_AS
列6 SQL_Latin1_General_CP1_CI_AS
列7 NULL
(7 rows affected)
1> SELECT COLLATIONPROPERTY('Japanese_CI_AS', 'CodePage');
2> go
----
932
このため、今回は、CREATE TABLE 文で明示的に Japanese_CI_AS を指定します。コードページは 932 なので ShiftJIS になります。
CREATE TABLE 日ほンゴ表 (
列1 char(30) collate Japanese_CI_AS,
れつ2 varchar(30) collate Japanese_CI_AS,
レツ3 text collate Japanese_CI_AS,
レツ4 nchar(10),
Retsu5 nvarchar(10),
列6 ntext,
列7 xml,
CONSTRAINT pk_日ほンゴ表 PRIMARY KEY(列1)
);
go
Golang
処理系とライブラリ
前回と同様、Golang は、1.18.6。github.com/microsoft/go-mssqldb は、v0.21.0 を使用します。
アプリケーション
前回と同様ですが、Golang 側の識別子も日本語にしてみます。ただし、Golang の識別子の先頭文字は、パッケージ外への export の可否の識別に使われるため、先頭文字は英大文字にしておきます。
package main
import (
"database/sql"
"log"
"os"
"reflect"
"github.com/google/go-cmp/cmp"
"github.com/jmoiron/sqlx"
_ "github.com/microsoft/go-mssqldb"
)
// 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"` // text
Fレツ4 sql.NullString `db:"レツ4"` // nchar(10)
FRetsu5 sql.NullString `db:"Retsu5"` // nvarchar(10)
F列6 sql.NullString `db:"列6"` // ntext
F列7 sql.NullString `db:"列7"` // xml
}
// Key は、日本語テストテーブルのキー
type Key struct {
Col01 string `db:"col01"`
}
func main() {
dsn := os.Getenv("DSN")
db, err := sqlx.Open("sqlserver", 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}, // cp932 に € は無い
Fレツ4: sql.NullString{String: "コテイ長文字列 ", Valid: true},
FRetsu5: sql.NullString{String: "カヘン長文字列", Valid: true},
F列6: sql.NullString{String: "あぁアアガAa漢〇€㈱ー~―‐-", Valid: true},
F列7: sql.NullString{String: "<xml/><ルートノード>あ</ルートノード>", Valid: true},
}
_, err = db.NamedExec(`
INSERT INTO 日ほンゴ表 (
列1, れつ2, レツ3, レツ4, Retsu5, 列6, 列7
) VALUES (
:列1, :れつ2, :レツ3, :レツ4, :Retsu5, :列6, :列7
)`,
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
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)
}
}
今回は、明示的に ShiftJIS を指定したので、ユーロ記号を入れると文字化けしました。このため、text のデータから除外しました。
sqlx の問題(再掲)
以前の PostgreSQL の評価時に明らかになったのですが、sqlx の Named Query は、マルチバイト Unicode 対応できていません。
このため、このプログラムを動かすには、sqlx を修正する必要があります。今回の修正は、sqlx には プルリク 済みです(が、休眠しているようなので、取り込まれないかもしれません)。
この修正を適用して動かすためには、以下のリポジトリを clone して、go.mod で replace する必要があります。
おわりに
今回の評価では、SQLServer 特有の日本語の問題は発見できませんでした。
Discussion