{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE Rank2Types #-}
module Opaleye.Internal.Table where
import Opaleye.Internal.Column (Column(Column), unColumn)
import qualified Opaleye.Internal.Tag as Tag
import qualified Opaleye.Internal.Unpackspec as U
import qualified Opaleye.Internal.PrimQuery as PQ
import qualified Opaleye.Internal.PackMap as PM
import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ
import qualified Data.Functor.Identity as I
import Data.Profunctor (Profunctor, dimap, lmap)
import Data.Profunctor.Product (ProductProfunctor)
import qualified Data.Profunctor.Product as PP
import qualified Data.List.NonEmpty as NEL
import Data.Monoid (Monoid, mempty, mappend)
import Data.Semigroup (Semigroup, (<>))
import Control.Applicative (Applicative, pure, (<*>), liftA2)
import qualified Control.Arrow as Arr
data Table writeFields viewFields
= Table String (TableFields writeFields viewFields)
| TableWithSchema String String (TableFields writeFields viewFields)
tableIdentifier :: Table writeColumns viewColumns -> PQ.TableIdentifier
tableIdentifier (Table t _) = PQ.TableIdentifier Nothing t
tableIdentifier (TableWithSchema s t _) = PQ.TableIdentifier (Just s) t
tableColumns :: Table writeColumns viewColumns -> TableFields writeColumns viewColumns
tableColumns (Table _ p) = p
tableColumns (TableWithSchema _ _ p) = p
tableProperties :: Table writeColumns viewColumns -> TableFields writeColumns viewColumns
tableProperties = tableColumns
data TableFields writeColumns viewColumns = TableFields
{ tablePropertiesWriter :: Writer writeColumns viewColumns
, tablePropertiesView :: View viewColumns }
{-# DEPRECATED TableColumns "Use 'TableFields' instead. 'TableColumns' will be removed in version 0.8." #-}
type TableColumns = TableFields
{-# DEPRECATED TableProperties "Use 'TableFields' instead. 'TableProperties' will be removed in version 0.8." #-}
type TableProperties = TableFields
tableColumnsWriter :: TableFields writeColumns viewColumns
-> Writer writeColumns viewColumns
tableColumnsWriter = tablePropertiesWriter
tableColumnsView :: TableFields writeColumns viewColumns
-> View viewColumns
tableColumnsView = tablePropertiesView
{-# DEPRECATED View "Internal only. Do not use. 'View' will be removed in version 0.8." #-}
newtype View columns = View columns
{-# DEPRECATED Writer "Internal only. Do not use. 'Writer' will be removed in 0.8." #-}
newtype Writer columns dummy =
Writer (forall f. Functor f =>
PM.PackMap (f HPQ.PrimExpr, String) () (f columns) ())
requiredTableField :: String -> TableFields (Column a) (Column a)
requiredTableField columnName = TableFields
(requiredW columnName)
(View (Column (HPQ.BaseTableAttrExpr columnName)))
optionalTableField :: String -> TableFields (Maybe (Column a)) (Column a)
optionalTableField columnName = TableFields
(optionalW columnName)
(View (Column (HPQ.BaseTableAttrExpr columnName)))
readOnlyTableField :: String -> TableFields () (Column a)
readOnlyTableField = lmap (const Nothing) . optionalTableField
{-# DEPRECATED required "Use 'requiredTableField' instead. Will be removed in version 0.8." #-}
required :: String -> TableFields (Column a) (Column a)
required = requiredTableField
{-# DEPRECATED optional "Use 'optionalTableField' instead. Will be removed in version 0.8." #-}
optional :: String -> TableFields (Maybe (Column a)) (Column a)
optional = optionalTableField
{-# DEPRECATED readOnly "Use 'readOnlyTableField' instead. Will be removed in version 0.8." #-}
readOnly :: String -> TableFields () (Column a)
readOnly = readOnlyTableField
{-# DEPRECATED tableColumn "Use 'tableField' instead. Will be removed in 0.8." #-}
class TableColumn writeType sqlType | writeType -> sqlType where
tableColumn :: String -> TableFields writeType (Column sqlType)
tableColumn = tableField
tableField :: String -> TableFields writeType (Column sqlType)
instance TableColumn (Column a) a where
tableField = requiredTableField
instance TableColumn (Maybe (Column a)) a where
tableField = optionalTableField
queryTable :: U.Unpackspec viewColumns columns
-> Table writeColumns viewColumns
-> Tag.Tag
-> (columns, PQ.PrimQuery)
queryTable cm table tag = (primExprs, primQ) where
View tableCols = tableColumnsView (tableColumns table)
(primExprs, projcols) = runColumnMaker cm tag tableCols
primQ :: PQ.PrimQuery
primQ = PQ.BaseTable (tableIdentifier table) projcols
runColumnMaker :: U.Unpackspec tablecolumns columns
-> Tag.Tag
-> tablecolumns
-> (columns, [(HPQ.Symbol, HPQ.PrimExpr)])
runColumnMaker cm tag tableCols = PM.run (U.runUnpackspec cm f tableCols) where
f = PM.extractAttrPE mkName tag
mkName pe i = (++ i) $ case pe of
HPQ.BaseTableAttrExpr columnName -> columnName
HPQ.CompositeExpr columnExpr fieldName -> mkName columnExpr i ++ fieldName
_ -> "tablecolumn"
runWriter :: Writer columns columns' -> columns -> [(HPQ.PrimExpr, String)]
runWriter (Writer (PM.PackMap f)) columns = outColumns
where (outColumns, ()) = f extract (I.Identity columns)
extract (pes, s) = ([(I.runIdentity pes, s)], ())
runWriter' :: Writer columns columns' -> NEL.NonEmpty columns -> (NEL.NonEmpty [HPQ.PrimExpr], [String])
runWriter' (Writer (PM.PackMap f)) columns = Arr.first unZip outColumns
where (outColumns, ()) = f extract columns
extract (pes, s) = ((Zip (fmap return pes), [s]), ())
newtype Zip a = Zip { unZip :: NEL.NonEmpty [a] }
instance Semigroup (Zip a) where
Zip xs <> Zip ys = Zip (NEL.zipWith (++) xs ys)
instance Monoid (Zip a) where
mempty = Zip mempty'
where mempty' = [] `NEL.cons` mempty'
mappend = (<>)
requiredW :: String -> Writer (Column a) (Column a)
requiredW columnName =
Writer (PM.iso (flip (,) columnName . fmap unColumn) id)
optionalW :: String -> Writer (Maybe (Column a)) (Column a)
optionalW columnName =
Writer (PM.iso (flip (,) columnName . fmap maybeUnColumn) id)
where maybeUnColumn = maybe HPQ.DefaultInsertExpr unColumn
instance Functor (Writer a) where
fmap _ (Writer g) = Writer g
instance Applicative (Writer a) where
pure _ = Writer (pure ())
Writer f <*> Writer x = Writer (liftA2 (\_ _ -> ()) f x)
instance Profunctor Writer where
dimap f _ (Writer h) = Writer (lmap (fmap f) h)
instance ProductProfunctor Writer where
purePP = pure
(****) = (<*>)
instance Functor (TableFields a) where
fmap f (TableFields w (View v)) = TableFields (fmap f w) (View (f v))
instance Applicative (TableFields a) where
pure x = TableFields (pure x) (View x)
TableFields fw (View fv) <*> TableFields xw (View xv) =
TableFields (fw <*> xw) (View (fv xv))
instance Profunctor TableFields where
dimap f g (TableFields w (View v)) = TableFields (dimap f g w)
(View (g v))
instance ProductProfunctor TableFields where
purePP = pure
(****) = (<*>)
instance Functor (Table a) where
fmap f (Table t tp) = Table t (fmap f tp)
fmap f (TableWithSchema s t tp) = TableWithSchema s t (fmap f tp)
instance Profunctor Table where
dimap f g (Table t tp) = Table t (dimap f g tp)
dimap f g (TableWithSchema s t tp) = TableWithSchema s t (dimap f g tp)