以前の記事では様々な条件を利用して、ad_manager_api gemを利用してオーダーを取得するようにしました。
今回は前回触れたwhereの応用として、IN句を使う方法を考えます。
まずはサンプルとして、INを使わないwhere文を使ったorderの取得プログラムです。
# frozen_string_literal: true
require 'dotenv'
require 'ad_manager_api'
Dotenv.load
ad_manager = AdManagerApi::Api.new(
{
authentication: {
method: 'OAUTH2_SERVICE_ACCOUNT',
application_name: 'Ruby Ad Manager Sample',
oauth2_issuer: ENV['GOOGLE_AD_MANAGER_CLIENT_EMAIL'],
network_code: ENV['GOOGLE_AD_MANAGER_NETWORK_CODE'],
oauth2_key: OpenSSL::PKey::RSA.new(
ENV['GOOGLE_AD_MANAGER_PRIVATE_KEY'].gsub('\\n', "\n")
)
}
}
)
order_service = ad_manager.service(:OrderService, :v202508)
statement = ad_manager.new_statement_builder do |sb|
sb.where = "name = :name_1"
sb.with_bind_variable("name_1", "テスト")
end.to_statement
response = order_service.get_orders_by_statement(statement)
p response[:results]
今回は上記のプログラムを拡張していきます。
以下のプログラムは動きませんがイメージ的にはこういう感じ。
names = ['テスト1','テスト2']
statement = ad_manager.new_statement_builder do |sb|
sb.where = "name IN (:names)"
sb.with_bind_variable("names", names)
end.to_statement
with_bind_variableを使わない場合
まずはwith_bind_variableを使わない方法を考えます。
一個は全てwhereに全部ベタ書きする方法。
names = ['テスト1','テスト2']
statement = ad_manager.new_statement_builder do |sb|
sb.where = "name IN ('#{names.join("', '")}')"
end.to_statement
p statement
# {query: "WHERE name IN ('テスト1', 'テスト2') LIMIT 500 OFFSET 0", values: []}
INの中身を配列から生成し、whereの中に直接入れています。
以下のような方法でも同様な結果にもなります。
names = ['テスト1','テスト2']
statement = ad_manager.new_statement_builder do |sb|
sb.where = "name IN ('%s')" % names.join("', '")
end.to_statement
p statement
# {query: "WHERE name IN ('テスト1', 'テスト2') LIMIT 500 OFFSET 0", values: []}
ただ、SQLインジェクションを考えるなら用意されているwith_bind_variableを使いたいです。
with_bind_variableを使う方法
with_bind_variableを使うためには配列に使うのではなく、配列の中身の要素それぞれにwith_bind_variableを使うようにします。
in_str = names.each_with_index.map { |_, index| ":name_#{index}" }.join(', ')
statement = gam_client.new_statement_builder do |sb|
sb.where = "name IN (#{in_str})"
names.each_with_index do |name, index|
sb.with_bind_variable("name_#{index}", name)
end
end.to_statement
p statement
# {query: "WHERE name IN (:name_0, :name_1) LIMIT 500 OFFSET 0", values: [{key: "name_0", value: {xsi_type: "TextValue", value: "テスト1"}}, {key: "name_1", value: {xsi_type: "TextValue", value: "テスト2"}}]}
配列を分解して、IN句に渡すためのパラメータを動的に生成します。
これによって、IN句を使いつつ各変数をwith_bind_variableを使うことでSQLインジェクションを予防できます。
まとめ
ad_manager_api gemで安全にIN句を使う方法をこの記事では考えました。
ad_manager_api gemはあまり知見が転がっていないので参考になれば幸いです。