{-# language FlexibleContexts #-}

module Rel8.Query.Rebind
  ( rebind
  )
where

-- base
import Prelude

-- opaleye
import qualified Opaleye.Internal.PackMap as Opaleye
import qualified Opaleye.Internal.PrimQuery as Opaleye
import qualified Opaleye.Internal.QueryArr as Opaleye
import qualified Opaleye.Internal.Tag as Opaleye
import qualified Opaleye.Internal.Unpackspec as Opaleye

-- rel8
import Rel8.Expr ( Expr )
import Rel8.Query ( Query( Query ) )
import Rel8.Table ( Table )
import Rel8.Table.Opaleye ( unpackspec )


-- | 'rebind' takes a variable name, some expressions, and binds each of them
-- to a new variable in the SQL. The @a@ returned consists only of these
-- variables. It's essentially a @let@ binding for Postgres expressions.
rebind :: Table Expr a => String -> a -> Query a
rebind :: String -> a -> Query a
rebind String
prefix a
a = ([PrimExpr] -> Select (Any, a)) -> Query a
forall a. ([PrimExpr] -> Select (Any, a)) -> Query a
Query (([PrimExpr] -> Select (Any, a)) -> Query a)
-> ([PrimExpr] -> Select (Any, a)) -> Query a
forall a b. (a -> b) -> a -> b
$ \[PrimExpr]
_ -> (((), Tag) -> ((Any, a), Lateral -> PrimQuery -> PrimQuery, Tag))
-> Select (Any, a)
forall a b.
((a, Tag) -> (b, Lateral -> PrimQuery -> PrimQuery, Tag))
-> SelectArr a b
Opaleye.QueryArr ((((), Tag) -> ((Any, a), Lateral -> PrimQuery -> PrimQuery, Tag))
 -> Select (Any, a))
-> (((), Tag)
    -> ((Any, a), Lateral -> PrimQuery -> PrimQuery, Tag))
-> Select (Any, a)
forall a b. (a -> b) -> a -> b
$ \(()
_, Tag
tag) ->
  let
    tag' :: Tag
tag' = Tag -> Tag
Opaleye.next Tag
tag
    (a
a', [(Symbol, PrimExpr)]
bindings) = PM [(Symbol, PrimExpr)] a -> (a, [(Symbol, PrimExpr)])
forall a r. PM [a] r -> (r, [a])
Opaleye.run (PM [(Symbol, PrimExpr)] a -> (a, [(Symbol, PrimExpr)]))
-> PM [(Symbol, PrimExpr)] a -> (a, [(Symbol, PrimExpr)])
forall a b. (a -> b) -> a -> b
$
      Unpackspec a a
-> (PrimExpr
    -> StateT ([(Symbol, PrimExpr)], Int) Identity PrimExpr)
-> a
-> PM [(Symbol, PrimExpr)] a
forall (f :: * -> *) columns b.
Applicative f =>
Unpackspec columns b -> (PrimExpr -> f PrimExpr) -> columns -> f b
Opaleye.runUnpackspec Unpackspec a a
forall a. Table Expr a => Unpackspec a a
unpackspec (String
-> Tag
-> PrimExpr
-> StateT ([(Symbol, PrimExpr)], Int) Identity PrimExpr
forall primExpr.
String -> Tag -> primExpr -> PM [(Symbol, primExpr)] PrimExpr
Opaleye.extractAttr String
prefix Tag
tag) a
a
  in
    ((Any
forall a. Monoid a => a
mempty, a
a'), \Lateral
_ -> Bool -> [(Symbol, PrimExpr)] -> PrimQuery -> PrimQuery
forall a.
Bool -> [(Symbol, PrimExpr)] -> PrimQuery' a -> PrimQuery' a
Opaleye.Rebind Bool
True [(Symbol, PrimExpr)]
bindings, Tag
tag')