AIエージェント向け読み取り専用DBクエリコマンド `rails query` の追加

rails/rails

Railsに bin/rails query コマンドが追加され、AIエージェントや自動化ツールが本番データベースに対して安全かつ構造化された形でクエリを実行できるようになりました。読み取り専用の強制、JSON出力、ページネーションを標準で備えています。

背景

AIエージェントや自動化ツールが本番データベースへの問い合わせを必要とする場面が増える中、既存の rails runner には実用上の課題がありました。rails runner "puts Account.count" は平文テキストを出力するため、エージェントが確実にパースできる一貫したフォーマットがなく、また Account.delete_all のような書き込みクエリの実行も防げませんでした。さらに、大量のレコードをすべて取得してしまうことを避けるために、呼び出し側が自前で LIMIT/OFFSET ロジックを組み込む必要もありました。

Kamal を使えば kamal app exec で本番へのコマンド実行は容易ですが、構造化された出力を返し、かつ読み取り専用を保証するコマンドが不足していました。rails query はこの空白を埋めるために設計されています。

技術的な変更

rails query コマンドは railties/lib/rails/commands/query/query_command.rbRails::Command::QueryCommand クラスとして実装されています。ActiveRecord 式と生 SQL の両方をサポートし、結果は常に JSON 形式で返します。

perform メソッドはサブコマンドのディスパッチを担い、引数に応じて schemamodelsexplain・通常クエリの4つの実行パスに分岐します。全体の実行は ActiveSupport::Notifications.instrument("query.rails") でラップされており、監査ログのフックポイントとして機能します。

def perform(expression = nil, *args)
  boot_application!
  Rails.application.load_runner

  ActiveSupport::Notifications.instrument("query.rails", expression: expression) do
    case expression
    when "schema"
      run_schema(args.first)
    when "models"
      run_models
    when "explain"
      run_explain(args.first)
    else
      run_query(expression)
    end
  end
rescue StandardError, SyntaxError, NotImplementedError => e
  output_error(e.message)
  exit 1
end

出力形式は columnsrowsmeta の3フィールドで構成された JSON です。meta には row_countquery_time_mspageper_pagehas_moresql が含まれます。スカラー値(Account.count など)も {"columns":["result"], "rows":[[42]]} の形に正規化されるため、呼び出し側は結果の型を意識せず同一フォーマットで扱えます。エラーが発生した場合も {"error": "...", "meta": {...}} の JSON として出力されます。

ページネーションは --page--per オプションで制御します。per は最大 10,000 件に上限が設けられており、[ [ options[:per], 1 ].max, 10_000 ].min で安全に境界を処理しています。

利用できる主なオプションとサブコマンドは以下の通りです:

  • --sql : ActiveRecord 式ではなく生 SQL として実行
  • --page / --per : ページネーション制御
  • --database / --db : マルチデータベース環境での接続先指定
  • schema [TABLE] : テーブル一覧、またはカラム・インデックス・enum・アソシエーションの詳細表示
  • models : ActiveRecord モデル一覧とアソシエーション情報
  • explain EXPRESSION : クエリプランの表示

設計判断

読み取り専用の強制はリーディングレプリカロールへの接続をデフォルトとし、シングルデータベース構成では while_preventing_writes にフォールバックする2段構えの設計です。書き込みを試みた場合は "Write query attempted while in readonly mode" というエラーが JSON で返ります。

ActiveSupport::Notifications による計装は、Console1984 のような監査ツールとのインテグレーションを想定した設計です。"query.rails" イベントにサブスクライブするだけで、コマンド自体にパッチを当てることなくアプリケーション側での追跡が可能になります。ペイロードには評価された式が含まれます。

--sql フラグによる信頼モデルの分離も注目すべき点です。デフォルトでは ActiveRecord 式を Ruby として評価するため、rails runnerrails console と同等の信頼モデルを前提としています。--sql フラグを指定した場合は SQL のみの実行に限定でき、例えば本番オペレーション向けの CI スクリプトで SQL のみを許可するといった運用上の制限を設けることができます。

まとめ

rails query は、AIエージェントや自動化ツールが本番データベースに安全にアクセスするための専用インターフェースをRailsフレームワーク自体に組み込んだ変更です。JSON出力の統一、読み取り専用の強制、計装フックの提供という3つの要素が組み合わさることで、rails runner では実現できなかった「エージェントが安全に問い合わせできる」運用パターンを標準ツールとして確立しています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
bce46b1e

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)」という3部構成が明確に適用されています。各セクションが適切な役割を果たしており、非常に分かりやすい構成です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```ruby:ファイルパス)と、PR番号のリンク記法([#123](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

`rails runner` の課題や `ActiveSupport::Notifications` など、Rails開発者が持つ前提知識を適切に活用しており、専門知識を持つエンジニアという対象読者に適合しています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構造を持ち、各パラグラフがトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られています。これにより、記事の骨子を素早く把握できます。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されている`perform`メソッドのコードブロックは、Diffで追加された`railties/lib/rails/commands/query/query_command.rb`の内容と完全に一致しており、正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「リーディングレプリカロール」「`while_preventing_writes`」「計装 (instrumentation)」など、PR情報やRailsの文脈に沿った技術用語が正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

ページネーションの`--per`オプションの上限が10,000件であることなど、Diff内のコードに基づいた具体的な説明が含まれており、技術的に正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(`rails runner`の課題、`rails query`の機能、設計思想など)は、提供されたPRのDescriptionやDiffの内容によって裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号 `#57156` やページネーションの上限値 `10,000` など、記事に含まれる数値や固有名詞はすべて正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルは、PRの主題である「読み取り専用クエリコマンド」に加え、PRの背景で述べられている「AIエージェント向け」という主要なユースケースを的確に反映しており、内容と完全に一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PR情報に記載のないバージョンサポート状況やリリース予定などの外部知識は含まれておらず、すべての情報が提供された資料に基づいています。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

新機能の追加という文脈で「追加され」といった表現が使われており、時間的な表現に誤りや歪曲はありません。