icon

nazo6.dev

一覧に戻る
2023/5/26 6 min read

RustでSQLからコードを生成するcornucopiaについて


#cornucopiaとは (2023-05-26)

SQLからRustのコードを生成して安全にデータベース操作ができる。恐らくGoのsqlcと同じ感じなんだと思う。

#というかcornucopiaって何よ

1 [the cornucopia] 【ギリシャ神話】 豊饒(ほうじよう)の角 《幼時の Zeus 神に授乳したと伝えられるやぎの角》. 2 可算名詞 豊饒の角の装飾 《角の中に花・果物・穀類を盛った形で,物の豊かな象徴》. 3 [a cornucopia] 豊富 〔of〕. a cornucopia of good things to eat たくさんのおいしい食物. 4 可算名詞 円錐形の容器.

(weblio - https://ejje.weblio.jp/content/cornucopia)

#resources

#準備 (2023-05-26)

#(2023-05-26)

Rustのプロジェクトを作成

cargo new --bin cornucopia-example

#(2023-05-26)

PostgreSQLのサーバーを適当に建てる

docker-compose.yml
version: "3"
services:
  db:
    image: postgres:13.3
    environment:
      POSTGRES_USER: test
      POSTGRES_PASSWORD: password
    ports:
      - 5432:5432
    volumes:
      - postgres:/var/lib/postgresql
volumes:
  postgres:
 

#(2023-05-26)

cornucopiaにはmigration機能などはついていない。 まず最初にデータベースのスキーマを作る必要がある。 ここではatlasを使って適当にスキーマを決める

schema.hcl
schema "public" {}
table "users" {
  schema = schema.public
  column "id" {
    null = false
    type = int
    identity {
        generated = ALWAYS
        start = 10
        increment = 10
    }
  }
  primary_key  {
    columns = [column.id]
  }
  column "name" {
    null = false
    type = text
  }
}

そして適用

atlas schema apply --url "postgresql://test:password@localhost:5432/test?sslmode=disable" --to "file://schema.hcl"

#使い方 (2023-05-26)

ここから実際にcornucopiaを使う

#クエリの作成 (2023-05-26)

Rustプロジェクトのルートにqueriesフォルダを作り、そこにSQLを置く 例:

queries/user.sql
--! insert_user
INSERT INTO users(name)
VALUES (:name);

#インストール (2023-05-26)

cliをインストール

cargo install cornucopia

#生成

cornucopiaコマンドでRustのファイルを生成できる。ちなみにcornucopiaがpostgresのdockerコンテナを勝手に作るようにもできるみたい。

cornucopia live "postgresql://test:password@localhost:5432/test"
rustfmt --edition 2021 ./src/cornucopia.rs

このコマンドでは実際のデータベースに接続することで存在しないテーブルにアクセスしようとした際などにエラーを出してくれる。とても便利。

src/cornucopia.rsが生成される。先のSQLではこのようなファイルが得られる。

src/cornucopia.rs
// This file was generated with `cornucopia`. Do not modify.
 
#[allow(clippy::all, clippy::pedantic)]
#[allow(unused_variables)]
#[allow(unused_imports)]
#[allow(dead_code)]
pub mod types {}
#[allow(clippy::all, clippy::pedantic)]
#[allow(unused_variables)]
#[allow(unused_imports)]
#[allow(dead_code)]
pub mod queries {
    pub mod user {
        use cornucopia_async::GenericClient;
        use futures;
        use futures::{StreamExt, TryStreamExt};
        pub fn insert_user() -> InsertUserStmt {
            InsertUserStmt(cornucopia_async::private::Stmt::new(
                "INSERT INTO users(name)
VALUES ($1)",
            ))
        }
        pub struct InsertUserStmt(cornucopia_async::private::Stmt);
        impl InsertUserStmt {
            pub async fn bind<'a, C: GenericClient, T1: cornucopia_async::StringSql>(
                &'a mut self,
                client: &'a C,
                name: &'a T1,
            ) -> Result<u64, tokio_postgres::Error> {
                let stmt = self.0.prepare(client).await?;
                client.execute(stmt, &[name]).await
            }
        }
    }
}

とてもわかりやすい

#使用方法 (2023-05-26)

この生成されたファイルの依存をいろいろ追加する。

cargo add tokio cornucopia_async futures tokio_postgres

なおデフォルトではtokio_postgresを用いた非同期コードが生成されるが同期コードも生成できるみたい。

あとは生成された関数を呼び出すだけ。

src/main.rs
use cornucopia::queries::user::insert_user;
use tokio_postgres::{Error, NoTls};
 
mod cornucopia;
 
#[tokio::main]
async fn main() -> Result<(), Error> {
    let (client, connection) = tokio_postgres::connect(
        "host=localhost port=5432 user=test password=password",
        NoTls,
    )
    .await?;
 
    tokio::spawn(async move {
        if let Err(e) = connection.await {
            eprintln!("connection error: {}", e);
        }
    });
 
    insert_user().bind(&client, &"me").await.unwrap();
 
    Ok(())
}

これで無事insertを行うことができた。

#良かったところ (2023-05-26)

今までRustでRDBを扱う方法を色々さがしてきて

  • ORM: まだあまり成熟してない感じだった(個人的にはprisma-client-rustが一番よかったと感じたが依存が重すぎなのと色々不安定だった)
  • sqlx: 確かにSQLを書けばマクロで型が補完されるのはすごいが開発中もデータベースの状態を気にしないといけないし何よりマクロは辛い

などの問題を感じていたがcornucopiaはデータベースに接続するのはコードを生成するときだけだし生成されたコードも普通のRustファイルで見やすいのがとても良い。

あとbind()以外にもparams()関数が用意されていてパラメータを構造体で作成できるのも嬉しい。

#改善されてほしいところ (2023-05-26)

  • まだ色々機能が足りてない感じがする
    • 例えばBatch insertionなど
      • イテレータを挿入できるようになれば結構便利になりそう
    • あとはこれとか
  • PostgreSQLにしか対応してない
    • まあこれは仕方ないのかなと思いつつもsqliteとかで使えたらとてもいいなと
Share this article:
一覧に戻る

関連記事

2021/12/27

2023/10/20

#tech/lang/rust
memo

Rust

気づきとかいろいろ

Read Article

2024/3/23

#tech/lang/rust#hardware/keyboard/keyball
blog

RustでKeyballのファームウェアを書きたい話

KeyballのファームウェアはQMKを使ったC言語のものになっています。ですがやはりRust、使いたいですよね?

Read Article

2023/11/15

#tech/lang/rust
memo

Rustでジョブキュー的なもの

実行するコマンド(EnqueueかClear)をチャネルで受け取る

Read Article

2025/12/13

2025/12/14

#tech/lang/rust
blog

RustでデスクトップGUI - gpui入門 Part1 (gpuiの仕組み・状態管理の基礎編)

gpui解説記事のPart1。gpuiのレンダリング方法や状態管理について、実際のソースを見ながら詳しく解説します。

Read Article

2024/4/26

2024/5/22

#tech/lang/rust#hardware/keyboard/keyball
blog

RustとEmbassyでKeyballのファームウェアを作った

以前RustでKeyballのファームウェアを書きたい話で、ATMega32U4向けのファームウェアの作成をRustで試みたという話を書きましたが、結論から言うとこれは諦めてProMicro RP2040向けのファームウェアをRustで書くことにしました。

Read Article

2023/8/27

#tech/lang/rust
memo

Rustのserde_jsonでエラーの発生箇所を知る方法

serde_jsonではパースエラー発生時にどのプロパティでエラーが発生したのかわからない

Read Article

2023/6/27

#tech/lang/rust
memo

Rustのtargetフォルダを軽くする

cargo-sweepを使う

Read Article

2022/2/13

#tech/lang/rust
memo

Rustアプリにwasmerを埋め込む

dioxusを使ってwebでもdesktopでも動くアプリを作りたい

Read Article

2025/3/29

#tech/lang/rust
memo

Rustアプリのメモリ使用量を調査する

主にstatic領域のメモリ使用量を調査するのに有益。embassyの独立したタスクなどのサイズが見れる。

Read Article

2023/9/10

2023/12/18

#tech/lang/rust
blog

SerdeのDeserializerを実装する(Part1)

Serdeで任意の形式のファイルなどをデシリアライズする際にはDeserializerを書く必要があります。この記事では基本的なDeserializerの書き方を解説します。 正直自分もあまり理解していない部分が多々あるのですが世に出ている情報が少ないので書くことにしました。

Read Article

2023/12/18

2023/12/19

#tech/lang/rust
blog

SerdeのDeserializerを実装する(Part2 JSON編)

この記事はRust Advent Calendar 2023 シリーズ3の19日目の記事です。

Read Article

2024/5/24

#hardware/keyboard#tech/lang/rust
blog

USB HIDキーボードでメディアキーを操作する方法

USB HIDでは0x80がVolume Up、0x81がVolume Downに割り当てられており、さらに0xEDや0xEEでもVolume UpやDownができそうですが、実はこれらは全て動きません(Windowsでは)。

Read Article

2023/9/1

#tech/lang/rust
blog

prisma-client-rust入門

prisma-client-rustはJavascript向けのORMであるprismaをRustから使えるようにしたものです。実はprismaのコア部分はRustで書かれているためこういうものも作りやすかったんじゃないかと思います。

Read Article

2021/12/25

#tech/lang/rust
memo

tauriでWindows上でproductionビルドでのみ画像が表示されない(fetchエラーが発生する)

誰の役にも立たない気がするけどハマったのでメモ

Read Article

2023/11/18

#tech/lang/rust
blog

tokioで作ったサーバーをdockerで起動すると終了が遅くなるときの対処法

axumなどを作ってRustでサーバーを作るとdocker compose stopなどが微妙に遅くてイライラだったのでそれを解決する方法です。

Read Article

2025/4/9

2025/11/5

#tech/lang/rust
blog

「Rustが嫌いです。」の感想

https://zenn.dev/miguel/articles/f052de93fc9980

Read Article

© 2025 nazo6. All rights reserved.