9. ネストした行は返せなく無
い?
SQL に JSON を組む関数がある
SELECT u.id, u.name, json_agg(c.content) as comments
FROM users AS u
LEFT OUTER JOIN comments AS c ON u.id = user_id
GROUP BY 1, 2
10. Array 入りの JSON の例
curl -X GET
-H 'Authorization: Token 4f3326a4-b900-4624-af58-87e8f363dee6'
-F "haute_couture[query]=
SELECT u.id, u.name, json_agg(c.content) as comments
FROM users AS u
LEFT OUTER JOIN comments AS c ON u.id = user_id
GROUP BY 1, 2"
https://sqlql-sample-yancya.herokuapp.com/haute_couture
11. Array 入りの JSON の例
[{"id":11,"name":"testuser","comments":["it is not secret"]},
{"id":14,"name":"secretman","comments":[null]},
{"id":10,"name":"yancya","comments":["hoge", "fuga", "piyo"]}]
12. どうやって実現しているのか
リクエストのクエリを CTE(WITH)
の中に埋め込んでいる
module HauteCouture
def self.find_by_sql(query:, user:)
ActiveRecord::Base.connection.execute(<<~SQL).first['result'] || '[]'
WITH users AS (#{user.for_haute_couture_sql})
, comments AS (#{Comment.for_haute_couture(user).to_sql})
, t AS (#{query})
SELECT JSON_AGG(t) AS result FROM t
SQL
end
end
14. 最終的に組まれる SQL
WITH users AS (
SELECT "users"."id", "users"."name", "users"."created_at", "users"."updated_at"
FROM "users" WHERE ("users"."id" = 10 OR "users"."privacy" = 'f'))
, t AS (SELECT id, name FROM users) -- <- ここに入ってるのかリクエストされた SQL
SELECT JSON_AGG(t) AS result FROM t
16. SQL インジェクションで死
ぬのでは
確かに、likes が全消しされる
SQL になってしまう
WITH users AS (
SELECT "users"."id", "users"."name", "users"."created_at", "users"."updated_at"
FROM "users" WHERE ("users"."id" = 10 OR "users"."privacy" = 'f'))
, t AS (SELECT 1), killer AS (DELETE FROM likes CASCADE RETURNING *)
SELECT JSON_AGG(t) AS result FROM t
17. SQL インジェクションで死
ぬのでは
流石に、一回 SQL パーサーに喰わ
せる必要がある
PgQuery.parse("SELECT 1), killer AS (DELETE FROM likes CASCADE RETURNING *")
#=> PgQuery::ParseError: syntax error at or near ")" (scan.l:1121)
21. replica 属性のコネクション
SQLQL の処理をするときだけ
replica 属性のコネクションを使
えば、Mutations っぽい SQL は
Rails が弾いてくれて便利っぽい
ActiveRecord::Base.connected_to(database: :readonly) do
User.first.update(name: 'hoge')
end
#=> ActiveRecord::ReadOnlyError
#=> (Write query attempted while in readonly mode...
22. WITH は危ないらしい
CTE の WITH 句は Rails 的には
ホワイトリストに入ってないっ
ぽい……
なぜ WITH がホワイトでないかにつ
いては長くなるので割愛します(気
になる人は訊いて下さい
ActiveRecord::Base.connected_to(database: :readonly) do
ActiveRecord::Base.connection.execute(
"WITH t AS (SELECT 1 AS n) SELECT * FROM t"
)
end
#=> ActiveRecord::ReadOnlyError
#=> (Write query attempted while in readonly mode...
23. DB ユーザーの権限
せっかく複数 DB 機能があるん
だから、本当に READONLY な
ユーザーを作って使えばよい
create user readonlyuser with password 'readonlyuser' NOCREATEDB NOCREATEROLE;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO "readonlyuser";
25. SQLQL の脆弱性
Generated Record Bomb
SELECT generate_series(1, 100000000) AS death
Recurring Nightmare
WITH RECURSIVE r AS (
SELECT 1 AS n UNION ALL SELECT n + 1 AS n FROM r)
SELECT * FROM r