🏗️

【Rust】sqlxでコンパイルエラーが発生する

2025/03/20に公開

はじめに

sqlxでDB接続する処理があってローカルではDBを起動していたのでビルドも通っていました。
Github Actionでビルドして問題ないかチェックするようにしていたのですが、以下のようなエラーが発生してしまいました。
🏗️

error: error communicating with database: Connection refused (os error 111)
  --> src/infrastructure/report_repository.rs:87:20
   |
87 |           let rows = sqlx::query!(
   |  ____________________^
88 | |             "SELECT DISTINCT 
89 | |                 CAST(EXTRACT(YEAR FROM date) AS INTEGER) AS year, 
90 | |                 CAST(EXTRACT(MONTH FROM date) AS INTEGER) AS month, 
...  |
94 | |             year as i64
95 | |         )
   | |_________^
   |
   = note: this error originates in the macro `$crate::sqlx_macros::expand_query` which comes from the expansion of the macro `sqlx::query` (in Nightly builds, run with -Z macro-backtrace for more info)

offlineモードでビルドする

発生して原因としては、query!がマクロなのでビルド時に実行するため、DBが必要となるのでこけていました。

一番簡単な解決策としては、ビルド時にコード自体を実行しないquery関数に置き換えるだけで解決します。
ただし、これでは堅安全性が下がるのでマクロをそのまま使いたい方もいると思うので、その場合は、offlineモードを使いましょう。
手順としては以下の通りです。

  1. 環境変数に以下のofflineモードを有効化する
SQLX_OFFLINE=true
  1. 実行したクエリのキャッシュを保存する
cargo sqlx prepare

これで、以下のようなjsonファイルが生成されます。
このキャッシュファイルがあることで事前にDBのクエリをキャッシュしておくことができるようになります。
queryを使っている対象のテーブルにおいてマイグレーションが発生する際に実行しておくようにしましょう。

.sqlx/query-xxxxx.json
{
  "db_name": "PostgreSQL",
  "query": "SELECT DISTINCT \n                CAST(EXTRACT(YEAR FROM date) AS INTEGER) AS year, \n                CAST(EXTRACT(MONTH FROM date) AS INTEGER) AS month, \n                CAST(EXTRACT(DAY FROM date) AS INTEGER) AS day \n            FROM reports \n            WHERE EXTRACT(YEAR FROM date) = $1;",
  "describe": {
    "columns": [
      {
        "ordinal": 0,
        "name": "year",
        "type_info": "Int4"
      },
      {
        "ordinal": 1,
        "name": "month",
        "type_info": "Int4"
      },
      {
        "ordinal": 2,
        "name": "day",
        "type_info": "Int4"
      }
    ],
    "parameters": {
      "Left": [
        "Numeric"
      ]
    },
    "nullable": [
      null,
      null,
      null
    ]
  },
  "hash": "7296bb3ca62a2c9ec4518a1b52848197c89cf8bacb09915ca4a7065c68585f5e"
}

  1. ビルドする

これでsqlxはofflineモードでビルドされるのでDBが起動していなくてもビルドが通るようになっているはずです。

cargo build
cargo build
   Compiling backend v0.1.0 (/Users/xxxxxx)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.59s
  1. Github Actionのワークフロー設定ファイルにofflineモードを定義

上までの操作でローカル上でofflineモードでビルドできるようになりましたが、冒頭で言及していた通り、Github Actionでこけていた問題を解消する場合は、ワークフロー設定ファイルにビルド時にofflineモードを有効化するように環境変数を追加してください。

rust.yml
name: Rust

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

env:
  CARGO_TERM_COLOR: always

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
    - name: Build
      working-directory: ./backend
      env:
        SQLX_OFFLINE: true # offlineモード有効化
      run: cargo build --verbose

また、cargo sqlx prepareで生成した.sqlx/query-xxxxx.jsonもcommitしておくようにしてください。

終わりに

sqlxのquery!でこけていたのはビルド時にDBがないためにこけていました。
事前にクエリのキャッシュを使うofflineモードを有効化すれば、DBが起動していなくてもビルドが通るようになります。

Discussion