module Opaleye.Exists (exists) where

import           Opaleye.Field (Field)
import           Opaleye.Internal.Column (Field_(Column))
import           Opaleye.Internal.QueryArr (productQueryArr, runSimpleSelect)
import           Opaleye.Internal.PackMap (run, extractAttr)
import           Opaleye.Internal.PrimQuery (PrimQuery' (Exists))
import           Opaleye.Internal.Tag (fresh)
import           Opaleye.Select (Select)
import           Opaleye.SqlTypes (SqlBool)

-- | True if any rows are returned by the given query, false otherwise.
--
-- This operation is equivalent to Postgres's @EXISTS@ operator.
exists :: Select a -> Select (Field SqlBool)
exists :: Select a -> Select (Field SqlBool)
exists Select a
q = State Tag (Field SqlBool, PrimQuery) -> Select (Field SqlBool)
forall a. State Tag (a, PrimQuery) -> Query a
productQueryArr (State Tag (Field SqlBool, PrimQuery) -> Select (Field SqlBool))
-> State Tag (Field SqlBool, PrimQuery) -> Select (Field SqlBool)
forall a b. (a -> b) -> a -> b
$ do
  (a
_, PrimQuery
query) <- Select a -> State Tag (a, PrimQuery)
forall a. Select a -> State Tag (a, PrimQuery)
runSimpleSelect Select a
q
  Tag
tag <- State Tag Tag
fresh
  let (PrimExpr
result, [(Symbol
binding, ())]) = PM [(Symbol, ())] PrimExpr -> (PrimExpr, [(Symbol, ())])
forall a r. PM [a] r -> (r, [a])
run (String -> Tag -> () -> PM [(Symbol, ())] PrimExpr
forall primExpr.
String -> Tag -> primExpr -> PM [(Symbol, primExpr)] PrimExpr
extractAttr String
"exists" Tag
tag ())
  (Field SqlBool, PrimQuery) -> State Tag (Field SqlBool, PrimQuery)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (PrimExpr -> Field SqlBool
forall (n :: Nullability) sqlType. PrimExpr -> Field_ n sqlType
Column PrimExpr
result, Symbol -> PrimQuery -> PrimQuery
forall a. Symbol -> PrimQuery' a -> PrimQuery' a
Exists Symbol
binding PrimQuery
query)