View が存在すると SQLBoiler でのModel自動生成に失敗

# お題
職場で[SQL Boiler](https://github.com/volatiletech/sqlboiler)というのを使い始めているので、何ができるかちょっと家でも試してみようと思った。
が、表題の事象が起きたので、一応、記録。

**【前置き】**
**※GoやDockerの環境構築やSQL Boilerのインストール・コマンド使用方法などは説明しない。**
**※提示した環境下で起きた事象なので、異なる環境下で同様に起きるかは不明。**
**※対象データベースは、[PostgreSQL](https://www.postgresql.org)です。**

# 環境
## # OS - Linux(Ubuntu)
```
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS"
```

## # 言語 - Go
```
$ go version
go version go1.13.9 linux/amd64
```

## # ツール - SQL Boiler
```
$ sqlboiler --version
SQLBoiler v3.6.1
```

## # DB - PostgreSQL (from Docker Image)
```
$ cat db/docker-compose.yml | grep image
    image: postgres:12-alpine
```

## # Docker
```
$ sudo docker version
Client: Docker Engine - Community
 Version:           19.03.8
 API version:       1.40
 Go version:        go1.12.17
 Git commit:        afacb8b7f0
 Built:             Wed Mar 11 01:25:46 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.8
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.17
  Git commit:       afacb8b7f0
  Built:            Wed Mar 11 01:24:19 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
```

## # Docker Compose
```
$ docker-compose version
docker-compose version 1.25.4, build 8d51620a
docker-py version: 4.1.0
CPython version: 3.7.5
OpenSSL version: OpenSSL 1.1.0l  10 Sep 2019
```

# 実践
## 対象データベース
適当に作った２〜３テーブルを相手にしても[SQL Boiler](https://github.com/volatiletech/sqlboiler)の真価はわからないだろうと思ったので、[PostgreSQL Sample Database](https://www.postgresqltutorial.com/postgresql-sample-database/)というのを使った。
15テーブルあり、リレーションもある程度貼ってある。
DVDレンタルのモデルらしい。
![actor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180599/961a8f4d-2d4d-1eeb-9187-2503bf7bdb65.png)
**※[DataGrip](https://www.jetbrains.com/datagrip/)によるDiagram生成**

## Model自動生成
こんな感じで必要最低限の定義だけしておく。
※あくまでローカル環境のDocker内Postgresが相手なのでパスワードも生ざらし。

```
$ cat sqlboiler.toml 
[psql]
  host   = "localhost"
  port   = 11311
  sslmode= "disable"
  schema = "public"
  dbname = "dvdrental"
  user   = "postgres"
  pass   = "localpass"
```

ちなみに、Postgresコンテナ起動用の[Docker Compose](http://docs.docker.jp/compose/toc.html)定義は↓

```
$ cat db/docker-compose.yml 
version: '3'

services:
  db:
    restart: always
    image: postgres:12-alpine
    container_name: study-sql-boiler-db-container
    ports:
      - "11311:5432"
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=localpass
      - PGPASSWORD=localpass
      - POSTGRES_DB=dvdrental
      - DATABASE_HOST=localhost
    volumes:
      - ./docker/db/init:/docker-entrypoint-initdb.d
sky0621@sky0621-W950JU:~/work/src/github.com/sky0621/study-sql-boiler$ 
```

いざ、Model自動生成。

```
$ sqlboiler psql
Error: unable to initialize tables: primary key missing in tables (actor_info, customer_list, film_list, nicer_but_slower_film_list, sales_by_film_category, sales_by_store, staff_list)
```

あれ？
『PKがないテーブルがあると自動生成失敗』なのか・・・。
ちゃんと仕様見よう。
https://github.com/volatiletech/sqlboiler#requirements
結合テーブルに複合PKが必要というのはわかった。
コマンド実行時のエラーメッセージからして、結合テーブルでなくてもPKが必要そうではある。
よくよく見ると、以下にそうと判断できる記述はあった。
https://github.com/volatiletech/sqlboiler#diagnosing-problems
「`Tables without a primary key. All tables require one.`」

ただ、対象となるDVDレンタルデータベースにあるテーブルは全てPK持っている。。。じゃあ、なんでエラー？

と思ったら、エラーメッセージに載ってる「`actor_info, customer_list, film_list, nicer_but_slower_film_list, sales_by_film_category, sales_by_store, staff_list`」はテーブル名じゃない。
ビューの名前だった。
![actor_info.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180599/8838f2dc-6d84-2036-f362-3adcc510e270.png)
**※[DataGrip](https://www.jetbrains.com/datagrip/)によるDiagram生成**

まあ、PKないよね。。。
というか、じゃあ、ビュー含んでたら、Model自動生成できないのか、SQL Boiler？

## View以外で自動生成
[SQL Boilerの設定方法](https://github.com/volatiletech/sqlboiler#configuration)を見ると、`blacklist` に記載があるものは無視する様子。
というわけでブラックリストにビューの名前を列挙。

```
$ cat sqlboiler.toml 
[psql]
  〜〜　省略　〜〜
  blacklist = ["actor_info", "customer_list", "film_list", "nicer_but_slower_film_list", "sales_by_film_category", "sales_by_store", "staff_list"]
```

リトライ。

```
$ sqlboiler psql
$
```

今度は何事もなくコマンド実行終了。
Modelは、できてる？

```
$ ll models/
合計 1.1M
-rw-rw-r-- 1 sky0621 sky0621  31K  4月 11 22:28 actor.go
-rw-rw-r-- 1 sky0621 sky0621  22K  4月 11 22:28 actor_test.go
-rw-rw-r-- 1 sky0621 sky0621  44K  4月 11 22:28 address.go
-rw-rw-r-- 1 sky0621 sky0621  33K  4月 11 22:28 address_test.go
-rw-rw-r-- 1 sky0621 sky0621 2.4K  4月 11 22:28 boil_main_test.go
-rw-rw-r-- 1 sky0621 sky0621  868  4月 11 22:28 boil_queries.go
-rw-rw-r-- 1 sky0621 sky0621 1.1K  4月 11 22:28 boil_queries_test.go
-rw-rw-r-- 1 sky0621 sky0621  18K  4月 11 22:28 boil_suites_test.go
-rw-rw-r-- 1 sky0621 sky0621  927  4月 11 22:28 boil_table_names.go
-rw-rw-r-- 1 sky0621 sky0621 1.4K  4月 11 22:28 boil_types.go
-rw-rw-r-- 1 sky0621 sky0621  29K  4月 11 22:28 category.go
-rw-rw-r-- 1 sky0621 sky0621  23K  4月 11 22:28 category_test.go
-rw-rw-r-- 1 sky0621 sky0621  32K  4月 11 22:28 city.go
-rw-rw-r-- 1 sky0621 sky0621  25K  4月 11 22:28 city_test.go
-rw-rw-r-- 1 sky0621 sky0621  29K  4月 11 22:28 country.go
-rw-rw-r-- 1 sky0621 sky0621  23K  4月 11 22:28 country_test.go
-rw-rw-r-- 1 sky0621 sky0621  41K  4月 11 22:28 customer.go
-rw-rw-r-- 1 sky0621 sky0621  30K  4月 11 22:28 customer_test.go
-rw-rw-r-- 1 sky0621 sky0621  46K  4月 11 22:28 film.go
-rw-rw-r-- 1 sky0621 sky0621  33K  4月 11 22:28 film_actor.go
-rw-rw-r-- 1 sky0621 sky0621  25K  4月 11 22:28 film_actor_test.go
-rw-rw-r-- 1 sky0621 sky0621  34K  4月 11 22:28 film_category.go
-rw-rw-r-- 1 sky0621 sky0621  26K  4月 11 22:28 film_category_test.go
-rw-rw-r-- 1 sky0621 sky0621  33K  4月 11 22:28 film_test.go
-rw-rw-r-- 1 sky0621 sky0621  33K  4月 11 22:28 inventory.go
-rw-rw-r-- 1 sky0621 sky0621  26K  4月 11 22:28 inventory_test.go
-rw-rw-r-- 1 sky0621 sky0621  29K  4月 11 22:28 language.go
-rw-rw-r-- 1 sky0621 sky0621  23K  4月 11 22:28 language_test.go
-rw-rw-r-- 1 sky0621 sky0621  37K  4月 11 22:28 payment.go
-rw-rw-r-- 1 sky0621 sky0621  28K  4月 11 22:28 payment_test.go
-rw-rw-r-- 1 sky0621 sky0621 5.4K  4月 11 22:28 psql_main_test.go
-rw-rw-r-- 1 sky0621 sky0621  851  4月 11 22:28 psql_suites_test.go
-rw-rw-r-- 1 sky0621 sky0621 1.6K  4月 11 22:28 psql_upsert.go
-rw-rw-r-- 1 sky0621 sky0621  41K  4月 11 22:28 rental.go
-rw-rw-r-- 1 sky0621 sky0621  32K  4月 11 22:28 rental_test.go
-rw-rw-r-- 1 sky0621 sky0621  43K  4月 11 22:28 staff.go
-rw-rw-r-- 1 sky0621 sky0621  32K  4月 11 22:28 staff_test.go
-rw-rw-r-- 1 sky0621 sky0621  32K  4月 11 22:28 store.go
-rw-rw-r-- 1 sky0621 sky0621  25K  4月 11 22:28 store_test.go
```

できてる。
というわけで、一応、ビューがあっても、`blacklist`に記載しておけば、自動生成でエラーが起きることはない。
のだけど、、、つどつどビューを列挙していくのって。。。。
あと、ビューに対してクエリ投げたい時は、SQL BoilerのModelを使ったコードは書けないということなのかな。。。
この手のORMが大抵備えているように、[Raw Query](https://github.com/volatiletech/sqlboiler#raw-query)を流すことはできるようなので、ビューを相手にするときはRaw Queryを使うということか。
う〜む。

# 後日談
[PostgreSQL Sample Database](https://www.postgresqltutorial.com/postgresql-sample-database/)で提供されているスキーマを使ったのだけど、SQL Boilerによるソース自動生成は正常終了したものの、Golangのソース的に実行するとエラーが起きる状態で自動生成されていた。。。

【エラー内容】

```
../models/actor.go:518:21: invalid operation: local.ActorID == foreign.ActorID (mismatched types int and int16)
../models/actor.go:540:16: cannot use o.ActorID (type int) as type int16 in assignment
../models/actor.go:561:16: cannot use o.ActorID (type int) as type int16 in assignment
../models/address.go:596:20: invalid operation: local.CityID == foreign.CityID (mismatched types int16 and int)
../models/address.go:691:23: invalid operation: local.AddressID == foreign.AddressID (mismatched types int and int16)
../models/address.go:786:23: invalid operation: local.AddressID == foreign.AddressID (mismatched types int and int16)
../models/address.go:881:23: invalid operation: local.AddressID == foreign.AddressID (mismatched types int and int16)
../models/address.go:922:11: cannot use related.CityID (type int) as type int16 in assignment
../models/address.go:950:18: cannot use o.AddressID (type int) as type int16 in assignment
../models/address.go:971:18: cannot use o.AddressID (type int) as type int16 in assignment
../models/address.go:971:18: too many errors
```

【原因】
例えば、`actor`テーブルと`film`テーブルのPKの型が`integer`であるのに対し、両者の結合テーブルである`film_actor`テーブルはの複合PKの型は`smallint`であり、異なっている。
そのため、自動生成されたGolangのソースで、一方が `int` 型で定義された要素、もう一方が `int16` 型で定義された要素となり、結合テーブルゆえ（？）に「`if local.ActorID == foreign.ActorID {`」なんてコードがありビルドエラー。

今回は、たまたま使ったサンプルDBがそういうようになっているための事象だったけど、自前でテーブル定義していく時にも下手すると起こりうるなぁと。。。

とりあえず、ひたすら `smallint` -> `integer` に直す。（念のため、修正後のERも載せる。）
![actor2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180599/3328c615-3505-86df-8883-4fc0af54c5f1.png)

そして、再度、Modelを生成。
そうしたら、（DBが起動した状態で）例えば以下のようなコードを実行すると標準出力に `actor` テーブルの中身がずらずらと吐かれた。

```go:main.go
package main

import (
	"context"
	"database/sql"
	"fmt"

	"github.com/sky0621/study-sql-boiler/models"

	_ "github.com/lib/pq"
)

func main() {
	db, err := sql.Open("postgres", "dbname=dvdrental user=postgres password=localpass sslmode=disable port=11311")
	if err != nil {
		panic(err)
	}

	ctx := context.Background()

	actors, err := models.Actors().All(ctx, db)
	if err != nil {
		panic(err)
	}
	for _, actor := range actors {
		fmt.Println(actor)
	}

}
```