‘MySQL’ カテゴリーのアーカイブ

murave

Docker公式イメージで開発用RDBをゲットだぜ(MariaDB、Percona Server編)

まえがき

Docker公式イメージで開発用RDBをゲットだぜ(PostgreSQL編)
Docker公式イメージで開発用RDBをゲットだぜ(MySQL編)

に続き予定どおりMariaDB編をお送りします。一言で済むので結論を書くと

「公式イメージのページを読み比べたらMySQLと同じでした。ありがとうございます」

という内容です。ついでに

Docker Hub で Official縛りでMySQLを検索したらPerconaも出てきたので思い出したりした。定期的にPercona良さそうだなぁと思って名前を忘れるを繰り返す存在。
— murave (@murave) 2016年8月16日

というわけでPercona Serverの公式イメージのページも見てみたらこれもMySQLの場合と同じ扱いでしたので含めてみましたよ。

Docker for Macを使用しております。

MySQL派生RDBMS達の公式イメージ

まえがきに引用したTwitterのつぶやきにあるようにDocker HubでOfficial縛りでMySQLを検索しました。

Docker HubでOfficial縛りでMySQLを検索

この様に3件引っかかります。

MariaDBとPercona ServerはMySQL派生のRDBMSです。概要をつかみやすいかと思うページへのリンクをあげておきますね。

MariaDB – Wikipedia
MySQL 互換のDB、Percona Server を使う理由 – Qiita

個人的に、これからはMySQLよりMariaDBを使うことが多くなりそうに感じておりますし、以前からPercona Serverは良さそうだと思っていました(そして忘れる)。

これらの公式イメージのページを読み比べた所、設定用の環境変数の名称も含めてMySQLと同じで、扱いかたはほぼ同じと考えて良いのではないかと思います。

というわけで、調査はすっとばしてMySQL用に作成したファイルをベースにイメージの日本語化とDocker Composeでの起動をしてみようと思います。

基本的に変更点だけ書いていきますのでベースについては前回MySQLの記事をご参照ください。

MariaDB

OFFICIAL REPOSITORY mariadb

2016年8月中旬現在の対応バージョン。

10.1(10.1.16), 10.0 (10.0.26), 5.5(5.5.51)

デフォルトで選択されるlatestは10.1系。今回は10.1系で進めます。

日本語化されたイメージの作成

default localeを設定するDockerfileを作ります。

Dockerfile

FROM mariadb:10.1

MAINTAINER murave

RUN apt-get update && apt-get install locales -y && localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.utf8
日本語化されたイメージ、mariadb_jp:10.1を作ります。

$ docker build -t mariadb_jp:10.1 ./
確認

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mariadb_jp 10.1 3fa6fd548f5b 11 seconds ago 420.8 MB
mariadb 10.1 9a0138c02438 3 weeks ago 391.6 MB
参考に元の公式イメージも載せましたが、サイズが結構でかいですね。

Docker Compose で起動する

docker-compose.ymlを調整。ポートフォワードは23306に設定しました。

docker-compose.yml

datastore:
image: busybox
volumes:
- /var/lib/mysql
container_name: mariadb-datastore

mariadb:
image: mariadb_jp:10.1
environment:
MYSQL_ROOT_PASSWORD: mysecretpassword
MYSQL_USER: foo
MYSQL_PASSWORD: foopassword
MYSQL_DATABASE: foodb
volumes:
- ./conf.d/:/etc/mysql/conf.d
- ./initdb/:/docker-entrypoint-initdb.d
ports:
- "23306:3306"
volumes_from:
- datastore
container_name: mariadb-db
起動します。

$ docker-compose up -d
Creating mariadb-datastore
Creating mariadb-db
起動状態の確認。

$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad95de15a67a mariadb_jp:10.1 "docker-entrypoint.sh" About a minute ago Up About a minute 0.0.0.0:23306->3306/tcp mariadb-db
3515029a71b5 busybox "sh" About a minute ago Exited (0) About a minute ago mariadb-datastore
Sequel Pro を使いポートフォワードした23306にユーザーfooでfoodbデータベースに接続して初期化スクリプトが動作していることを確認できました。

クライアントコンテナでの接続用に作成したclientスクリプトを調整。

client

docker run -it --link mariadb-db:mysql --rm mariadb_jp:10.1 sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"'
接続して動作確認します。

$ ./client
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 5
Server version: 10.1.16-MariaDB-1~jessie mariadb.org binary distribution

Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| foodb |
| information_schema |
| mysql |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)

MariaDB [(none)]> select Host, User from mysql.user;
+------+------+
| Host | User |
+------+------+
| % | foo |
| % | root |
+------+------+
2 rows in set (0.00 sec)

MariaDB [(none)]> USE foodb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [foodb]> SHOW TABLES;
+-----------------+
| Tables_in_foodb |
+-----------------+
| users |
+-----------------+
1 row in set (0.00 sec)

MariaDB [foodb]> SELECT * from users;
+------+--------+
| id | name |
+------+--------+
| 1 | first |
| 1 | second |
+------+--------+
2 rows in set (0.00 sec)

MariaDB [foodb]> exit
Bye
Percona ServerOFFICIAL REPOSITORY percona

2016年8月中旬現在の対応バージョン。

5.7(5.7.13), 5.6 (5.6.32), 5.5 (5.5.51)

MariaDBと違ってMySQL準拠ですね。latestは5.7系。今回は5.7系で進めます。

日本語化されたイメージの作成

default localeを設定するDockerfileを作ります。

Dockerfile

FROM percona:5.7

MAINTAINER murave

RUN apt-get update && apt-get install locales -y && localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.utf8
日本語化されたイメージ、percona_jp:5.7を作ります。

$ docker build -t percona_jp:5.7 ./
確認

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
percona_jp 5.7 22e3746fb304 8 minutes ago 367.2 MB
percona 5.7 ed3fc6a159be 3 weeks ago 338.2 MB
こちらも参考に元の公式イメージも載せました。

Docker Compose で起動する

docker-compose.ymlを調整。ポートフォワードは33306に設定しました。

docker-compose.yml

datastore:
image: busybox
volumes:
- /var/lib/mysql
container_name: percona-datastore

percona:
image: percona_jp:5.7
environment:
MYSQL_ROOT_PASSWORD: mysecretpassword
MYSQL_USER: foo
MYSQL_PASSWORD: foopassword
MYSQL_DATABASE: foodb
volumes:
- ./conf.d/:/etc/mysql/conf.d
- ./initdb/:/docker-entrypoint-initdb.d
ports:
- "33306:3306"
volumes_from:
- datastore
container_name: percona-db
起動します。

$ docker-compose up -d
Creating percona-datastore
Creating percona-db
起動状態の確認。

$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b39630e2fd38 percona_jp:5.7 "docker-entrypoint.sh" 35 seconds ago Up 33 seconds 0.0.0.0:33306->3306/tcp percona-db
ea4c9a0a21b5 busybox "sh" 35 seconds ago Exited (0) 34 seconds ago percona-datastore
Sequel Pro を使いポートフォワードした33306にユーザーfooでfoodbデータベースに接続して初期化スクリプトが動作していることを確認できました。

クライアントコンテナでの接続用に作成したclientスクリプトを調整。

client

docker run -it --link percona-db:mysql --rm percona_jp:5.7 sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"'
接続して動作確認します。

$ ./client
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 6
Server version: 5.7.13-6 Percona Server (GPL), Release '6', Revision 'e3d58bb'

Copyright (c) 2009-2016 Percona LLC and/or its affiliates
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| foodb |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)

mysql> select Host, User from mysql.user;
+------+------+
| Host | User |
+------+------+
| % | foo |
| % | root |
+------+------+
2 rows in set (0.00 sec)
せっかくのPercona Serverなのでレスポンスタイム分布の取得をやってみましょう。

Response Time Distribution

を参考にPluginを導入して有効化します。

mysql> INSTALL PLUGIN QUERY_RESPONSE_TIME_AUDIT SONAME 'query_response_time.so';
Query OK, 0 rows affected (0.03 sec)

mysql> INSTALL PLUGIN QUERY_RESPONSE_TIME SONAME 'query_response_time.so';
Query OK, 0 rows affected (0.03 sec)

mysql> INSTALL PLUGIN QUERY_RESPONSE_TIME_READ SONAME 'query_response_time.so';
Query OK, 0 rows affected (0.03 sec)

mysql> INSTALL PLUGIN QUERY_RESPONSE_TIME_WRITE SONAME 'query_response_time.so';
Query OK, 0 rows affected (0.02 sec)

mysql> SHOW PLUGINS;
+-----------------------------+----------+--------------------+------------------------+---------+
| Name | Status | Type | Library | License |
+-----------------------------+----------+--------------------+------------------------+---------+
(長いので省略しました)
| QUERY_RESPONSE_TIME_AUDIT | ACTIVE | AUDIT | query_response_time.so | GPL |
| QUERY_RESPONSE_TIME | ACTIVE | INFORMATION SCHEMA | query_response_time.so | GPL |
| QUERY_RESPONSE_TIME_READ | ACTIVE | INFORMATION SCHEMA | query_response_time.so | GPL |
| QUERY_RESPONSE_TIME_WRITE | ACTIVE | INFORMATION SCHEMA | query_response_time.so | GPL |
+-----------------------------+----------+--------------------+------------------------+---------+
52 rows in set (0.00 sec)

mysql> SET GLOBAL query_response_time_stats = on;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * from INFORMATION_SCHEMA.QUERY_RESPONSE_TIME;
+----------------+-------+----------------+
| TIME | COUNT | TOTAL |
+----------------+-------+----------------+
| 0.000001 | 0 | 0.000000 |
| 0.000010 | 0 | 0.000000 |
| 0.000100 | 0 | 0.000000 |
| 0.001000 | 1 | 0.000271 |
| 0.010000 | 0 | 0.000000 |
| 0.100000 | 0 | 0.000000 |
| 1.000000 | 0 | 0.000000 |
| 10.000000 | 0 | 0.000000 |
| 100.000000 | 0 | 0.000000 |
| 1000.000000 | 0 | 0.000000 |
| 10000.000000 | 0 | 0.000000 |
| 100000.000000 | 0 | 0.000000 |
| 1000000.000000 | 0 | 0.000000 |
| TOO LONG | 0 | TOO LONG |
+----------------+-------+----------------+
14 rows in set (0.01 sec)
おー。取れた取れた。

初期投入データのサンプルについてsecondのidを1にしているという恥ずかしい状況に気づいたのでSequel Proで編集、ついでに3:thirdを追加してから再度レスポンスタイム分布を取得してみます。

mysql> SELECT * from INFORMATION_SCHEMA.QUERY_RESPONSE_TIME;
+----------------+-------+----------------+
| TIME | COUNT | TOTAL |
+----------------+-------+----------------+
| 0.000001 | 0 | 0.000000 |
| 0.000010 | 8 | 0.000057 |
| 0.000100 | 3 | 0.000173 |
| 0.001000 | 9 | 0.001866 |
| 0.010000 | 0 | 0.000000 |
| 0.100000 | 2 | 0.058591 |
| 1.000000 | 0 | 0.000000 |
| 10.000000 | 0 | 0.000000 |
| 100.000000 | 0 | 0.000000 |
| 1000.000000 | 0 | 0.000000 |
| 10000.000000 | 0 | 0.000000 |
| 100000.000000 | 0 | 0.000000 |
| 1000000.000000 | 0 | 0.000000 |
| TOO LONG | 0 | TOO LONG |
+----------------+-------+----------------+
14 rows in set (0.00 sec)
発行されたSequel Proから発行されたクエリ文増えました。スロークエリはないですね(あたりまえ)。

気軽に試せて良いですね。
以上、「Docker公式イメージで開発用RDBをゲットだぜ」でした。

murave

Docker公式イメージで開発用RDBをゲットだぜ(MySQL編)

まえがき

Docker公式イメージで開発用RDBをゲットだぜ(PostgreSQL編)

に続きMySQL編です。Docker for Macを使用しております。はじめるよ〜。

MySQL

Dockerの公式MySQLイメージの使い方を徹底的に解説するよ

という素敵なページがありましたのでこちらも参考にさせていただこうと思います。

公式イメージを調べる

OFFICIAL REPOSITORY mysql

2016年8月中旬現在、以下のバージョンがあるようです。

5.7(5.7.14), 5.6(5.6.32), 5.5(5.5.51)

少ない気もしますが覚悟を決めて5.5以上で行きましょう。「古いMySQL死すべし」という意思を感じます。

起動する

latestは5.7系のようですので、あえて5.5系ですすめてみます。

$ docker run -d --name mysql55 -e MYSQL_ROOT_PASSWORD=mysecretpassword -p 13306:3306 mysql:5.5
公式の例を参考にコンテナの3306を13306にポートフォワードしています。

接続する

mysqlコマンドで接続します。公式の例はクライアントコンテナでenvの情報を利用して一気につなぐ方法ですね。

$ docker run -it --link mysql55:mysql --rm mysql:5.5 sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"'
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.5.51 MySQL Community Server (GPL)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)

mysql> select Host, User from mysql.user;
+------+------+
| Host | User |
+------+------+
| % | root |
+------+------+
1 row in set (0.00 sec)

mysql> exit
Bye

便利だけど長い〜。シェルスクリプトにでもしておくのがよいでしょうか。clientというファイルに書いて実行権限を付けておきます。

$ ./client
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.5.51 MySQL Community Server (GPL)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
おけおけ。PostgreSQLの方にも作っておこうかな。

次に Sequel Pro 先生で繋いでみます。13306にポートフォワードしてますので

Sequelで接続(接続情報)

サクッと繋がりました。

Sequelで操作

mysqlデータベースの中など覗いてみたり。楽ちん。

設定ファイルなど

コンテナのシェルを起動して探検してみます。

$ docker exec -it mysql55 bash
とりあえずmy.cnfを眺めてみる。

root@688eab40f1a8:/# more /etc/mysql/my.cnf
[mysqld]
skip-host-cache
skip-name-resolve
datadir = /var/lib/mysql
!includedir /etc/mysql/conf.d/
/etc/mysql/conf.d/ のcnfファイルを読み込むようになっていますね。

拡張する

いろいろな方法が用意されているようです。

まず、先ほど調べたようにコンテナの /etc/mysql/conf.d/ にcnfファイルを置くと読み込まれます。

PostgreSQLの公式イメージと同じくコンテナの /docker-entrypoint-initdb.d に拡張子がsh、sql、sql.gzのファイルを置くとコンテナ起動時に実行してくれます。mysql/5.5/docker-entrypoint.shを眺めてみたところ、

for f in /docker-entrypoint-initdb.d/*; do
case "$f" in
*.sh) echo "$0: running $f"; . "$f" ;;
*.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
*) echo "$0: ignoring $f" ;;
esac
echo
done
このように仕組みも同じ感じでした。

他、PostgreSQLの時と同じように日本語化されたイメージを作ろうと思います。

日本語化されたイメージの作成

PostgreSQLでのやり方を参考にdefault localeを設定するDockerfileを作ります。

Dockerfile

FROM mysql:5.5

MAINTAINER murave

RUN apt-get update && apt-get install locales -y && localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.utf8
公式イメージにはlocalesが入っていなかったのでlocaledefの前にapt-getで導入するようにしました。

日本語化されたイメージ、mysql_jp:5.5を作ります。

$ docker build -t mysql_jp:5.5 ./
確認

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql_jp 5.5 42465a1c94d5 22 minutes ago 284.5 MB
設定ファイルの調整/etc/mysql/conf.d/ にバインドするディレクトリconf.dを作成して追加の設定を書いたcnfファイルを置きます。

conf.d/custom.cnf

[mysqld]
character-set-server=utf8
Mac上のcond.dディレクトリを/etc/mysql/conf.dにバインドするようにオプションを付けて起動します。

$ docker run -d --name mysql55 -e MYSQL_ROOT_PASSWORD=mysecretpassword -p 13306:3306 -v $(pwd)/conf.d/:/etc/mysql/conf.d mysql_jp:5.5
execでコンテナ上の/etc/mysql/conf.dのls結果を見てバインドされているか確認。

$ docker exec -it mysql55 ls /etc/mysql/conf.d
custom.cnf
おけおけ。

データベースの初期化

アカウント、データベース作成は設定用に準備された環境変数(MYSQL_USER、MYSQL_PASSWORD、MYSQL_DATABASE)を利用し、/docker-entrypoint-initdb.d ディレクトリのスクリプト実行を利用してテーブル作成などをしてみたいと思います。

ユーザーをfoo、データベースをfoodbとする前提で初期化用のsqlファイルを作ります。
initdb/01createtable.sql

CREATE TABLE foodb.users (id int , name varchar(255));
GRANT ALL ON foodb.users TO foo;
INSERT INTO foodb.users (id, name) VALUES (1, 'first'), (1, 'second')
foodbデータベースにusersテーブルを作成しユーザーfooに権限を与え、usersテーブルに初期値をINSERT文で投入というシナリオです。

/docker-entrypoint-initdb.d をバインドして起動。

$ docker run -d --name mysql55 -e MYSQL_DATABASE=foodb -e MYSQL_USER=foo -e MYSQL_PASSWORD=foopassword -e MYSQL_ROOT_PASSWORD=mysecretpassword -p 13306:3306 -v $(pwd)/conf.d/:/etc/mysql/conf.d -v $(pwd)/initdb/:/docker-entrypoint-initdb.d mysql_jp:5.5
確認します。

$ ./client
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.5.51 MySQL Community Server (GPL)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| foodb |
| mysql |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)

mysql> select Host, User from mysql.user;
+------+------+
| Host | User |
+------+------+
| % | foo |
| % | root |
+------+------+
2 rows in set (0.00 sec)

mysql> USE foodb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> SHOW TABLES;
+-----------------+
| Tables_in_foodb |
+-----------------+
| users |
+-----------------+
1 row in set (0.01 sec)

mysql> SELECT * from users;
+------+--------+
| id | name |
+------+--------+
| 1 | first |
| 1 | second |
+------+--------+
2 rows in set (0.00 sec)

mysql> exit
Bye
想定通りに初期化されているようです。

スクリプト実行についての悲しい補足情報

はじめはsqlファイルではなくshファイル(シェルスクリプト)内でmysqlコマンドを呼び出して初期化しようとしていました。実際に使用するユーザーでテーブル作成やデータ投入したいじゃないですか。気分的に。

initdb/01createtable.sh

!/bin/bash
set -e

mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" $MYSQL_DATABASE <<-EOSQL
CREATE TABLE users (id int , name varchar(255));
EOSQL
このようなスクリプトでテーブル作成は行われるのですがshスクリプト実行後にコンテナがExitしてしまうという症状が発生。解決出来ずにハマりまくり、sqlファイルでの初期化に方針変更しました。

その後、落ち着いて公式イメージのページを見なおしていると

No connections until MySQL init completes

という項目があるのに気付きました。mysqlコマンドで接続しているのが原因っぽいですね。

Docker Compose で開発用の MySQL を起動する

日本語化したイメージを使用しconf.dのcnfファイルで設定調整、initdbのスクリプトで初期化、データの永続化はbusyboxを使用する方針でdocker-compose.ymlを作成します。

docker-composeでmysql & postgreSQL をサクッと起動

を参考にさせていただきました。

docker-compose.yml

datastore:
image: busybox
volumes:
- /var/lib/mysql
container_name: mysql-datastore

mysql:
image: mysql_jp:5.5
environment:
MYSQL_ROOT_PASSWORD: mysecretpassword
MYSQL_USER: foo
MYSQL_PASSWORD: foopassword
MYSQL_DATABASE: foodb
volumes:
- ./conf.d/:/etc/mysql/conf.d
- ./initdb/:/docker-entrypoint-initdb.d
ports:
- "13306:3306"
volumes_from:
- datastore
container_name: mysql-db
起動します。

$ docker-compose up -d
Creating mysql-datastore
Creating mysql-db
以降、使用するあいだはstop・startで停止・起動。

mysqlコマンドでの接続用のclientスクリプトのコンテナ名をmysql-dbに修正して接続して動作を確認。
Sequel Pro を使いポートフォワードした13306にユーザーfooでfoodbデータベースに接続しての操作もできました。

ディレクトリファイル構成は以下のようになりました。

.
├── Dockerfile
├── client
├── conf.d
│   └── custom.cnf
├── docker-compose.yml
└── initdb
    └── 01createtable.sql
mine

OpenCOBOLとファイル操作(弊社拡張)

OpenCOBOLからのファイル操作ですが内部での定義によって幾つか種類があります。

  1. 固定長レコードのシーケンシャルファイル
  2. 固定長レコードのISAM形式ファイル
  3. 可変長レコードのシーケンシャルファイル
  4. 他

これらはCOBOLソース内でのSELECT句での定義でファイル名を直接または環境変数を経由して間接的に指定することが可能です。OpenCOBOLの素の状態ですと

SELECT ADBF0320 ASSIGN TO "FILE0001"
ORGANIZATION SEQUENTIAL
ACCESS MODE SEQUENTIAL.
と記述されている場合は、FILE0001または環境変数 DD_FILE0001または dd_FILE0001に設定されているファイル名のファイルのOPENが可能です。弊社ではこの環境変数渡しの機能を活用してperlからOpenCOBOL側へJCL中で使用しているファイル名を渡しています。
さて、JCL中ではSYSINと呼ばれる形式でファイルを作らずにその場で渡したいデータを記述することがあります。

\INPUT ACCEPT1,TYPE=DATASSF,LIST=YES
4241122 登録データ1
4241122 登録データ1追加分
\ENDINPUT;

ADAM2200:
\STEP PROG2000 FILE=USL.CAT1 DUMP=DATA SUBLM=NORMAL;
\ASSIGN FILE0010 USR.F001 SHARE=ALL HOLDMODE=NO;
\ASSIGN FILE0110 USR.F011-T FILESTAT=TEMP PUBLIC NORMAL=PASS;
\ALLOCATE FILE0110 USR.F011-T SIZE=05;
\DEFINE FILE0110 RECSIZE=57 BLOCKSZ=10260 INCRSZ=01
RELSP RECFORM=FB;
\ASSIGN SIN ACCEPT1 FILESTAT=SYSIN;
\ENDSTEP;
上記ではJCL中で定義されたASSIGN1というSYSINの内容をSINというファイル識別名に割り当てています。これをperlに置き換える(この部分自動的に処理しています)と

INPUT "ACCEPT1,TYPE=DATASSF,LIST=YES",<<_EOT;
4241122 登録データ1
4241123 登録データ1追加分
_EOT
ENDINPUT;

ADAM2200:
STEP "PROG2000 FILE=USL.CAT1 DUMP=DATA SUBLM=NORMAL";
ASSIGN "FILE0010 USR.F001 SHARE=ALL HOLDMODE=NO";
ASSIGN "FILE0110 USR.F011-T FILESTAT=TEMP PUBLIC NORMAL=PASS";
ALLOCATE "FILE0110 USR.F011-T SIZE=05";
DEFINE "FILE0110 RECSIZE=57 BLOCKSZ=10260 INCRSZ=01",
"RELSP RECFORM=FB";
ASSIGN "SIN ACCEPT1 FILESTAT=SYSIN";
上記のように変換しています。さて、SYSINの内容ですがまず1レコードが何byteであるという情報がありません。そして1行毎に行の長さが異なっています。今回移植の対象となった対象機のCOBOLではこのような場合には「改行区切りで1レコード」とするようになっていました。つまり可変長レコードです。ところがOpenCOBOLで可変長レコードをファイルとして扱うためには:
レコード先頭1バイトまたは2バイトにレコード長+1レコード分のデータ
レコード先頭1バイトまたは2バイトにレコード長+1レコード分のデータ
・・・
という形式でデータを作成する必要があります(つまり1byte目がレコード長として正しくないと、メモリ上に過大な長さのデータが読み込まれて、あっという間にSegfault します)。OpenCOBOLの外側からファイルの形式について何らかの方法で指示を出す必要が在りましたので、弊社ではファイル名の先頭に「sysin://」という識別子を(URI的に)付けてファイル名を渡すようにしています。これをOpenCOBOL内のファイルハンドラに渡る前に処理し、改行区切りの可変長レコードとして処理しています。同じく、印刷用の中間データなど1行の長さが可変長となる場合について「sysout://」という識別子を付けて指定することができるようにしています。他、標準の固定長レコードのドライバと動作をちょっと変えたドライバを使いたい場合を考え「misam://」や「mseq://」さらにLinux他では/dev/nullに該当するものとして「nullfs://」という識別子を指定可能としています。
標準の固定長レコードのドライバと動作をちょっと変えたいというのは例えばレコード挿入、削除時の細かい振る舞い、二次キー指定時の動作、二次キーを持っているISAMファイルを主キーしか定義していないCOBOLソースから書き込みモードで開いた場合の動作(OpeCOBOLの標準の動作では、書き込みモードでISAMファイルを開くと、一旦削除されますので、最悪二次キーについての定義が欠落します)等々です。
また、「perlfs://CLASSNAME/param」という形式でファイル名を渡す事によりファイルハンドラとしてperlにて記述したものを呼び出すようにもしています。DBとCOBOL内の固定長レコードの編集用コードについてperlで記述できるため、大変柔軟にDBとの連携を図れるようになりました(つまり、DBD::PgやDBD:MySQL、Oracleなどとの連携も可能です。MySQLについては既に運用されていますし、KeyValue系のDBへの接続もそれほどの変更なしに実装できます)。
上記に加え、固定長レコードやキー定義などの情報を別ディレクトリ内の管理ファイルに登録しておくことで、ファイルオープン時に正しい形式のファイルを使用しているかどうかCOBOLプログラム内の定義と照らし合わせて動的にチェックできるようになり、また現在どのようなファイルがオープン状態であるか?を全てモニタできるようにしています。
これらの改造はOpenCOBOLがオープンソースとして配布されていたことで可能になりました。成果は随時コミュニティ等にフィードバックしていきたいと考えております。

murave

恥ずかしながら PHP + MySQL + シフトJIS でハマりました

「今更こんなことやっちゃうとはなぁ」というたぐいの話です。

mysql_query(“SET NAMES ‘CP932′”, $conn);

とかやっちゃうとMySQLには文字コードを教えてるけどPHPには伝わらないので

mysql_set_charset(‘CP932’);

としてPHPにも教えてあげなさいとバグるぜって話。セキュリティホールにもなります。

シフトJISには文字コード中に\(5C)が含まれる文字があるのでPHPさんがUTF8気分で mysql_real_escape_string すると\が挿入されてバグるわけです。

昔、Rails関係でも SET NAMES ダメ絶対って話を読んだような。UTF8ばかり使っててボケたかね。

詳しくは『SET NAMESが危険な理由のおさらい』に簡潔にまとめてくださってるのでそちらをどうぞ。

yoshida

mysqld_multiを使って同一マシンに複数のMySQLサーバを立てる

業務で複数MySQLの開発環境が必要になりました。VM作っても良かったのですが、いちいち立ち上げたくなかったのと、Mac1台で完結させたかったのでTwitterでその旨つぶやいたら、とある方からmysqld_multiというものを教えて頂きました。

私はHomebrewでMySQL5.1を入れていたのですが、

$ ll /usr/local/Cellar/mysql51/5.1.58/bin/ | grep mysqld_multi
-r-xr-xr-x 1 dataich staff 24334 11 1 14:17 mysqld_multi

おお、既に入ってました。

でmy.cnfに設定を書いてあげます。[mysqld1][mysqld2][mysqld3]という感じでオプショングループを書いてあげてそれぞれの設定を記述します。

この状態で

$ mysqld_multi start

とすれば全部立ち上がります。
またマニュアルにもありますが、特定のサーバだけ立ち上げることも可能です。

なんで今まで知らなかったのかと後悔してます。
まあアプリ側がちゃんとポート指定に対応していないと残念なことにはなりますが、非常に便利に使えそうです。
ちなみにWindows開発環境では、多分別の社員さんが書いてくれると思います。