fix: using " and \ as values on the in filter

Properly escape the array literal built at the QueryBuilder
stage.
This commit is contained in:
steve-chavez
2021-09-13 19:06:30 -05:00
committed by Steve Chavez
parent 0ba133a0bd
commit 4a594d5e95
4 changed files with 38 additions and 14 deletions
+13 -6
View File
@@ -123,7 +123,8 @@ normalizedBody body =
singleParameter :: Maybe BL.ByteString -> ByteString -> H.Snippet
singleParameter body typ =
if typ == "bytea"
then H.encoderAndParam (HE.nullable HE.bytea) (toS <$> body) -- needed because bytea fails with HE.unknown(pg tries to utf8 encode)
-- TODO: Hasql fails when using HE.unknown with bytea(pg tries to utf8 encode).
then H.encoderAndParam (HE.nullable HE.bytea) (toS <$> body)
else H.encoderAndParam (HE.nullable HE.unknown) (toS <$> body) <> "::" <> H.sql typ
selectBody :: SqlFragment
@@ -138,6 +139,15 @@ pgFmtLit x =
then "E" <> slashed
else slashed
-- Here we build the pg array literal, e.g '{"Hebdon, John","Other","Another"}', manually.
-- This is necessary to pass an "unknown" array and let pg infer the type.
pgFmtArrayLit :: [Text] -> Text
pgFmtArrayLit vals =
let trimmed = trimNullChars
slashed = T.replace "\\" "\\\\" . trimmed
escaped x = "\"" <> T.replace "\"" "\\\"" (slashed x) <> "\"" in
"{" <> T.intercalate "," (escaped <$> vals) <> "}"
-- TODO: refactor by following https://github.com/PostgREST/postgrest/pull/1631#issuecomment-711070833
pgFmtIdent :: Text -> SqlFragment
pgFmtIdent x = encodeUtf8 $ "\"" <> T.replace "\"" "\"\"" (trimNullChars x) <> "\""
@@ -220,12 +230,9 @@ pgFmtFilter table (Filter fld (OpExpr hasNot oper)) = notOp <> " " <> case oper
-- We don't use "IN", we use "= ANY". IN has the following disadvantages:
-- + No way to use an empty value on IN: "col IN ()" is invalid syntax. With ANY we can do "= ANY('{}')"
-- + Can invalidate prepared statements: multiple parameters on an IN($1, $2, $3) will lead to using different prepared statements and not take advantage of caching.
In vals -> pgFmtField table fld <> " " <>
case vals of
In vals -> pgFmtField table fld <> " " <> case vals of
[""] -> "= ANY('{}') "
-- Here we build the pg array, e.g '{"Hebdon, John","Other","Another"}', manually. We quote the values to prevent the "," being treated as an element separator.
-- TODO: Ideally this would be done on Hasql with an encoder, but the "array unknown" is not working(Hasql doesn't pass any value).
_ -> "= ANY (" <> unknownLiteral ("{" <> T.intercalate "," ((\x -> "\"" <> x <> "\"") <$> vals) <> "}") <> ")"
_ -> "= ANY (" <> unknownLiteral (pgFmtArrayLit vals) <> ") "
Fts op lang val ->
pgFmtFieldOp op <> "(" <> ftsLang lang <> unknownLiteral val <> ") "