diff --git a/src/Database/Esqueleto/Internal/Sql.hs b/src/Database/Esqueleto/Internal/Sql.hs index 3bda565..4876334 100644 --- a/src/Database/Esqueleto/Internal/Sql.hs +++ b/src/Database/Esqueleto/Internal/Sql.hs @@ -404,8 +404,8 @@ instance Esqueleto SqlQuery SqlExpr SqlBackend where avg_ = unsafeSqlFunction "AVG" lower_ = unsafeSqlFunction "LOWER" - coalesce = unsafeSqlFunction "COALESCE" - coalesceDefault exprs = unsafeSqlFunction "COALESCE" . (exprs ++) . return . just + coalesce = unsafeSqlFunctionParens "COALESCE" + coalesceDefault exprs = unsafeSqlFunctionParens "COALESCE" . (exprs ++) . return . just like = unsafeSqlBinOp " LIKE " (%) = unsafeSqlValue "'%'" @@ -584,6 +584,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 ())] diff --git a/test/Test.hs b/test/Test.hs index 22ffb41..5c8d379 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -798,6 +798,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