简体   繁体   English

如何将呈现的持久性/esqueleto 查询传递给另一个查询?

[英]How do I pass a rendered persistent/esqueleto query to another query?

I'd like to use Persistent/Esqueleto to implement count estimates.我想使用 Persistent/Esqueleto 来实现计数估计。

One approach recommended in this article is to define a function like this 本文推荐的一种方法是像这样定义 function

CREATE FUNCTION count_estimate(query text) RETURNS integer AS $$
DECLARE
  rec   record;
  rows  integer;
BEGIN
  FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP
    rows := substring(rec."QUERY PLAN" FROM ' rows=([[:digit:]]+)');
    EXIT WHEN rows IS NOT NULL;
  END LOOP;
  RETURN rows;
END;
$$ LANGUAGE plpgsql VOLATILE STRICT;

and then use it like this然后像这样使用它

SELECT count_estimate('SELECT * FROM companies WHERE status = ''Active''');

In order to use the count_estimate function, I'll need (I think?) to render the query that Peristent/Equeleto generates, however when I try rendering the query with renderQuerySelect , I get something like this为了使用count_estimate function,我需要(我认为?)呈现 Peristent/Equeleto 生成的查询,但是当我尝试使用renderQuerySelect呈现查询时,我得到了这样的结果

SELECT "companies"."id", "companies"."name", "companies"."status"
FROM "companies"
WHERE "companies"."status" IN (?)
; [PersistText "Active"]

This of course can't be stuffed into the count_estimate , because it will syntax error on the ?这当然不能塞进count_estimate中,因为它会在? placeholder.占位符。 I also can't naïvely replace the ?我也不能天真地替换? with "Active" , because it will syntax error on that first double quote.使用"Active" ,因为它会在第一个双引号上出现语法错误。

How do I render the query in a way that my count_estimate function will accept?如何以我的count_estimate function 将接受的方式呈现查询?

I tried something like this, but it fails at runtime我试过这样的东西,但它在运行时失败

getEstimate :: (Text, [PersistValue]) -> DB [Single Int]
getEstimate (query, params) = rawSql [st|
  SELECT count_estimate('#{query}');
  |] params

I managed to figure it out (mostly).我设法弄清楚(大部分)。

It's a matter of escaping the single quotes in both the query and the PersistValue parameters.这是查询和PersistValue参数中的单引号 escaping 的问题。 I'm doing it like this at the moment, but escaping will need to be added back in otherwise I think it creates a SQL injection vulnerability.我目前正在这样做,但需要重新添加 escaping 否则我认为它会创建 SQL 注入漏洞。 I may also need to handle the other PersistValue constructors in some specific way, but I haven't run into problems there yet.我可能还需要以某种特定方式处理其他PersistValue构造函数,但我还没有遇到问题。

import qualified Data.Text as T
import qualified Database.Persist as P

getEstimate :: (Text, [PersistValue]) -> DB (Maybe Int)
getEstimate (query, params) = fmap unSingle . listToMaybe <$> rawSql [st|
  SELECT count_estimate('#{T.replace "'" "''" query}');
  |] (map replace' params)
  where literal a = PersistLiteral_ P.Unescaped ("''" <> a <> "''")
        replace' = \case
          PersistText t -> literal $ encodeUtf8 t
          PersistDay  d -> literal $ encodeUtf8 $ pack $ showGregorian d
          a             -> a

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM