Docker上のgolangから Oracle接続やってみました。

#はじめに
コロナ禍で自宅勤務になったので、今更ながらDockerやgolangでも使ってみようと思い少しお勉強してみました。
Qiitaで記事書くのも初めてなので優しくしてね(汗)
仕事ではOracleが多いのでgolangからOracleに接続を試みます。

#評価環境
　ホストOS：CentOS Linux release 7.7.1908 (Core)
　Docker：Docker version 19.03.8, build afacb8b
　DB Server：Oracle Database 11g Express Edition Release 11.2.0.2.0

#Oracleクライアント
　インスタントクライアントをOTN(Oracle Technology Network)からダウンロードします。
　今回は入手できた最新の19.6を使いました。
　　instantclient-basic-linux.x64-19.6.0.0.0dbru.zip
　　instantclient-sdk-linux.x64-19.6.0.0.0dbru.zip
　　instantclient-sqlplus-linux.x64-19.6.0.0.0dbru.zip
　sqlplusは必須ではありませんが接続確認用に入れています。

#接続ライブラリ
　mattn/go-oci8 というのがあるらしいのでそれを使っています。

#実行手順
　host# はホストのrootアカウントで実行
　container# はDockerコンテナ上のrootアカウントで実行

イメージを取得して実行（デーモンで起動したいのだがExitedになってしまう）

```Bash
host# docker pull golang
host# docker run -it --name gogo golang
container# exit
```
コンテナにモジュールをコピーしてコンテナに接続

```Bash
host# docker start gogo
host# docker cp instantclient-basic-linux.x64-19.6.0.0.0dbru.zip gogo:/usr/lib
host# docker cp instantclient-sdk-linux.x64-19.6.0.0.0dbru.zip gogo:/usr/lib
host# docker cp instantclient-sqlplus-linux.x64-19.6.0.0.0dbru.zip gogo:/usr/lib
host# docker exec -it gogo /bin/bash
```
必要なモジュールをインストール後、Oracleクライアント/usr/lib配下に展開

```Bash
container# apt-get update
container# apt-get install -y vim libaio1 unzip
container# cd /usr/lib
container# unzip instantclient-basic-linux.x64-19.6.0.0.0dbru.zip
container# unzip instantclient-sdk-linux.x64-19.6.0.0.0dbru.zip
container# unzip instantclient-sqlplus-linux.x64-19.6.0.0.0dbru.zip
```
環境変数を設定（本来なら.bash_profileに書くべきですが省略）

```console
container# export LD_LIBRARY_PATH=/usr/lib/instantclient_19_6:$LD_LIBRARY_PATH
container# export NLS_LANG=JAPANESE_JAPAN.AL32UTF8
```
sqlplusで接続してみる。日本語で表示されたのでNLS_LANGもちゃんと効いている様子。

```console
container# /usr/lib/instantclient_19_6/sqlplus scott/tiger@dbhost:1521/xe
SQL*Plus: Release 19.0.0.0.0 - Production on 水 4月 22 09:24:41 2020
Version 19.6.0.0.0
Copyright (c) 1982, 2019, Oracle.  All rights reserved.
Oracle Database 11g Release 11.2.0.4.0 - 64bit Production
に接続されました。
SQL>exit
container# 
```
よく分からないがoci8.pc というファイルが必要らしいので作成

```console
container# vi /usr/lib/pkgconfig/oci8.pc
```
```:oci8.pc
prefix=/usr/lib/instantclient_19_6/
libdir=${prefix}
includedir=${prefix}sdk/include/

Name: oci8
Description: Oracle Instant Client
Version: 19.6
Libs: -L${libdir} -lclntsh
Libs.private:
Cflags: -I${includedir}
```

githubからgo-oci8を取得

```console
container# cd $GOPATH/src
container# go get github.com/mattn/go-oci8
```

動作確認用のプログラム作成

```Golang:test.go
package main

import (
	"database/sql"
	"fmt"
	"os"
	_ "github.com/mattn/go-oci8"
)

func execSQL(db *sql.DB, sql string) bool{
	_,err := db.Exec(sql)
	if err != nil {
		fmt.Println(err)
		return false
	} else {
		return true
	}
}

func main() {
	db, err := sql.Open("oci8", os.Args[1])
	if err != nil {
		fmt.Println(err)
		return
	}
	defer db.Close()
	if !execSQL(db,"create table HOGE (NUM number(2),WORD varchar2(10))"){
		return
	}
	if !execSQL(db,"insert into HOGE values (1,'One')"){
		return
	}
	if !execSQL(db,"insert into HOGE values (2,'Two')"){
		return
	}
	if !execSQL(db,"insert into HOGE values (3,'Three')"){
		return
	}

	rows ,err := db.Query("select NUM,WORD  from HOGE order by NUM")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer rows.Close()
	for rows.Next(){
		var num int 
		var word string

		rows.Scan(&num, &word)
		fmt.Println(num, word)
	}
	_,err = db.Exec("drop table HOGE")
}
```
実行してみる

```console
container# go run test.go scott/tiger@dbhost:1521/xe
1 One
2 Two
3 Three
container#
```

ということで何とか動きましたとさ。めでたし　めでたし