2/24 (月)
PostgreSQL JDBC で復号型の配列を扱いたいのだが…
PostgreSQL JDBC のドライバーにはいろいろ癖があり、JDBC で定義されている機能の一部をサポートしていない。 ユーザー定義タイプ(User-Defined Type)を扱う java.sql.SQLData がサポートされておらず、PostgreQL の複合型(composite type)を扱うのが非常に難しい。
例えば entity_struct という複合型があるとする。 この配列を作って entity_table テーブルに一度に複数行を挿入することを考える。
CREATE TABLE entity_table (
guid uuid NOT NULL,
version bigint NOT NULL,
message text NOT NULL,
creation timestamp NOT NULL
);
CREATE TYPE entity_struct AS (
guid uuid,
version bigint,
message text,
creation timestamp
);
一応、org.postgresql.util.PGobject を介して複合型のオブジェクトを作ることはできるが、その setValue メソッドの値に PostgreSQL のネイティブの複合型のリテラル表現 を入れることになる。 例えば "(9107ffb3-2457-4dc9-8f20-12c645c288fd, 123, 'abc', '2018-03-21 16:00:45.0')" みたいに。
これは全体が文字リテラルの上に、その中の構成要素も型に応じた文字列化が必要なので書くのが非常に複雑だ。 uuid 型や数値型はまだよいが文字列型を入れるにはエスケープシーケンスを二重にかけることになる。 より書きやすい ROW(9107ffb3-2457-4dc9-8f20-12c645c288fd, 123, 'abc', '2018-03-21 16:00:45.0') はここには書けないようだ。
Class.forName("org.postgresql.Driver");
Properties info = new Properties();
info.setProperty("user", "<user>");
info.setProperty("password", "<password>");
Connection connection;
connection = DriverManager.getConnection("jdbc:postgresql://127.0.0.1:5432/<db>", info);
connection.setAutoCommit(true);
Object[] in = new Object[SIZE];
for (int i = 0 ; i < SIZE ; i++) {
PGobject cc = new PGobject();
UUID uuid = UUID.randomUUID();
long version = i;
String message = "abc";
Timestamp timestamp = new Timestamp(new Date().getTime());
cc.setType("entity_struct");
cc.setValue(uuid.toString() + "," + String.valeOf(version) + ",'" + message + "','" + timestamp.toString() + "')");
in[i] = cc;
}
try (PreparedStatement pstmt = connection.prepareStatement(
"INSERT INTO entity_table (guid, version, message, creation) " +
"SELECT E.guid, E.version, E.message, E.creation FROM unnest(?::entity_struct[]) E";")) {
pstmt.setArray(1, connection.createArrayOf("entity_struct", in));
pstmt.executeUpdate();
}
いろいろ困っていたのだが、無理に JDBC のレベルで複合型にする必要はないと気づいた。 PostgreSQL の場合、SELECT リストに unnest 関数を並べると、配列のインデックスの番号が一致する要素を集めて新しい行を作ることができるので、JDBC 側では複合型の列を別々の配列にして渡せばよい。 単一型の配列ならば通常通りの変換がでできるので、Java の String 型を java.sql.String の配列にする場合、エスケープシーケンスなどは JDBC ドライバー側に任せることができるようになる。
Object[] in1 = new Object[SIZE];
Object[] in2 = new Object[SIZE];
Object[] in3 = new Object[SIZE];
Object[] in4 = new Object[SIZE];
for (int i = 0 ; i < SIZE ; i++) {
in1[i] = UUID.randomUUID();
in2[i] = new Long(i);
in3[i] = "abc";
in4[i] = new Timestamp(new Date().getTime());
}
try (PreparedStatement pstmt = connection.prepareStatement(
"INSERT INTO entity_table (guid, version, message, creation) " +
"SELECT unnest(?::uuid[]), unnest(?::int8[]), unnest(?::text[]), unnest(?::timestamp[])));
pstmt.setArray(1, connection.createArrayOf("uuid", in1));
pstmt.setArray(2, connection.createArrayOf("int8", in2));
pstmt.setArray(3, connection.createArrayOf("message", in3));
pstmt.setArray(4, connection.createArrayOf("timestamp", in4));
pstmt.executeUpdate();
}
非常にかっこが悪いが実用的なやり方だ。
2/14 (金)
GitLab CI で Docker で
GitLab CI のジョブコンテナの中から GitLab CI サーバーの Docker デーモンにアクセスする、それも Docker デーモンが TCP ソケットで待ち受けしている所にアクセスしようとする。 この場合、GitLab CI のジョブコンテナから GitLab CI サーバー(ホスト)にアクセスする場合、ホスト側のファイヤーウォールの制限を受けるようだ。
ホストとコンテナは docker0 のネットワークでつながっているので、ここにホストの Docker デーモンのアクセスポートを書く必要がある。
firewall-cmd --permanent --zone=trusted --change-interface=docker0
firewall-cmd --permanent --zone=trusted --add-port=(ポート)/tcp
firewall-cmd --reload
2/6 (木)
DNS リゾルバーを設定しないホストでは Kong が異常動作する
Kong を動かすサーバーは DNS リゾルバーを設定していないと動作しない。 127.0.0.1 経由でも Admin API を実行できなくなるためだ。
Kong が DNS を使ってロードバランシングをする機能があり、DNS リゾルバーが設定されていないと異常を検出されてしまうためで、どこにもつながらないローカルに閉じた DNS サーバーであっても、指定されてさいいれば動くようだ。
2/5 (水)
Open Container Initiative コンテナの仕様
Open Container Initiative(OCI) コンテナの仕様を読む。