Как получить оператор SQL с заменой переменных в Rails?

Я использую Rails 7.1.2 и пытаюсь понять, как заставить Rails/ActiveRecord выполнять интерполяцию переменных в строку SQL и возвращать мне полученный SQL. Вот что я пробовал:

>> User.connection.to_sql("select * from users where id = $1", [1])
=> "select * from users where id = $1"
>> User.connection.to_sql("select * from users where id = ?", [1])
=> "select * from users where id = ?"
>> User.connection.to_sql("select * from users where id = :id", { id: 1 })
=> "select * from users where id = :id"
>> User.connection.to_sql("select * from users where id = :id", [{ id: 1 }])
=> "select * from users where id = :id"

В документации этого метода неясно, следует ли интерполировать параметры привязки или нет, там просто говорится:

https://api.rubyonrails.org/v7.0.8/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-to_sql

Преобразует AST в SQL

Я не знаю, имеют ли они в виду SQL с интерполированными параметрами или нет, когда говорят «SQL», но я не знаю, почему еще метод будет принимать параметры связывания, если он их не интерполирует.

Я ищу любой вариант параметров в запросе, который будет заменен, поэтому мне нужно, чтобы значения параметров привязки были интерполированы в результаты, например:

>> User.connection.to_sql("select * from users where id = $1", [1])
=> "select * from users where id = 1"
User.where("id=?", 1).to_sql или User.where(id: 1).to_sql
Les Nightingill 04.04.2024 18:23

Привязки SQL работают не так. Вся суть в том, что вы передаете в базу данных запрос и значения отдельно. Это позволяет базе данных подготовить и повторно использовать запрос и избежать внедрения SQL. Как вы думаете, почему вам это нужно/нужно?

max 04.04.2024 18:33

Что я делаю, так это то, что у меня есть сложный запрос, который я на самом деле вызываю с помощью select_all, например User.connection.select_all("select first_name from users where id = $1", nil, [1]). Мне бы хотелось увидеть запрос с интерполированными параметрами, но, поскольку это происходит в базе данных, возможно, в Ruby/Rails это просто невозможно сделать.

pjb3 04.04.2024 18:37

Эй, Пол! Я думаю, что чтобы увидеть это на клиенте, уведомления AS должны быть одним из последних действий перед отправкой текста запроса. Если да, то вы можете создать класс подписчика для событий «sql.active_record» и зарегистрировать его. Вот сообщение в блоге с соответствующим примером. В вашем случае вы не захотите фильтровать только медленные запросы, а регистрировать их все, возможно, включая класс подписчика по мере необходимости. kinsta.com/blog/logging-slow-queries

Andrew Atkinson 05.04.2024 02:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
89
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы ищете sanitze_sql и друзей из ActiveRecord::Sanitization::ClassMethods:

User.sanitize_sql(['select * from users where id = ?', 1])
# select * from users where id = 1

User.sanitize_sql(['select * from users where id = :id', id: 11])
# select * from users where id = 11

User.sanitize_sql(['select * from users where name = :name', name: "G'Kar"])
# select * from users where name = 'G''Kar'

Чего я действительно хочу на самом деле, так это получить SQL при использовании таких параметров привязки: User.connection.select_one('select * from users where id = $1', nil, [1]) Но, как указано выше, для того типа запроса, который использует подготовленные операторы под капотом, эта интерполяция не происходит в Ruby, она происходит в Postgres, так что это может быть не так важно

pjb3 04.04.2024 20:02

@pjb3 Rails передает это pg. (После подготовки привязок) См. Здесь и PG::Connection#exec_params . Также см. функцию libpq

engineersmnky 04.04.2024 20:25
Ответ принят как подходящий

Хотя это и не встроено в рельсы, вы можете попробовать реализовать простую версию этого для себя.

Например:

def interpolate_sql_with_binds(sql, binds=[])
  sql_binds = sql.scan(/\$\d+/)
  raise ArgumentError, "incorrect number of bind values" if binds.size != sql_binds.size
  quoted_binds = binds.map {|b| Arel::Nodes.build_quoted(b).to_sql} 
  sql.gsub(/\$\d+/,sql_binds.zip(quoted_binds).to_h) 
end

interpolate_sql_with_binds("select * from users where id = $1",[1])
#=> "select * from users where id = 1"
interpolate_sql_with_binds("select * from users where id = $1 OR username = $2",[1,'engineersnmky'])
#=> "select * from users where id = 1 OR username = 'engineersmnky'"

Очевидно, что вы можете продолжать совершенствовать обработку ошибок и обработку синтаксиса (например, предложения IN(), поддержку привязок массивов и т. д.) по мере необходимости.

Да, я понял, что в Rails нет для этого метода. Если бы я захотел это сделать, мне пришлось бы создать что-то вроде этого

pjb3 06.04.2024 12:43

Другие вопросы по теме