{-| Module: Squeal.PostgreSQL.Expression.Subquery Description: subquery expressions Copyright: (c) Eitan Chatav, 2019 Maintainer: eitan@morphism.tech Stability: experimental subquery expressions -} {-# LANGUAGE DataKinds , OverloadedStrings , RankNTypes , TypeOperators #-} module Squeal.PostgreSQL.Expression.Subquery ( -- * Subquery exists , in_ , notIn , subAll , subAny ) where import Squeal.PostgreSQL.Type.Alias import Squeal.PostgreSQL.Expression import Squeal.PostgreSQL.Expression.Logic import Squeal.PostgreSQL.Type.List import Squeal.PostgreSQL.Query import Squeal.PostgreSQL.Render import Squeal.PostgreSQL.Type.Schema -- $setup -- >>> import Squeal.PostgreSQL {- | The argument of `exists` is an arbitrary subquery. The subquery is evaluated to determine whether it returns any rows. If it returns at least one row, the result of `exists` is `true`; if the subquery returns no rows, the result of `exists` is `false`. The subquery can refer to variables from the surrounding query, which will act as constants during any one evaluation of the subquery. The subquery will generally only be executed long enough to determine whether at least one row is returned, not all the way to completion. -} exists :: Query (Join lat from) with db params row -- ^ subquery -> Expression grp lat with db params from (null 'PGbool) exists query = UnsafeExpression $ "EXISTS" <+> parenthesized (renderSQL query) {- | The right-hand side is a parenthesized subquery, which must return exactly one column. The left-hand expression is evaluated and compared to each row of the subquery result using the given `Operator`, which must yield a Boolean result. The result of `subAll` is `true` if all rows yield true (including the case where the subquery returns no rows). The result is `false` if any `false` result is found. The result is `Squeal.PostgreSQL.Expression.Null.null_` if no comparison with a subquery row returns `false`, and at least one comparison returns `Squeal.PostgreSQL.Expression.Null.null_`. >>> printSQL $ subAll true (.==) (values_ (true `as` #foo)) (TRUE = ALL (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))) -} subAll :: Expression grp lat with db params from ty1 -- ^ expression -> Operator ty1 ty2 ('Null 'PGbool) -- ^ operator -> Query (Join lat from) with db params '[col ::: ty2] -- ^ subquery -> Condition grp lat with db params from subAll expr (?) qry = expr ? (UnsafeExpression $ "ALL" <+> parenthesized (renderSQL qry)) {- | The right-hand side is a parenthesized subquery, which must return exactly one column. The left-hand expression is evaluated and compared to each row of the subquery result using the given `Operator`, which must yield a Boolean result. The result of `subAny` is `true` if any `true` result is obtained. The result is `false` if no true result is found (including the case where the subquery returns no rows). >>> printSQL $ subAny "foo" like (values_ ("foobar" `as` #foo)) ((E'foo' :: text) LIKE ANY (SELECT * FROM (VALUES ((E'foobar' :: text))) AS t ("foo"))) -} subAny :: Expression grp lat with db params from ty1 -- ^ expression -> Operator ty1 ty2 ('Null 'PGbool) -- ^ operator -> Query (Join lat from) with db params '[col ::: ty2] -- ^ subquery -> Condition grp lat with db params from subAny expr (?) qry = expr ? (UnsafeExpression $ "ANY" <+> parenthesized (renderSQL qry)) {- | The result is `true` if the left-hand expression's result is equal to any of the right-hand expressions. >>> printSQL $ true `in_` [true, false, null_] TRUE IN (TRUE, FALSE, NULL) -} in_ :: Expression grp lat with db params from ty -- ^ expression -> [Expression grp lat with db params from ty] -> Expression grp lat with db params from ('Null 'PGbool) _ `in_` [] = false expr `in_` exprs = UnsafeExpression $ renderSQL expr <+> "IN" <+> parenthesized (commaSeparated (renderSQL <$> exprs)) {- | The result is `true` if the left-hand expression's result is not equal to any of the right-hand expressions. >>> printSQL $ true `notIn` [false, null_] TRUE NOT IN (FALSE, NULL) -} notIn :: Expression grp lat with db params from ty -- ^ expression -> [Expression grp lat with db params from ty] -> Expression grp lat with db params from ('Null 'PGbool) _ `notIn` [] = true expr `notIn` exprs = UnsafeExpression $ renderSQL expr <+> "NOT IN" <+> parenthesized (commaSeparated (renderSQL <$> exprs))