LoginSignup
2
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-04-11

お題

職場でSQL Boilerというのを使い始めているので、何ができるかちょっと家でも試してみようと思った。
が、表題の事象が起きたので、一応、記録。

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

環境

# 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

実践

対象データベース

適当に作った2〜3テーブルを相手にしてもSQL Boilerの真価はわからないだろうと思ったので、PostgreSQL Sample Databaseというのを使った。
15テーブルあり、リレーションもある程度貼ってある。
DVDレンタルのモデルらしい。
actor.png
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定義は↓

$ 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
DataGripによるDiagram生成

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

View以外で自動生成

SQL Boilerの設定方法を見ると、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を流すことはできるようなので、ビューを相手にするときはRaw Queryを使うということか。
う〜む。

後日談

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

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

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)
    }

}
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0