Как правильно параметризовать мой запрос postgresql

Я пытаюсь параметризовать свой запрос postgresql, чтобы предотвратить внедрение SQL в моем приложении ruby ​​on rails. SQL-запрос будет суммировать разные значения в моей таблице в зависимости от ввода.

Вот упрощенная версия моей функции:

def self.calculate_value(value)
    calculated_value = ""
    if value == "quantity"
        calculated_value = "COALESCE(sum(amount), 0)"
    elsif value == "retail"
        calculated_value = "COALESCE(sum(amount * price), 0)"
    elsif value == "wholesale"
        calculated_value = "COALESCE(sum(amount * cost), 0)"
    end
    
    query = <<-SQL
        select CAST(? AS DOUBLE PRECISION) as ? from table1
    SQL
    return Table1.find_by_sql([query, calculated_value, value])
end

Если я вызову calculate_value("retail"), он выполнит запрос следующим образом:

select location, CAST('COALESCE(sum(amount * price), 0)' AS DOUBLE PRECISION) as 'retail' from table1 group by location

Это приводит к ошибке. Я хочу, чтобы он выполнялся без кавычек, например:

select location, CAST(COALESCE(sum(amount * price), 0) AS DOUBLE PRECISION) as retail from table1 group by location

Я понимаю, что добавление кавычек предотвращает внедрение sql, но как мне предотвратить это в этом случае? Каков наилучший способ справиться с этим сценарием?

Обновлено: я добавил дополнительный столбец для извлечения из таблицы, чтобы подчеркнуть, что я не могу использовать pick для получения одного значения.

Каков результат, который вы ищете? Table1.find_by_sql попытается создать Table1 объекты, но вы подаете ему один столбец. Вам нужны только результаты расчета?

Schwern 12.12.2020 06:13
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
120
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

find_by_sql используется, когда вы хотите заполнить объекты одной строкой литерала SQL. Но мы можем использовать ActiveRecord для большей части этого, нам нужен только один столбец. Чтобы создавать объекты, используйте выберите . Если вам просто нужен результат, используйте щипки.

Поскольку вы выбираете из фиксированного набора строк, в этом коде отсутствует риск внедрения SQL. Используйте Arel.sql, чтобы передать литерал SQL, который, как вы знаете, безопасен.

def self.calculate_value(result_name)
  sum_sql = case result_name
    when "quantity"
      "sum(amount)"
    when "retail"
      "sum(amount * price)"
    when "wholesale"
      "sum(amount * cost)"
    end

  sum_sql = Arel.sql(
    "coalesce(cast(#{sum_sql} as double precision), 0) as #{result_name}"
  )

  return Table1
    .group(:location)
    # replace pluck with select to get Table1 objects
    .pluck(:location, sum_sql)
end

Я внес правку в ОП. В большинстве случаев я не хочу получать только одно значение. У меня просто так было, чтобы упростить запрос.

tee 13.12.2020 17:06

@tee Используй pluck. Применяется тот же принцип. Table1.group(:location).pluck(:location, Arel.sql("...")).

Schwern 13.12.2020 20:12

мой запрос упрощен. Я хочу использовать find_by_sql, поскольку я пишу более сложные запросы.

tee 14.12.2020 04:37

@tee Извините, вам нужно предоставить что-то, представляющее вашу проблему. Обычно find_by_sql можно избежать. Связывание предназначено для значений, вы добавляете идентификаторы. Используйте quote_column_name. Но так как это фиксированные строки, они безопасны и не нуждаются в кавычках.

Schwern 14.12.2020 05:44

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