From 8c35fce90f1722c3696c1666acfe1dc8a5d89e36 Mon Sep 17 00:00:00 2001 From: Alexander Ljungberg Date: Wed, 8 Feb 2023 12:51:18 +0000 Subject: [PATCH] New: use a subquery to interpret `IN` literals requiring data rep transformation. - With the previous method, very long queries such as `ANY (ARRAY[test.color('000100'), test.color('CAFE12'), test.color('01E240'), ...` could be generated. Consider the case where the parser function name is 45 characters and there's a hundred literals. That's 4.5kB of SQL just for the function name alone! - New version uses `unnest`: `ANY (SELECT test.color(unnest('{000100,CAFE12,01E240,...}'::text[]))` to produce a much shorter query. - This is likely to be more performant and either way much more readable and debuggable in the logs. --- src/PostgREST/Query/SqlFragment.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/PostgREST/Query/SqlFragment.hs b/src/PostgREST/Query/SqlFragment.hs index 3c70ed47ad..3b756641e4 100644 --- a/src/PostgREST/Query/SqlFragment.hs +++ b/src/PostgREST/Query/SqlFragment.hs @@ -297,8 +297,10 @@ pgFmtUnknownLiteralForField value _ = value -- | Array version of the above, used by ANY(). pgFmtArrayLiteralForField :: [Text] -> CoercibleField -> SQL.Snippet -pgFmtArrayLiteralForField values CoercibleField{cfTransform=(Just parserProc)} = SQL.sql "ARRAY[" <> intercalateSnippet ", " (pgFmtCallUnary parserProc . unknownLiteral <$> values) <> "]" --- When no transformation is requested, use an array literal which should be simpler, maybe faster. +-- When a transformation is requested, we need to apply the transformation to each element of the array. This could be done by just making a query with `parser(value)` for each value, but may lead to huge query lengths. Imagine `data_representations.color_from_text('...'::text)` for repeated for a hundred values. Instead we use `unnest()` to unpack a standard array literal and then apply the transformation to each element, like a map. +-- Note the literals will be treated as text since in every case when we use ANY() the parameters are textual (coming from a query string). We want to rely on the `text->domain` parser to do the right thing. +pgFmtArrayLiteralForField values CoercibleField{cfTransform=(Just parserProc)} = SQL.sql "(SELECT " <> pgFmtCallUnary parserProc (SQL.sql "unnest(" <> unknownLiteral (pgBuildArrayLiteral values) <> "::text[])") <> ")" +-- When no transformation is requested, we don't need a subquery. pgFmtArrayLiteralForField values _ = unknownLiteral (pgBuildArrayLiteral values) pgFmtFilter :: QualifiedIdentifier -> CoercibleFilter -> SQL.Snippet