From f22a11d989a7017c13147ba15b5857fc49652d20 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Fri, 27 Feb 2015 23:00:43 +0100 Subject: [PATCH 1/2] Fix coalesce/coalesceDefault for sub-queries If COALESCE has a sub-query as an argument, then this sub-query needs to remain wrapped in an extra pair of parentheses. --- src/Database/Esqueleto/Internal/Sql.hs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Database/Esqueleto/Internal/Sql.hs b/src/Database/Esqueleto/Internal/Sql.hs index 342b69e..6844d28 100644 --- a/src/Database/Esqueleto/Internal/Sql.hs +++ b/src/Database/Esqueleto/Internal/Sql.hs @@ -376,8 +376,8 @@ instance Esqueleto SqlQuery SqlExpr SqlBackend where min_ = unsafeSqlFunction "MIN" max_ = unsafeSqlFunction "MAX" - coalesce = unsafeSqlFunction "COALESCE" - coalesceDefault exprs = unsafeSqlFunction "COALESCE" . (exprs ++) . return . just + coalesce = unsafeSqlFunctionParens "COALESCE" + coalesceDefault exprs = unsafeSqlFunctionParens "COALESCE" . (exprs ++) . return . just like = unsafeSqlBinOp " LIKE " (%) = unsafeSqlValue "'%'" @@ -524,6 +524,16 @@ unsafeSqlExtractSubField subField arg = uncommas' $ map (\(ERaw _ f) -> f info) $ toArgList arg in ("EXTRACT" <> parens (subField <> " FROM " <> argsTLB), argsVals) +-- | (Internal) A raw SQL function. Preserves parentheses around arguments. +-- See 'unsafeSqlBinOp' for warnings. +unsafeSqlFunctionParens :: UnsafeSqlFunctionArgument a => + TLB.Builder -> a -> SqlExpr (Value b) +unsafeSqlFunctionParens name arg = + ERaw Never $ \info -> + let (argsTLB, argsVals) = + uncommas' $ map (\(ERaw p f) -> first (parensM p) (f info)) $ toArgList arg + in (name <> parens argsTLB, argsVals) + class UnsafeSqlFunctionArgument a where toArgList :: a -> [SqlExpr (Value ())] From 0d76e0e09032745088d3fcc56861820e936be244 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Fri, 27 Feb 2015 22:11:24 +0100 Subject: [PATCH 2/2] added test: coalesceDefault with sub_select The generated SQL code needs to wrap the sub-query in an extra pair of parentheses. I.e. the following is invalid SQL: COALESCE( SELECT ... , ... ) This is the correct syntax: COALESCE( (SELECT ...) , ... ) --- test/Test.hs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/Test.hs b/test/Test.hs index ce9cecf..2a4d6af 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -592,6 +592,28 @@ main = do , Value 5 ] + it "works with sub-queries" $ + run $ do + p1id <- insert p1 + p2id <- insert p2 + p3id <- insert p3 + _ <- insert p4 + _ <- insert p5 + _ <- insert $ BlogPost "a" p1id + _ <- insert $ BlogPost "b" p2id + _ <- insert $ BlogPost "c" p3id + ret <- select $ + from $ \b -> do + let sub = + from $ \p -> do + where_ (p ^. PersonId ==. b ^. BlogPostAuthorId) + return $ p ^. PersonAge + return $ coalesceDefault [sub_select sub] (val (42 :: Int)) + liftIO $ ret `shouldBe` [ Value (36 :: Int) + , Value 42 + , Value 17 + ] + #if defined(WITH_POSTGRESQL) || defined(WITH_MYSQL) it "works on PostgreSQL and MySQL with <2 arguments" $ run $ do