docker-composeでopensource COBOL+Postgresql開発環境を作る

# 概要

コミュニティから、docker-composeを使った開発環境がgithubで公開されていたので、それを利用してみました。
[opensource-cobol-devel](https://github.com/opensourcecobol/opensource-cobol-devel)

COBOLと埋め込みSQLを利用したDBアクセス環境が簡単に手に入れられるはずです。

# docker-composeを準備

docker-composeをあらかじめ準備する必要があります。
私は以下の記事を参考にさせていただきました。
[本家マニュアル](https://docs.docker.com/compose/install/)
[Docker Compose - インストール](https://qiita.com/zembutsu/items/dd2209a663cae37dfa81)

# サンプルを動かしてみる

基本的にはgithubのREADME.mdを参考に実行しています。

まずはリポジトリを持ってきます。

```
[user@localhost work-docker]$ git clone https://github.com/opensourcecobol/opensource-cobol-devel.git
Cloning into 'opensource-cobol-devel'...
remote: Enumerating objects: 13, done.
remote: Counting objects: 100% (13/13), done.
remote: Compressing objects: 100% (11/11), done.
remote: Total 13 (delta 1), reused 9 (delta 1), pack-reused 0
Unpacking objects: 100% (13/13), done.
[user@localhost work-docker]$ cd opensource-cobol-devel/
[user@localhost opensource-cobol-devel]$ ls
LICENSE  README.md  cpy  docker-compose.yml  src
[user@localhost opensource-cobol-devel]$
```

コンテナを起動します。この際、`-b`オプションでバックエンド実行にしています。

```
[user@localhost opensource-cobol-devel]$ docker-compose up -d
Creating opensourcecoboldevel_db_1 ...
Creating opensourcecoboldevel_oscobol_1 ...
Creating opensourcecoboldevel_db_1
Creating opensourcecoboldevel_db_1 ... done
[user@localhost opensource-cobol-devel]$ docker ps
CONTAINER ID        IMAGE                              COMMAND                  CREATED             STATUS              PORTS               NAMES
426febb415ad        opensourcecobol/opensource-cobol   "/bin/bash"              23 seconds ago      Up 18 seconds                           opensourcecoboldevel_oscobol_1
eaa05a81fdb5        postgres:9.6                       "docker-entrypoint.s…"   23 seconds ago      Up 18 seconds       5432/tcp            opensourcecoboldevel_db_1
```

COBOL用のコンテナ`opensourcecoboldevel_oscobol_1`と、Postgresql用のコンテナ`opensourcecoboldevel_db_1`が起動しました。

続いて、Postgresqlにテスト用のDBを作成します。docoker-composeのexecをつかってcreatedbを実行します。

```
[user@localhost opensource-cobol-devel]$ docker-compose exec db createdb -U postgres testdb
[user@localhost opensource-cobol-devel]$ docker-compose exec db psql -U postgres
psql (9.6.12)
Type "help" for help.

postgres=# \l
                                 List of databases
   Name    |  Owner   | Encoding |  Collate   |   Ctype    |   Access privileges

-----------+----------+----------+------------+------------+--------------------
---
 postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
 template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres
  +
           |          |          |            |            | postgres=CTc/postgr
es
 template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres
  +
           |          |          |            |            | postgres=CTc/postgr
es
 testdb    | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
(4 rows)

postgres=# \q
[user@localhost opensource-cobol-devel]$
```

正しくtestdbを作成することができました。

この後は、サンプルのCOBOLプログラムを実行し、DBアクセスを行います。
手順通りコンパイルを進めると`INSERTTBL.so`と`FETCHTBL.so`ができます。

```
[user@localhost opensource-cobol-devel]$ docker-compose exec oscobol /bin/bash
[root@426febb415ad oscobol]# ocesql src/INSERTTBL.cbl INSERTTBL.pre
precompile start: src/INSERTTBL.cbl
=======================================================
              LIST OF CALLED DB Library API
=======================================================
Generate:OCESQLConnect
Generate:OCESQLExec
Generate:OCESQLExec
Generate:OCESQLExec
Generate:OCESQLExec
Generate:OCESQLExecParams
Generate:COMMIT
Generate:OCESQLDisconnect
Generate:ROLLBACK
=======================================================
[root@426febb415ad oscobol]# cobc -locesql INSERTTBL.pre
[root@426febb415ad oscobol]# ocesql src/FETCHTBL.cbl FETCHTBL.pre
precompile start: src/FETCHTBL.cbl
=======================================================
              LIST OF CALLED DB Library API
=======================================================
Generate:OCESQLConnect
Generate:OCESQLExecSelectIntoOne
Generate:OCESQLCursorDeclare
Generate:OCESQLCursorOpen
Generate:OCESQLCursorFetchOne
Generate:OCESQLCursorFetchOne
Generate:OCESQLCursorClose
Generate:COMMIT
Generate:OCESQLDisconnect
Generate:ROLLBACK
=======================================================
[root@426febb415ad oscobol]# cobc -locesql FETCHTBL.pre
[root@426febb415ad oscobol]# ls
FETCHTBL.pre  FETCHTBL.so  INSERTTBL.pre  INSERTTBL.so  cpy  src
```

実際に実行してみます。

```
[root@426febb415ad oscobol]# cobcrun INSERTTBL
*** INSERTTBL STARTED ***
NOTICE:  table "emp" does not exist, skipping
*** INSERTTBL FINISHED ***
[root@426febb415ad oscobol]# cobcrun FETCHTBL
*** FETCHTBL STARTED ***
TOTAL RECORD: 0012
---- -------------------- ------
NO   NAME                 SALARY
---- -------------------- ------
0001 HOKKAI TARO             400
0002 AOMORI JIRO             350
0003 AKITA SABURO            300
0004 IWATE SHIRO            -250
0005 MIYAGI GORO            -200
0006 FUKUSHIMA RIKURO        150
0007 TOCHIGI SHICHIRO       -100
0008 IBARAKI HACHIRO          50
0009 GUMMA KURO             -200
0010 SAITAMA JURO            350
0046 KAGOSHIMA ROKURO       -320
0047 OKINAWA SHICHIRO        480
*** FETCHTBL FINISHED ***
[root@426febb415ad oscobol]#
```

実行結果として、COBOLからINSETデータがFETCHで取得できているように見えます。

Postgresql側から確認します。

```
[user@localhost opensource-cobol-devel]$ docker-compose exec db psql -U postgres
psql (9.6.12)
Type "help" for help.

postgres=# \c testdb
You are now connected to database "testdb" as user "postgres".
testdb=# \dt
 public | emp  | table | postgres

testdb=# \d emp
 emp_no     | numeric(4,0)  | not null
 emp_name   | character(20) |
 emp_salary | numeric(4,0)  |

testdb=# select * from emp;
     46 | KAGOSHIMA ROKURO     |       -320
     47 | OKINAWA SHICHIRO     |        480
      1 | HOKKAI TARO          |        400
      2 | AOMORI JIRO          |        350
      3 | AKITA SABURO         |        300
      4 | IWATE SHIRO          |       -250
      5 | MIYAGI GORO          |       -200
      6 | FUKUSHIMA RIKURO     |        150
      7 | TOCHIGI SHICHIRO     |       -100
      8 | IBARAKI HACHIRO      |         50
      9 | GUMMA KURO           |       -200
     10 | SAITAMA JURO         |        350

testdb=#
```

DB側からもデータが追加されていることが確認できました。

# COBOLプログラムを作ってみた

せっかくなので、サンプルプログラムを参考に、自前でプログラムを作って動かしてみます。
筆者が茨城県出身なので、IBARAKI HACHIRO氏の給料を10倍にしたいと思います。

まずはホスト側のsrcフォルダに新しいCOBOLファイルを追加します。
```
[user@localhost opensource-cobol-devel]$ touch src/UPDATETBL.cbl
```

```cobol:UPDATETBL.cbl
      ******************************************************************
       IDENTIFICATION              DIVISION.
      ******************************************************************
       PROGRAM-ID.                 UPDATETBL.
       AUTHOR.                     nor51010.
       DATE-WRITTEN.               2019-06-27.

      ******************************************************************

      ******************************************************************
       DATA                        DIVISION.
      ******************************************************************
       WORKING-STORAGE             SECTION.

       EXEC SQL BEGIN DECLARE SECTION END-EXEC.
       01  DBNAME                  PIC  X(30) VALUE SPACE.
       01  USERNAME                PIC  X(30) VALUE SPACE.
       01  PASSWD                  PIC  X(10) VALUE SPACE.
       01  EMP-REC-VARS.
         03  EMP-NO                PIC S9(04) VALUE ZERO.
         03  EMP-NAME              PIC  X(20) .
         03  EMP-SALARY            PIC S9(04) VALUE ZERO.
       EXEC SQL END DECLARE SECTION END-EXEC.

       EXEC SQL INCLUDE SQLCA END-EXEC.
      ******************************************************************
       PROCEDURE                   DIVISION.
      ******************************************************************
       MAIN-RTN.
           DISPLAY "*** UPDATETBL STARTED ***".

      *    CONNECT
           MOVE  "testdb@db"       TO   DBNAME.
           MOVE  "postgres"        TO   USERNAME.
           MOVE  SPACE             TO   PASSWD.
           EXEC SQL
               CONNECT :USERNAME IDENTIFIED BY :PASSWD USING :DBNAME 
           END-EXEC.
           IF  SQLCODE NOT = ZERO PERFORM ERROR-RTN STOP RUN.

      *    SELECT IBARAKI san INTO HOST-VARIABLE
           EXEC SQL 
               SELECT * INTO :EMP-NO,
                             :EMP-NAME,
                             :EMP-SALARY
                 FROM EMP
                 WHERE EMP_NO = 8
           END-EXEC.
           IF  SQLCODE NOT = ZERO PERFORM ERROR-RTN STOP RUN.

      *    SALARY * 10
           COMPUTE EMP-SALARY = EMP-SALARY * 10.
           
      *    UPDATE IBARAKI san
           EXEC SQL 
               UPDATE emp SET EMP_SALARY = :EMP-SALARY
                 WHERE EMP_NO = 8
           END-EXEC.
           IF  SQLCODE NOT = ZERO PERFORM ERROR-RTN STOP RUN.

      *    COMMIT
           EXEC SQL 
               COMMIT WORK
           END-EXEC.
           
      *    DISCONNECT
           EXEC SQL
               DISCONNECT ALL
           END-EXEC.

      *    END
           DISPLAY "*** UPDATETBL FINISHED ***".
           STOP RUN.

      ******************************************************************
       ERROR-RTN.
      ******************************************************************
           DISPLAY "*** SQL ERROR ***".
           DISPLAY "SQLCODE: " SQLCODE " " NO ADVANCING.
           EVALUATE SQLCODE
              WHEN  +10
                 DISPLAY "Record not found"
              WHEN  -01
                 DISPLAY "Connection falied"
              WHEN  -20
                 DISPLAY "Internal error"
              WHEN  -30
                 DISPLAY "PostgreSQL error"
                 DISPLAY "ERRCODE: "  SQLSTATE
                 DISPLAY SQLERRMC
              *> TO RESTART TRANSACTION, DO ROLLBACK.
                 EXEC SQL
                     ROLLBACK
                 END-EXEC
              WHEN  OTHER
                 DISPLAY "Undefined error"
                 DISPLAY "ERRCODE: "  SQLSTATE
                 DISPLAY SQLERRMC
           END-EVALUATE.
      ******************************************************************  
```

では実際にコンパイルして実行していきます。srcフォルダはdocker-compose.ymlの設定でCOBOLコンテナと共有されているため、コンテナに入りコマンドをたたいていきます。

```
[user@localhost opensource-cobol-devel]$ docker-compose exec oscobol /bin/bash
[root@426febb415ad oscobol]# ls src/
FETCHTBL.cbl  INSERTTBL.cbl  UPDATETBL.cbl
[root@426febb415ad oscobol]# ocesql src/UPDATETBL.cbl UPDATETBL.pre
precompile start: src/UPDATETBL.cbl
=======================================================
              LIST OF CALLED DB Library API
=======================================================
Generate:OCESQLConnect
Generate:OCESQLExecSelectIntoOne
Generate:OCESQLExecParams
Generate:COMMIT
Generate:OCESQLDisconnect
Generate:ROLLBACK
=======================================================
[root@426febb415ad oscobol]# cobc -locesql UPDATETBL.pre
[root@426febb415ad oscobol]# ls
FETCHTBL.pre  INSERTTBL.pre  UPDATETBL.pre  cpy
FETCHTBL.so   INSERTTBL.so   UPDATETBL.so   src
[root@426febb415ad oscobol]# cobcrun INSERTTBL
*** INSERTTBL STARTED ***
*** INSERTTBL FINISHED ***
[root@426febb415ad oscobol]# cobcrun UPDATETBL
*** UPDATETBL STARTED ***
*** UPDATETBL FINISHED ***
[root@426febb415ad oscobol]# cobcrun FETCHTBL
*** FETCHTBL STARTED ***
TOTAL RECORD: 0012
---- -------------------- ------
NO   NAME                 SALARY
---- -------------------- ------
0001 HOKKAI TARO             400
0002 AOMORI JIRO             350
0003 AKITA SABURO            300
0004 IWATE SHIRO            -250
0005 MIYAGI GORO            -200
0006 FUKUSHIMA RIKURO        150
0007 TOCHIGI SHICHIRO       -100
0008 IBARAKI HACHIRO         500
0009 GUMMA KURO             -200
0010 SAITAMA JURO            350
0046 KAGOSHIMA ROKURO       -320
0047 OKINAWA SHICHIRO        480
*** FETCHTBL FINISHED ***
```

これまでと同様に、DB側も確認していきます。

```
[user@localhost opensource-cobol-devel]$ docker-compose exec db psql -U postgres
psql (9.6.12)
Type "help" for help.

postgres=# \c testdb
You are now connected to database "testdb" as user "postgres".
testdb=# select * from emp ;
 emp_no |       emp_name       | emp_salary
--------+----------------------+------------
     46 | KAGOSHIMA ROKURO     |       -320
     47 | OKINAWA SHICHIRO     |        480
      1 | HOKKAI TARO          |        400
      2 | AOMORI JIRO          |        350
      3 | AKITA SABURO         |        300
      4 | IWATE SHIRO          |       -250
      5 | MIYAGI GORO          |       -200
      6 | FUKUSHIMA RIKURO     |        150
      7 | TOCHIGI SHICHIRO     |       -100
      9 | GUMMA KURO           |       -200
     10 | SAITAMA JURO         |        350
      8 | IBARAKI HACHIRO      |        500
(12 rows)

testdb=#
```
無事にIBARAKI氏の給料を上げることができました。

# まとめ

やはりコンテナ環境は偉大だなと感じられる環境です。

設定等を考えずとも、埋め込みSQLを利用したCOBOLの開発・実行環境がコマンド1発でできるため、すでに持っているCOBOLをとりあえず動かしてみるとかといった時に簡単にできそうです。

DBとの連携に関しても、COBOLのデータ型からPostgresにちゃんとデータを追加できていました。
サンプルでは給料がマイナスの社員さん達がいますが、マイナス符号の数値も問題なく扱え、一般的なCOBOLデータを扱っている埋め込みSQL付きアプリケーションはそのまま持って行けそうです。

※Postgresql以外もいけるといいな