FIX: Errors when running query due to PG template patterns or comments (#189)

Before this fix, the use of PG template patterns containing ":" or the
use of "?" in comments in the SQL will result in an error being raised
because `DB.param_encoder.encode` calls ActiveRecord's `sanitize_sql_array` which is
meant for SQL fragments and not an entire SQL string.

Instead we change data-explorer to use `MiniSql::InlineParamEncoder`
instead which takes into account of template patterns and does not trip
on `?` which is a special param encoding character used by ActiveRecord.
This commit is contained in:
Alan Guo Xiang Tan 2022-10-10 09:03:37 +08:00 committed by GitHub
parent 729e5a2add
commit 4236689d27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 4 deletions

View File

@ -76,7 +76,7 @@ after_initialize do
# Run a data explorer query on the currently connected database.
#
# @param [DataExplorer::Query] query the Query object to run
# @param [Hash] params the colon-style query parameters to pass to AR
# @param [Hash] params the colon-style query parameters for the query
# @param [Hash] opts hash of options
# explain - include a query plan in the result
# @return [Hash]
@ -124,9 +124,9 @@ SQL
time_start = Time.now
# we probably want to rewrite this ... but for now reuse the working
# code we have
sql = DB.param_encoder.encode(sql, query_args)
# Using MiniSql::InlineParamEncoder directly instead of DB.param_encoder because current implementation of
# DB.param_encoder is meant for SQL fragments and not an entire SQL string.
sql = MiniSql::InlineParamEncoder.new(ActiveRecord::Base.connection.raw_connection).encode(sql, query_args)
result = ActiveRecord::Base.connection.raw_connection.async_exec(sql)
result.check # make sure it's done

View File

@ -0,0 +1,61 @@
# frozen_string_literal: true
describe DataExplorer do
describe '.run_query' do
fab!(:topic) { Fabricate(:topic) }
it 'should run a query that includes PG template patterns' do
sql = <<~SQL
WITH query AS (
SELECT TO_CHAR(created_at, 'yyyy:mm:dd') AS date FROM topics
) SELECT * FROM query
SQL
query = DataExplorer::Query.create!(name: "some query", sql: sql)
result = described_class.run_query(query)
expect(result[:error]).to eq(nil)
expect(result[:pg_result][0]["date"]).to eq(topic.created_at.strftime("%Y:%m:%d"))
end
it 'should run a query containing a question mark in the comment' do
sql = <<~SQL
WITH query AS (
SELECT id FROM topics -- some SQL ? comment ?
) SELECT * FROM query
SQL
query = DataExplorer::Query.create!(name: "some query", sql: sql)
result = described_class.run_query(query)
expect(result[:error]).to eq(nil)
expect(result[:pg_result][0]["id"]).to eq(topic.id)
end
it 'can run a query with params interpolation' do
topic2 = Fabricate(:topic)
sql = <<~SQL
-- [params]
-- int :topic_id = 99999999
WITH query AS (
SELECT
id,
TO_CHAR(created_at, 'yyyy:mm:dd') AS date
FROM topics
WHERE topics.id = :topic_id
) SELECT * FROM query
SQL
query = DataExplorer::Query.create!(name: "some query", sql: sql)
result = described_class.run_query(query, { "topic_id" => topic2.id.to_s })
expect(result[:error]).to eq(nil)
expect(result[:pg_result].to_a.size).to eq(1)
expect(result[:pg_result][0]["id"]).to eq(topic2.id)
end
end
end