{-# language FlexibleContexts #-}
{-# language MonoLocalBinds #-}

module Rel8.Expr.Subscript
  ( unsafeSubscript
  , unsafeSubscripts
  )
where

-- base
import Data.Foldable (foldl')
import Prelude

-- opaleye
import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye

-- rel8
import Rel8.Expr (Expr)
import Rel8.Expr.Opaleye (fromPrimExpr, toPrimExpr)
import Rel8.Schema.HTable (hfoldMap)
import Rel8.Schema.Null (Sql, Unnullify)
import Rel8.Table (Table, toColumns)
import Rel8.Type (DBType, typeInformation)
import Rel8.Type.Array (extractArrayElement)
import Rel8.Type.Information (TypeInformation)


-- | @'unsafeSubscript' a i@ will generate the SQL @a[i]@.
--
-- Note that this function is not type checked and the generated SQL has no
-- casts. This is only intended an escape hatch to be used if Rel8 cannot
-- otherwise express the expression you need. If you find yourself using this
-- function, please let us know, as it may indicate that something is missing
-- from Rel8!
unsafeSubscript :: Sql DBType b => Expr a -> Expr i -> Expr b
unsafeSubscript :: forall b a i. Sql DBType b => Expr a -> Expr i -> Expr b
unsafeSubscript = TypeInformation (Unnullify' (IsMaybe b) b)
-> Expr a -> Expr i -> Expr b
forall b a i.
TypeInformation (Unnullify b) -> Expr a -> Expr i -> Expr b
sunsafeSubscript TypeInformation (Unnullify' (IsMaybe b) b)
forall a. DBType a => TypeInformation a
typeInformation


-- | @'unsafeSubscripts' a (i, j)@ will generate the SQL @a[i][j]@.
--
-- Note that this function is not type checked and the generated SQL has no
-- casts. This is only intended an escape hatch to be used if Rel8 cannot
-- otherwise express the expression you need. If you find yourself using this
-- function, please let us know, as it may indicate that something is missing
-- from Rel8!
unsafeSubscripts :: (Table Expr i, Sql DBType b) => Expr a -> i -> Expr b
unsafeSubscripts :: forall i b a. (Table Expr i, Sql DBType b) => Expr a -> i -> Expr b
unsafeSubscripts = TypeInformation (Unnullify' (IsMaybe b) b) -> Expr a -> i -> Expr b
forall i b a.
Table Expr i =>
TypeInformation (Unnullify b) -> Expr a -> i -> Expr b
sunsafeSubscripts TypeInformation (Unnullify' (IsMaybe b) b)
forall a. DBType a => TypeInformation a
typeInformation


sunsafeSubscript :: TypeInformation (Unnullify b) -> Expr a -> Expr i -> Expr b
sunsafeSubscript :: forall b a i.
TypeInformation (Unnullify b) -> Expr a -> Expr i -> Expr b
sunsafeSubscript TypeInformation (Unnullify b)
info Expr a
array Expr i
i =
  PrimExpr -> Expr b
forall a. PrimExpr -> Expr a
fromPrimExpr (PrimExpr -> Expr b)
-> (PrimExpr -> PrimExpr) -> PrimExpr -> Expr b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TypeInformation (Unnullify b) -> PrimExpr -> PrimExpr
forall a. TypeInformation a -> PrimExpr -> PrimExpr
extractArrayElement TypeInformation (Unnullify b)
info (PrimExpr -> Expr b) -> PrimExpr -> Expr b
forall a b. (a -> b) -> a -> b
$
    PrimExpr -> PrimExpr -> PrimExpr
Opaleye.ArrayIndex (Expr a -> PrimExpr
forall a. Expr a -> PrimExpr
toPrimExpr Expr a
array) (Expr i -> PrimExpr
forall a. Expr a -> PrimExpr
toPrimExpr Expr i
i)


sunsafeSubscripts :: Table Expr i => TypeInformation (Unnullify b) -> Expr a -> i -> Expr b
sunsafeSubscripts :: forall i b a.
Table Expr i =>
TypeInformation (Unnullify b) -> Expr a -> i -> Expr b
sunsafeSubscripts TypeInformation (Unnullify b)
info Expr a
array i
i =
  PrimExpr -> Expr b
forall a. PrimExpr -> Expr a
fromPrimExpr (PrimExpr -> Expr b) -> PrimExpr -> Expr b
forall a b. (a -> b) -> a -> b
$ TypeInformation (Unnullify b) -> PrimExpr -> PrimExpr
forall a. TypeInformation a -> PrimExpr -> PrimExpr
extractArrayElement TypeInformation (Unnullify b)
info (PrimExpr -> PrimExpr) -> PrimExpr -> PrimExpr
forall a b. (a -> b) -> a -> b
$ Expr a -> [PrimExpr] -> PrimExpr
forall a. Expr a -> [PrimExpr] -> PrimExpr
primSubscripts Expr a
array [PrimExpr]
indices
  where
    indices :: [PrimExpr]
indices = (forall a. Expr a -> [PrimExpr]) -> Columns i Expr -> [PrimExpr]
forall (t :: HTable) s (context :: Context).
(HTable t, Semigroup s) =>
(forall a. context a -> s) -> t context -> s
hfoldMap (PrimExpr -> [PrimExpr]
forall a. a -> [a]
forall (f :: Context) a. Applicative f => a -> f a
pure (PrimExpr -> [PrimExpr])
-> (Expr a -> PrimExpr) -> Expr a -> [PrimExpr]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Expr a -> PrimExpr
forall a. Expr a -> PrimExpr
toPrimExpr) (Columns i Expr -> [PrimExpr]) -> Columns i Expr -> [PrimExpr]
forall a b. (a -> b) -> a -> b
$ i -> Columns i Expr
forall (context :: Context) a.
Table context a =>
a -> Columns a context
toColumns i
i


primSubscripts :: Expr a -> [Opaleye.PrimExpr] -> Opaleye.PrimExpr
primSubscripts :: forall a. Expr a -> [PrimExpr] -> PrimExpr
primSubscripts Expr a
array [PrimExpr]
indices =
  (PrimExpr -> PrimExpr -> PrimExpr)
-> PrimExpr -> [PrimExpr] -> PrimExpr
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: Context) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' PrimExpr -> PrimExpr -> PrimExpr
Opaleye.ArrayIndex (Expr a -> PrimExpr
forall a. Expr a -> PrimExpr
toPrimExpr Expr a
array) [PrimExpr]
indices