はじめに
下記のような形でActiveRecordが発行するSQLからWHERE句の部分のSQLだけを取得したいケースがあり、色々やり方とか調べたのでMEMO✍
やりたかったこと
今回やりたかったのは、下記のようなscope
を定義していたときにto_sql
するとSQL全体を取得することが出来ますが、
class Book < ApplicationReacord scope :viewable, ->(now = Time.current) { where(published_at: now..) } end Book.viewable.to_sql #=> select * from books where published_at > '2020-05-23 15:24:34'
そうではなくWHERE
句のpublished_at > '2020-05-23 15:24:34'
部分だけ取得したいというものです。
where部分だけのSQLを取得する方法
下記のような感じで取れるみたいです👀(⚠nodocなので動作保証されてないですが・・・!)
Book.viewable.values[:where].ast.to_sql #=> published_at > '2020-05-23 15:24:34'
values[:where]
でActiveRecord::Relation::WhereClauseのオブジェクトを取得することができて、オブジェクトに対してastメソッドを呼び出すことによってArel::Nodes
系のオブジェクトを取得できるようです👀
そして取得したオブジェクトにto_sql
を呼び出すことによってそのNodeの部分だけのSQLを取得できるような形で動いてそうな気がしました・・・!
WHEREも含めて取得したいときは下記のようにするととれるようです👀(⚠こちらもnodocなので動作保証されてないです。)
Book.viewable.arel.where_sql #=> WHERE published_at > '2020-05-23 15:24:34'
ですが、Arel::Nodes::Node#to_sql
はコメントにも無くなる可能性が明記されてるので、素直にSQLをparseした方が良さそうな気もしました😓
# FIXME: this method should go away. I don't like people calling # to_sql on non-head nodes. This forces us to walk the AST until we # can find a node that has a "relation" member. # # Maybe we should just use `Table.engine`? :'( def to_sql(engine = Table.engine) collector = Arel::Collectors::SQLString.new collector = engine.connection.visitor.accept self, collector collector.value end
rails/node.rb at b1f6d8c8d8ad3e2e5b96e95b455c70f2c895ce14 · rails/rails · GitHub