GolangでDb2(その2)
はじめに
前回は Golang で Db2 の各種データ型を扱った場合の評価を行いました。
今回は、日本語の識別子、データを扱った場合の評価を行います。
Db2
データベース
前回と同様、Db2 for Docker 上の Db2 を使用します。
テスト用のテーブル
対象とするデータ型
前回は全てのデータ型を対象としましたが、今回は文字データを保持するものを対象とします。
型名 | 範囲 |
---|---|
CHAR | 固定長文字列(1~255byte or 1~63CODEUNIT32) |
VARCHAR | 可変長文字列(1~32,672byte or 1~8,168CODEUNIT32) |
CLOB | 可変長文字列(1~2,147,483,647byte or 1~536,870,911CODEUNIT32) |
GRAPHIC | 固定長グラフィック(2byte文字)文字列(1~127グラフィック文字 or 1~63CODEUNIT32) |
VARGRAPHIC | 可変長グラフィック文字列(1~16,336グラフィック文字 or 1~8,168CODEUNI32) |
可変長グラフィック文字列(1~1,073,741,823グラフィック文字 or 1~536,870,911CODEUNIT32) | |
整形式のXMLドキュメント |
この中で、前回の評価で文字列として扱うことが困難だった DBCLOB
と XML
はギブアップすることにします。
テスト用のテーブル
ちょっとアレな感じの CREATE TABLE 文にします。
create table 日ほンゴ表 (
列1 char(30) NOT NULL,
れつ2 varchar(30),
レツ3 clob(30),
レツ4 graphic(10),
Retsu5 vargraphic(20),
CONSTRAINT pk_datatypes PRIMARY KEY(列1)
) ccsid unicode;
ccsid unicode
については、以下のページを参照ください。
Golang
処理系とライブラリ
前回と同様、Golang は、1.20.7。github.com/ibmdb/go_ibm_db は、v0.4.5 を使用します。
アプリケーション
前回と同様ですが、Golang 側の識別子も日本語にしてみます。ただし、Golang の識別子の先頭文字は、パッケージ外への export の可否の識別に使われるため、先頭文字は英大文字にしておきます。
package main
import (
"database/sql"
"log"
"os"
"reflect"
"github.com/google/go-cmp/cmp"
_ "github.com/ibmdb/go_ibm_db"
"github.com/roboninc/sqlx"
)
// S日ほンゴ表 は、日本語テストテーブル
type S日ほンゴ表 struct {
F列1 sql.NullString `db:"列1"` // char(30)
Fれつ2 sql.NullString `db:"れつ2"` // varchar(30)
Fレツ3 sql.NullString `db:"レツ3"` // clob(30)
Fレツ4 sql.NullString `db:"レツ4"` // graphic(10)
FRetsu5 sql.NullString `db:"RETSU5"` // vargraphic(10)
}
// Key は、日本語テストテーブルのキー
type Key struct {
Col01 string `db:"col01"`
}
func main() {
sqlx.BindDriver("go_ibm_db", sqlx.QUESTION)
dsn := os.Getenv("DSN")
db, err := sqlx.Open("go_ibm_db", 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}, // clob は日本語NGみたい
Fレツ4: sql.NullString{String: "コテイ長文字列 ", Valid: true},
FRetsu5: sql.NullString{String: "あぁアアガAa漢〇€㈱ー~―‐-", Valid: true},
}
_, err = db.NamedExec(`
INSERT INTO 日ほンゴ表 (
列1, れつ2, レツ3, レツ4, RETSU5
) VALUES (
:列1, :れつ2, :レツ3, :レツ4, :RETSU5
)`,
src,
)
if err != nil {
log.Printf("db.Exec error %s", err)
}
dst := S日ほンゴ表{}
query, args, err := db.BindNamed(`
SELECT
列1, れつ2, レツ3, レツ4, RETSU5
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)
}
}
問題ではありませんが、Oracle と同じで、Db2 のカラム名の戻しが upper case なのは、全角文字でも同様みたいなので、StructScan で受けるためには、上記のように CREATE TABLE 文で「Retsu5」あっても、db タグは「RETSU5」にしないといけませんでした。
sqlx の問題(再掲というかアップデート)
以前の PostgreSQL の評価時に明らかになったのですが、sqlx の Named Query は、マルチバイト Unicode 対応できていません。
このため、このプログラムを動かすには、sqlx を修正する必要があります。(ここまで再掲)
この修正をプルリクしたのですが、反映というか更新そのものが停止しているようなので、当社で fork した sqlx をメンテナンスすることにします。(上のアプリケーションもこちらを import しています。)
このリポジトリについては、どこかで記事にしたいと思います。
おわりに
今回の評価では、Db2 特有の日本語の問題は発見できませんでしたが、DBCLOB
や CLOB
の振る舞いを見ると、文字コードセットの設定のバリエーションによって動きが異なる可能性もあります。
Discussion