sqlcでSQLからGoのコードを生成する

kyleconroy/sqlcSQLからGoのコードを生成してくれるツールです。
schemaやSELECT文などからコードを生成してくれます。

今回は基本的な使い方を紹介します。

インストール

$ go install github.com/kyleconroy/sqlc/cmd/sqlc@latest

brewでもインストール可能です。

使い方

設定ファイル、schema、queryの準備

sqlcを使うために3つのファイルを準備します。

.
├── query.sql
├── schema.sql
└── sqlc.yml
# sqlc.yml

version: 1
packages:
  - path: "db"
    name: "db" # package name
    engine: "postgresql"
    schema: "schema.sql"
    queries: "query.sql"
-- schema.sql

CREATE TABLE users (
  id uuid NOT NULL PRIMARY KEY,
  name text NOT NULL
);

Get, Create, Update, Delete, List, CountのSQLを書きました。
これらのSQLからコードを生成することになります。

-- query.sql

-- name: Get :one
SELECT * FROM users
WHERE id = $1 LIMIT 1;

-- name: Create :one
INSERT INTO users (
  id, name
) VALUES (
  $1, $2
)
RETURNING *;

-- name: Update :exec
UPDATE users SET name = $2
WHERE id = $1;

-- name: Delete :exec
DELETE FROM users WHERE id = $1;

-- name: List :many
SELECT * FROM users
ORDER BY id;

-- name: Count :one
SELECT count(*) FROM users;

コードを生成する

$ sqlc generate -f sqlc.yml

db下にファイルが生成されました。

.
├── db (生成されたファイル)
│   ├── db.go
│   ├── models.go
│   └── query.sql.go
├── query.sql
├── schema.sql
└── sqlc.yml

models.go

// Code generated by sqlc. DO NOT EDIT.

package db

import (
    "github.com/google/uuid"
)

type User struct {
    ID   uuid.UUID
    Name string
}

db.go

// Code generated by sqlc. DO NOT EDIT.

package db

import (
    "context"
    "database/sql"
)

type DBTX interface {
    ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
    PrepareContext(context.Context, string) (*sql.Stmt, error)
    QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
    QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}

func New(db DBTX) *Queries {
    return &Queries{db: db}
}

type Queries struct {
    db DBTX
}

func (q *Queries) WithTx(tx *sql.Tx) *Queries {
    return &Queries{
        db: tx,
    }
}

query.sql.go

// Code generated by sqlc. DO NOT EDIT.
// source: query.sql

package db

import (
    "context"

    "github.com/google/uuid"
)

const count = `-- name: Count :one
SELECT count(*) FROM users
`

func (q *Queries) Count(ctx context.Context) (int64, error) {
    row := q.db.QueryRowContext(ctx, count)
    var count int64
    err := row.Scan(&count)
    return count, err
}

const create = `-- name: Create :one
INSERT INTO users (
  id, name
) VALUES (
  $1, $2
)
RETURNING id, name
`

type CreateParams struct {
    ID   uuid.UUID
    Name string
}

func (q *Queries) Create(ctx context.Context, arg CreateParams) (User, error) {
    row := q.db.QueryRowContext(ctx, create, arg.ID, arg.Name)
    var i User
    err := row.Scan(&i.ID, &i.Name)
    return i, err
}

const delete = `-- name: Delete :exec
DELETE FROM users WHERE id = $1
`

func (q *Queries) Delete(ctx context.Context, id uuid.UUID) error {
    _, err := q.db.ExecContext(ctx, delete, id)
    return err
}

const get = `-- name: Get :one
SELECT id, name FROM users
WHERE id = $1 LIMIT 1
`

func (q *Queries) Get(ctx context.Context, id uuid.UUID) (User, error) {
    row := q.db.QueryRowContext(ctx, get, id)
    var i User
    err := row.Scan(&i.ID, &i.Name)
    return i, err
}

const list = `-- name: List :many
SELECT id, name FROM users
ORDER BY id
`

func (q *Queries) List(ctx context.Context) ([]User, error) {
    rows, err := q.db.QueryContext(ctx, list)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    var items []User
    for rows.Next() {
        var i User
        if err := rows.Scan(&i.ID, &i.Name); err != nil {
            return nil, err
        }
        items = append(items, i)
    }
    if err := rows.Close(); err != nil {
        return nil, err
    }
    if err := rows.Err(); err != nil {
        return nil, err
    }
    return items, nil
}

const update = `-- name: Update :exec
UPDATE users SET name = $2
WHERE id = $1
`

type UpdateParams struct {
    ID   uuid.UUID
    Name string
}

func (q *Queries) Update(ctx context.Context, arg UpdateParams) error {
    _, err := q.db.ExecContext(ctx, update, arg.ID, arg.Name)
    return err
}

生成されたコードを使う

.
├── app.go
├── db
│   ├── db.go
│   ├── models.go
│   └── query.sql.go
├── query.sql
├── schema.sql
└── sqlc.yml

app.goに生成されたコードを使う部分を書いていくとこんな感じになります(このままでは動かないですが、雰囲気は伝わるかと思います)
WithTx() を使うことでトランザクションも使用可能です。

// app.go
func createUser(ctx context.Context) error {
    postgresDB, err := sql.Open("postgres", "")
    if err != nil {
        return err
    }

    queries := db.New(postgresDB)

    tx, err := postgresDB.BeginTx(ctx, nil)
    if err != nil {
        return err
    }
    args := db.CreateParams{
        ID:   "00000000-0000-0000-0000-000000000000",
        Name: "inari111",
    }
    if _, err := queries.WithTx(tx).Create(ctx, args); err != nil {
        return err
    }

    if err := tx.Commit(); err != nil {
        return err
    }

    return nil
}

使ってみてどうだったか

  • SQLからGoのコードを生成できるのは便利だと思った
  • NOT NULL制約をつけなければ sql.NullString でモデルの生成された
  • sqlc compile で構文や型のエラーをチェックすることも可能
  • k0kubun/sqldef で冪等にschema管理管理をし、sqlcでコードを生成する。といった使い方ができそうだと思った
  • こういった処理はどれも似たようなコードになるので、自動生成することで開発時間の短縮につながりそう

Cloud RunのサービスからCloud Tasksにenqueueし別のCloud Runサービスに処理させる

前回の記事では Cloud Scheduler から Cloud Run (batch) の呼び出しについて書きました。 inari111.hatenablog.com

自分のメモ用に雑に書いていきます。
今回は Cloud Scheduler → Cloud Run(batch) → Cloud Tasks → Cloud Run(task) の流れを目指します。

Cloud Tasksのpushキュー作成

任意の名前でキュー作成 f:id:inari111:20210309235252p:plain

サービスアカウント作成

  • 下記3つのロールを持つ
    • クラウドタスクキュー管理者
    • サービスアカウントユーザー
    • Cloud Run 起動元

f:id:inari111:20210309235755p:plain

Cloud Tasks に enqueueするコードを書く

Cloud Run (batch)側にenqueueするコードを書きます。
認証トークンを使用した HTTP タスクの作成 の通り書くとHTTPタスクを作成することができます。

Cloud Run (task) で処理するコードを書く

任意の処理を書きます。
今回は呼び出されていることを確かめることができればいいので、ログを吐くだけにしていおきます。

Cloud Runにデプロイ

Cloud Run (batch) と Cloud Run (task) をデプロイします。

動作検証

Cloud Schedulerのジョブを実行し、
Cloud Scheduler → Cloud Run(batch) → Cloud Tasks → Cloud Run(task) の流れが確認できれば動作確認は完了です。

Cloud Shcdulerから認証が必要なCloud Runのサービスを実行する

認証が必要なサービスがCloud Runにデプロイは完了している前提で進めます。
デプロイ周りはこれらを参考に進めていけば完了します。
Cloud Run へのデプロイ  |  Cloud Build のドキュメント  |  Google Cloud
コンテナ イメージのデプロイ  |  Cloud Run のドキュメント  |  Google Cloud

サービスアカウントの作成

サービスアカウントを作成し、 ロールにCloud Run 起動元を選択

f:id:inari111:20210309230459p:plain

Cloud Scheduler のジョブ作成

URLにはCloud Runのエンドポイントを指定
Authヘッダーには OIDCトークンを追加 を指定
サービスアカウントには先程作成したものを指定
対象はCloud Runのエンドポイントを指定 f:id:inari111:20210207172408p:plain

これで完了です。
あとはCloud Shcdulerのジョブが対象のエンドポイントを叩くのを待つだけです。

2020年買ってよかったもの

2020年買ってよかったものを記録として残します。
だいたい買った順になっています。

Xiaomi Mi Smart Band 4

スリープトラックをしたくて買いました。
安くて、軽くて、バッテリーの持ちが良いのでスマートウォッチの入門としてよかったです。
感覚ですが、スリープトラックはだいたい正確な数値を出しているように思いました。

【日本正規代理店品】Xiaomi Mi Smart Band 4 スマートバンド 活動量計

【日本正規代理店品】Xiaomi Mi Smart Band 4 スマートバンド 活動量計

  • 発売日: 2019/12/23
  • メディア: エレクトロニクス

iPad Pro 11インチ

Kindleで本を読む、図を描いて頭を整理する、YouTubeNetflixを見るのに使っています。
新しいiPad Airが出る前だったのでiPad Proを買いましたが、今なら間違いなくiPad Airを買います。

シャープ 除湿機 兼 加湿 空気清浄機 プラズマクラスター 7000

長く使っていた除湿機を処分したので、空気清浄機に除湿と加湿の機能がついたものを買いました。
「おまかせ」で起動していおけば自動で空気清浄・加湿・除湿を1台やってくれるので便利です。
想像以上に音が大きいので、寝るときには 「加湿」 の風量弱のようにモードと風量を指定して使っています。
あと、物理的にけっこう大きいので部屋によっては圧迫感があると思います。
この時期は乾燥するので、加湿器をもう一つ使用しています。

マイク SAMSON Q2U

私が好きなPodcast rebuild.fm をやっている宮川さんのブログで紹介されていて、値段も手頃だったので買ってみました。オーディオインターフェイス不要なUSBマイクなので、すぐ使い始めることができてよかったです。
ダイナミックマイクの特性上、マイクに口を近づけて話す必要がありますが、周りのノイズは拾いにくいのかなと思っています。

マイクスタンド

最初はマイクに付属していたデスクトップスタンドを使用していたのですが、キーボードの手前にデスクトップスタンドを置くとキーボードを打ちにくいため、マイクスタンドを使ってマイクを宙に浮かすことにしました。 MTGの議事録だったり、話しながらタイピングするときに楽になりました。
この商品はAmazonで検索して適当に買いました。特に不便はないです。

電動昇降デスク FLEXISPOT E6

モニター2枚とMacを開いていると横100cmのデスクでは厳しいため買い替えました。
140cmになったことで今までよりも広々と使えるようになり、高さ調節することでスタンディングデスクとしても役立っています。
いいお値段するので買うか迷いましたが、1日10時間とか12時間はデスクにいるので、買ってよかったです。
ものすごく重たいので、組み立てる際は2人でやったほうがいいです。1人で組み立てることも可能ですがしんどいです。

f:id:inari111:20201226234339j:plain

Herman Miller セイルチェア

週5リモートワークになってから2ヶ月くらいは元々使っていた椅子で頑張っていたのですが、腰が痛くてどうしようもなくなったので、椅子を新しいものにしました。
ちょうど緊急事態宣言の頃だったので、大塚家具などに試しにくことができず、しかし高い買い物なので失敗したくないという思いからレンタルすることにしました。
新品オフィス家具レンタル Kaggレンタル を使ってセイルチェアをレンタルしています。 新品をレンタルすることができて、2年レンタルを継続すると無償で譲り受けることができます。買取が可能なのも嬉しいです。
もう半年以上セイルチェアをレンタルしていて、座り心地には満足しているため、そろそろ買い取ろうと考えています。

骨伝導イヤホン OpenMove AfterShokz

初めての骨伝導イヤホンでしたが、想像以上に快適です。
オンラインミーティング時に役立っています。

ヘッドホンフック

ヘッドホンと骨伝導イヤホンを掛けるのに使っています。

Apple Watch

寝るときも含めてずっとMi Smart Band 4をつけていることにも慣れたので、Apple Watch Series 6を買いました。
バンドはスポーツバンドとソロループ両方持っていますが、ソロループしか使っていないです。そのくらい快適です。
ソロループはサイズが重要になるので実際にApple Storeで試すのがおすすめです。

体重計 Withings Body +

Amazonでセールだったときに買いました。
勝手に体重や体脂肪率が記録されていくので便利です。

ダイソン

これは ダイソン テクノロジー プラス サービス というサブスクなんですが、月1000円でダイソンの掃除機をレンタルできます。
掃除機以外のダイソン製品もあるので、とりあえず試してみたいという人にはいいかもしれないです。

買ったら満足してただろうけど諦めたもの

来年はMacを買い替える予定です。

goenvを使うのをやめた

Goを書き始めた当初からずっとgoenvを使っていましたが、Gophers Slackを見ているとgoenvを使わずにGoをインストールしている人が多い印象を受けたのでgoenvから脱却してみました。

以下、メモ書きとして残します。

1. go getしてくる

$ go get golang.org/dl/go1.12.15

2. go1.12.15コマンドが使えるようになったのでdownloadする

$ go1.12.15 download

3. シンボリックリンクを作成してgoコマンドが使えるようにする

$ ln -s $GOPATH/bin/go1.12.15 /usr/local/bin/go

これだけでgoenvから脱却できました。

新しいバージョンをインストールした場合は、

$ unlink /usr/local/bin/go

した後、

$ ln -s $GOPATH/bin/go1.14 /usr/local/bin/go

のようにする。

2019年買ってよかったもの

今年買ってよかったものを記録として残します。

Soundcore Liberty Air

rebuild.fm で紹介されていたので買ってみました。
値段も手頃で、半年くらい使っていました。

LG 27インチ 4K モニター

会社で4Kモニターを使っていると、家でフルHDのモニターを使うのが辛くなってきたので買いました。
LGのモニターはType-Cのケーブル1本で繋げられて給電もできるタイプもありますが、予算オーバーだったので安いのを。
モニターアームを付ける予定だったので、ピボット・高さ調整のできないタイプです。
モニター自体は満足しています。

モニターアーム

YouTubeで紹介されているのを見て買いました。
USBポートがアームに付いているのが地味に便利です。
モニターの下のスペースを有効に活用できていいですね。

www.instagram.com

PS4

LGのモニターに繋げて使っています。 買ったらゲームするようになるかと思いましたが、週に1回やるかどうかといったところですね。

WH-1000XM3

SONYノイズキャンセリングヘッドホンです。
ノイズキャンセリングの製品を買うのが初めてだったので、こんなにも快適なのかと驚きました。
長時間つけていても疲れないのがいいですね。充電はノイキャンONの状態で30時間持つみたいなので、長距離の移動時にはいいかもしれないです。 家やカフェで作業するときに使っていますが、たまに会社でも使っています。

ケーブルオーガナイザー

デスク周りのコードをスッキリさせるために、デスクの天板のに取り付けています。

WF-1000XM3

完全ワイヤレスのノイズキャンセリングイヤホンがSONYから出たので Soundcore Liberty Air から乗り換えました。
最初はとても気に入っていたのですが、微妙な部分が数箇所あり小さなストレスが溜まる感じがありました。
WH-1000XM3 がよかっただけに期待値高めでしたが、少々残念でした。

自作PC

Linux デスクトップのPCが欲しくて買いました。 ベアボーン自作PC初心者でも簡単に組み立てることができるのでオススメです。
5万円台でメモリ32GBのPCができたのは満足しています。
詳細はこちらで紹介しています。

inari111.hatenablog.com

マネークリップ

友人の結婚式でスーツを着る機会があったので、ポケットを膨らませないために買ってみました。
カード5枚 + お札 という最小限なので、ポケットに入れても目立たなくてお気に入りです。 普段は二つ折りの財布を使用していますが、荷物を少なくしたいときに使っています。
マネークリップ、iPhone、イヤホンだけをもって外出することが増えました。 カズさんの動画を見たのが決め手になりました。

www.youtube.com

iPhone11 Pro

iPhone X から乗り換えました。
買い換えるつもりなかったのですが、気づいたら予約していました。
Apple製品ってそういうものですよね。
iPhone X と Pixel3 の2台持ちの生活から開放されました。
カメラの性能がいいのとバッテリー持ちがよくなったなという感じですが、高い買い物だったなと思っています…

AirPods Pro

2019年、買って一番よかったものだと思います。
WF-1000XM3 から乗り換えました。
WF-1000XM3 で気になっていた点が全て解消されていて、とても満足度の高いイヤホンだと思います。

HHKB Professional HYBRID Type-S 墨

2019年の散財はAirPods Pro で終わりだと思っていましたが、Bluetoothで静音タイプで墨という自分にとって求めていた組み合わせが出てしまったので買ってしまいました。
カフェで作業するときは尊師スタイルなのでコードがないのは快適で見た目もスッキリしています。

www.instagram.com

まとめ

2020年は散財を控えたいと思います

ASRock DeskMini A300で初めて自作PCを作った

自作PCをやろうと思った理由

普段はMacBook Proで開発しているのですが、Docker for Macがあまりに遅く、開発機をMacからLinux Desktopに移行できないかと考えていました。
しかし、いきなり移行するのは厳しく、とりあえずLinux Desktopがどんなもんか試してみることにしました。

最初はWindowsのノートPCを買おうと考えていたのですが、メモリ16GBにすると20万くらいになってしまい見送っていました。
そんなとき、会社の方にベアボーンなら安く組めると教えてもらい、今回自作することにしました。

購入したパーツ

f:id:inari111:20190818212615j:plain

  • ベアボーン
    • ASRock DeskMini A300
    • マザボや電源が付いています
    • 14900円
  • CPU
    • Ryzen 5 2400G (4コア8スレッド)
    • 11780円
  • メモリ
    • ノートPC用 メモリ PC4-21300(DDR4-2666) 16GB×2枚
    • 16180円
  • SSD
    • WDの2.5インチ
    • 6460円

組み立て

CPU

向きに注意してつけるだけです
f:id:inari111:20190818212918j:plain

CPUクーラー

爪に引っ掛けて固定するのに少し苦労しました。
CPUに付属しているものではなく、ベアボーンに付属しているものを使います。
CPU付属のものだと取り付けできないはず。
f:id:inari111:20190818213002j:plain

メモリ

けっこう強めで押し込みます。
f:id:inari111:20190818213046j:plain

f:id:inari111:20190818213120j:plain

SSD

取り付けた後にネジで固定します。
f:id:inari111:20190818213151j:plain

配線

f:id:inari111:20190818213232j:plain

完成

ケースに戻してネジを締めたら完成です。
f:id:inari111:20190818213304j:plain

BIOS

無事起動しました。

f:id:inari111:20190818213339j:plain

まとめ

ASRock DeskMini A300で初めて自作PCを作ってみました。
CPU、 CPUクーラー、 メモリ、 SSDを取り付けるだけなので、未経験でも1時間ちょっとで起動までいけました。自作PCの入門には最適だと思います。
メモリ32GBのPCを約5万円で組めたのはかなりコスパいいと思うので、オススメです。
OSはUbuntu19を入れて使っています。 しばらく使ってみて良ければMacから移行する日も来るかもしれないです。