はじめに

前回の記事の続きです。
前回の記事はこちら

PostgreSQLデータベースの使用

今回は PostgreSQL のデータベースをGoで使用する手順についてです。
前回と同様の環境を使用します。

PostgreSQLはフリーなオブジェクト-リレーショナルデータベースサーバ(データベース管理システム)です。これは活発なBSDライクなライセンスで公開されています。
他のオープンソースなデータベースシステム(MySQLやFirebird)やOracle、Sybase、IBMのDB2やMicrosoft SQL Serverといったプロプライエタリなシステムに対する選択肢の一つです。

なるほど。

PostgreSQLとMySQLを比較すると、これは少々巨大です。これはOracleの代替として設計されているためです。そのため、企業のアプリケーションではPostgreSQLを選択することが賢い選択の一つとなっています。

MySQLはOracleに買収され、現在徐々にクローズされつつあります。(MySQL 5.5.31以降のすべてのバージョンがGPLライセンスを順守していません)。
これに鑑み、将来我々もプロジェクトのバックエンドのデータベースとしてMySQLではなくPostgreSQLを選択することになるかもしれません。

DB 事情に詳しくありませんでしたが、そうなんですね。
MySQL 派生の MariaDB とかはどうなんだろう(?)
未だに 各々の DB の良し悪しがわかっていないので、今度勉強してみます。

準備

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
docker-compose up -d

##コンテナに入ります。
docker-compose exec postgres bash

bash-5.0# psql -U postgres
psql (12.1)
Type "help" for help.
postgres=#\l
                                 List of databases
   Name    |  Owner   | Encoding |  Collate   |   Ctype    |   Access privileges
-----------+----------+----------+------------+------------+-----------------------
 db        | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
 postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
 template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
           |          |          |            |            | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
           |          |          |            |            | postgres=CTc/postgres
(4 rows)

postgres=# \c db

`CREATE` 文の実行

db=# \d
                List of relations
 Schema |       Name       |   Type   |  Owner
--------+------------------+----------+----------
 public | userdeatail      | table    | postgres
 public | userinfo         | table    | postgres
 public | userinfo_uid_seq | sequence | postgres
(3 rows)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
CREATE TABLE userinfo
(
    uid serial NOT NULL,
    username character varying(100) NOT NULL,
    departname character varying(500) NOT NULL,
    Created date,
    CONSTRAINT userinfo_pkey PRIMARY KEY (uid)
)
WITH (OIDS=FALSE);

CREATE TABLE userdeatail
(
    uid integer,
    intro character varying(100),
    profile character varying(100)
)
WITH(OIDS=FALSE);

さてこれで、DB の準備はできました。

コードを書いていきましょう。

コード

GoはPostgreSQLをサポートしたドライバも非常に多く実装されています。国外では多くの人が開発でこのデータベースを使用しているためです。 https://github.com/lib/pq database/sqlドライバをサポートしています。純粋にGoで書かれています。

lib/pq を使っていくようですね。

postgresql.go で作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package main
import (
    "database/sql"
    "fmt"
    _ "github.com/lib/pq"
)

func main() {
    db, err := sql.Open("postgres", "host=postgres port=5432 user=postgres password=postgres dbname=db sslmode=disable")
    checkErr(err)

    //データの挿入
    stmt, err := db.Prepare("INSERT INTO userinfo(username,departname,created) VALUES($1,$2,$3) RETURNING uid")
    checkErr(err)

    res, err := stmt.Exec("astaxie", "研究開発部門", "2012-12-09")
    checkErr(err)

    // //pgはこの関数をサポートしていません。MySQLのインクリメンタルなIDのようなものが無いためです。
    // id, err := res.LastInsertId()
    // checkErr(err)

    // fmt.Println(id)

    //データの更新
    stmt, err = db.Prepare("update userinfo set username=$1 where uid=$2")
    checkErr(err)

    res, err = stmt.Exec("astaxieupdate", 1)
    checkErr(err)

    affect, err := res.RowsAffected()
    checkErr(err)

    fmt.Println(affect)

    //データの検索
    rows, err := db.Query("SELECT * FROM userinfo")
    checkErr(err)

    for rows.Next() {
        var uid int
        var username string
        var department string
        var created string
        err = rows.Scan(&uid, &username, &department, &created)
        checkErr(err)
        fmt.Println(uid)
        fmt.Println(username)
        fmt.Println(department)
        fmt.Println(created)
    }

    //データの削除
    stmt, err = db.Prepare("delete from userinfo where uid=$1")
    checkErr(err)

    res, err = stmt.Exec(1)
    checkErr(err)

    affect, err = res.RowsAffected()
    checkErr(err)

    fmt.Println(affect)

    db.Close()

}

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ docker-compose exec workspace bash
bash-5.0# go run postgresql.go 
1
2
astaxie
研究開発部門        
2012-12-09T00:00:00Z
1
astaxieupdate       
研究開発部門        
2012-12-09T00:00:00Z
1
bash-5.0# 

よし!できてますね。
こちらも前回と同様に魔改造しましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
docker-compose exec postgres bash
psql -U postgres
db=# TRUNCATE TABLE userinfo RESTART IDENTITY;
db=# select * from userinfo;
 uid | username | departname | created 
-----+----------+------------+---------
(0 rows)
db=# select * from userinfo_uid_seq;
 last_value | log_cnt | is_called 
------------+---------+-----------
          1 |       0 | f
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package main
import (
    "database/sql"
    "fmt"
    _ "github.com/lib/pq"
)

func main() {
    db, err := sql.Open("postgres", "host=postgres port=5432 user=postgres password=postgres dbname=db sslmode=disable")
    checkErr(err)

    //データの挿入
    stmt, err := db.Prepare("INSERT INTO userinfo(username,departname,created) VALUES($1,$2,$3) RETURNING uid")
    checkErr(err)

	var res sql.Result
	for i := 0; i < 10; i++ {
		res, err = stmt.Exec("astaxie", "研究開発部門", "2012-12-09")
		checkErr(err)
	}

    // //pgはこの関数をサポートしていません。MySQLのインクリメンタルなIDのようなものが無いためです。
    // id, err := res.LastInsertId()
    // checkErr(err)

    // fmt.Println(id)

    //データの更新
    stmt, err = db.Prepare("update userinfo set username=$1 where uid=$2")
    checkErr(err)

    res, err = stmt.Exec("Update(id:1)", 1)
    checkErr(err)

    affect, err := res.RowsAffected()
    checkErr(err)

    fmt.Println(affect)

    //データの検索
    rows, err := db.Query("SELECT * FROM userinfo")
    checkErr(err)

    for rows.Next() {
        var uid int
        var username string
        var department string
        var created string
        err = rows.Scan(&uid, &username, &department, &created)
        checkErr(err)
        fmt.Println(uid)
        fmt.Println(username)
        fmt.Println(department)
        fmt.Println(created)
    }

    //データの削除
    stmt, err = db.Prepare("delete from userinfo where uid=$1")
    checkErr(err)

    res, err = stmt.Exec(2)
    checkErr(err)

    affect, err = res.RowsAffected()
    checkErr(err)

    fmt.Println(affect)

    db.Close()

}

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
db=# select * from userinfo order by uid asc;
 uid |   username   |  departname  |  created   
-----+--------------+--------------+------------
   1 | Update(id:1) | 研究開発部門 | 2012-12-09
   3 | astaxie      | 研究開発部門 | 2012-12-09
   4 | astaxie      | 研究開発部門 | 2012-12-09
   5 | astaxie      | 研究開発部門 | 2012-12-09
   6 | astaxie      | 研究開発部門 | 2012-12-09
   7 | astaxie      | 研究開発部門 | 2012-12-09
   8 | astaxie      | 研究開発部門 | 2012-12-09
   9 | astaxie      | 研究開発部門 | 2012-12-09
  10 | astaxie      | 研究開発部門 | 2012-12-09
(9 rows)

うまく行ってますね!
postgresql でもちゃんと接続できているようですね。

上のコードによって、PostgreSQLが$1や$2といった方法によって引数を渡している様子がお分かりいただけるかとおもいます。
MySQLの中の?ではありません。
また、sql.Openではdsn情報のシンタックスがMySQLのドライバでのdsnシンタックスと異なります。そのため、使用される際はこの違いにご注意ください。
また、pgはLastInsertId関数をサポートしていません。PostgreSQLの内部ではMySQLのインクリメンタルなIDを返すといった実装がないためです。
その他のコードはほとんど同じです。

おわりに

明日は、 beedbライブラリでORM開発 です。