{-# LANGUAGE OverloadedStrings, FlexibleInstances, UndecidableInstances #-}
{-# LANGUAGE ScopedTypeVariables, TypeOperators, GADTs, FlexibleContexts #-}
{-# LANGUAGE DeriveGeneric, GeneralizedNewtypeDeriving, TypeFamilies, CPP #-}
{-# LANGUAGE DataKinds #-}
module Database.Selda
(
MonadSelda, Backend
, SeldaError (..), ValidationError
, SeldaT, SeldaM
, Relational, Only (..), The (..)
, Table (tableName), Query, Row, Col, Res, Result
, query, queryInto
, transaction, withoutForeignKeyEnforcement
, newUuid
, SqlType (..), SqlRow (..), SqlEnum (..)
, Columns, Same
, Order (..)
, (:*:)(..)
, select, selectValues, from, distinct
, restrict, limit
, order, ascending, descending
, orderRandom
, inner, suchThat
, Selector, Coalesce
, HasField, FieldType, IsLabel
, (!), (?), Assignment ((:=)), with
, (+=), (-=), (*=), (||=), (&&=), ($=)
, Set (..)
, ID, invalidId, isInvalidId, untyped, fromId, toId
, RowID, invalidRowId, isInvalidRowId, fromRowId, toRowId
, (.==), (./=), (.>), (.<), (.>=), (.<=), like
, (.&&), (.||), not_
, literal, is, int, float, text, true, false, null_
, roundTo, length_, isNull, ifThenElse, ifNull, matchNull
, new, only
, Mappable (..)
, round_, just, fromBool, fromInt, toString
, Aggr, Aggregates, OuterCols, AggrCols, LeftCols, Inner, SqlOrd
, innerJoin, leftJoin
, aggregate, groupBy
, count, avg, sum_, max_, min_
, insert, insert_, insertWithPK, tryInsert, insertUnless, insertWhen, def
, update, update_, upsert
, deleteFrom, deleteFrom_
, Preparable, Prepare
, prepared
, Generic
, TableName, ColName, Attr (..), Attribute
, ForeignKey (..)
, SelectorLike, Group (..), sel
, table, tableFieldMod
, primary, autoPrimary, weakAutoPrimary
, untypedAutoPrimary, weakUntypedAutoPrimary
, unique
, IndexMethod (..), index, indexUsing
, createTable, tryCreateTable
, dropTable, tryDropTable
, Tup, Head
, first, second, third, fourth, fifth
, MonadIO, MonadMask, liftIO
, Text, Day, TimeOfDay, UTCTime, UUID
) where
import Control.Monad.Catch (MonadMask)
import Data.Typeable (Typeable)
import Database.Selda.Backend
import Database.Selda.Column
import Database.Selda.Compile
import Database.Selda.FieldSelectors
import Database.Selda.Frontend
import Database.Selda.Generic
import Database.Selda.Inner
import Database.Selda.Prepared
import Database.Selda.Query
import Database.Selda.Query.Type
import Database.Selda.Selectors
import Database.Selda.SQL hiding (distinct)
import Database.Selda.SqlRow
import Database.Selda.Table
import Database.Selda.Table.Validation
import Database.Selda.Types
import Database.Selda.Unsafe
import Data.Proxy
import Data.String (IsString)
import Data.Text (Text)
import Data.Time (Day, TimeOfDay, UTCTime)
import Data.Typeable (eqT, (:~:)(..))
import GHC.Generics (Rep)
import Unsafe.Coerce
import System.Random (randomIO)
import GHC.TypeLits as TL
class SqlType a => SqlOrd a
instance {-# OVERLAPPABLE #-} (SqlType a, Num a) => SqlOrd a
instance SqlOrd RowID
instance SqlOrd Text
instance SqlOrd Day
instance SqlOrd UTCTime
instance SqlOrd TimeOfDay
instance SqlOrd a => SqlOrd (Maybe a)
instance Typeable a => SqlOrd (ID a)
newtype Only a = Only a
deriving
( Generic
, Show
, Read
, Eq
, Ord
, Enum
, Num
, Integral
, Fractional
, Real
, IsString
)
instance SqlType a => SqlRow (Only a)
instance (TypeError
( 'TL.Text "'Only " ':<>: 'ShowType a ':<>: 'TL.Text "' is not a proper SQL type."
':$$: 'TL.Text "Use 'the' to access the value of the column."
), Typeable a) => SqlType (Only a) where
mkLit = error "unreachable"
sqlType = error "unreachable"
fromSql = error "unreachable"
defaultValue = error "unreachable"
newUuid :: MonadIO m => m UUID
newUuid = liftIO randomIO
sel :: Selector t a -> Selector t a
sel = id
(+=) :: (SqlType a, Num (Col s a)) => Selector t a -> Col s a -> Assignment s t
s += c = s $= (+ c)
infixl 2 +=
(-=) :: (SqlType a, Num (Col s a)) => Selector t a -> Col s a -> Assignment s t
s -= c = s $= (\x -> x - c)
infixl 2 -=
(*=) :: (SqlType a, Num (Col s a)) => Selector t a -> Col s a -> Assignment s t
s *= c = s $= (* c)
infixl 2 *=
(||=) :: Selector t Bool -> Col s Bool -> Assignment s t
s ||= c = s $= (.|| c)
infixl 2 ||=
(&&=) :: Selector t Bool -> Col s Bool -> Assignment s t
s &&= c = s $= (.&& c)
infixl 2 &&=
class The a where
type TheOnly a
the :: a -> TheOnly a
instance The (Only a) where
type TheOnly (Only a) = a
the (Only x) = x
instance The (Row s (Only a)) where
type TheOnly (Row s (Only a)) = Col s a
the (Many [Untyped x]) = One (unsafeCoerce x)
the (Many _) = error "BUG: non-singleton Only-column"
only :: SqlType a => Col s a -> Row s (Only a)
only (One x) = Many [Untyped x]
new :: forall s a. Relational a => [Assignment s a] -> Row s a
new fields = Many (gNew (Proxy :: Proxy (Rep a))) `with` fields
from :: (Typeable t, SqlType a)
=> Selector t a
-> Query s (Row s t)
-> Query s (Col s a)
from s q = (! s) <$> q
infixr 7 `from`
inner :: (Columns a, Columns (OuterCols a))
=> Query (Inner s) a
-> Query s (OuterCols a)
inner = innerJoin (const true)
suchThat :: (Columns a, Columns (OuterCols a))
=> Query (Inner s) a
-> (a -> Col (Inner s) Bool)
-> Query s (OuterCols a)
suchThat q p = inner $ do
x <- q
restrict (p x)
return x
infixr 7 `suchThat`
(.==), (./=) :: (Same s t, SqlType a) => Col s a -> Col t a -> Col s Bool
(.>), (.<), (.>=), (.<=) :: (Same s t, SqlOrd a) => Col s a -> Col t a -> Col s Bool
(.==) = liftC2 $ BinOp Eq
(./=) = liftC2 $ BinOp Neq
(.>) = liftC2 $ BinOp Gt
(.<) = liftC2 $ BinOp Lt
(.>=) = liftC2 $ BinOp Gte
(.<=) = liftC2 $ BinOp Lte
infixl 4 .==
infixl 4 ./=
infixl 4 .>
infixl 4 .<
infixl 4 .>=
infixl 4 .<=
isNull :: SqlType a => Col s (Maybe a) -> Col s Bool
isNull = liftC $ UnOp IsNull
matchNull :: (SqlType a, SqlType b, Same s t)
=> Col s b
-> (Col s a -> Col s b)
-> Col t (Maybe a)
-> Col s b
matchNull nullvalue f x = ifThenElse (isNull x) nullvalue (f (cast x))
ifNull :: (Same s t, SqlType a) => Col s a -> Col t (Maybe a) -> Col s a
ifNull nullvalue x = ifThenElse (isNull x) nullvalue (cast x)
class Mappable f where
type Container f a
(.<$>) :: (SqlType a, SqlType b)
=> (Col s a -> Col s b)
-> f s (Container f a)
-> f s (Container f b)
infixl 4 .<$>
instance Mappable Aggr where
type Container Aggr a = a
(.<$>) = liftAggr
instance Mappable Col where
type Container Col a = Maybe a
f .<$> mx = cast (f (cast mx))
class Set set where
isIn :: (Same s t, SqlType a) => Col s a -> set (Col t a) -> Col s Bool
infixl 4 `isIn`
instance Set [] where
isIn _ [] = false
isIn (One x) xs = One $ InList x [c | One c <- xs]
instance Set (Query s) where
isIn (One x) = One . InQuery x . snd . compQueryWithFreshScope
(.&&), (.||) :: Same s t => Col s Bool -> Col t Bool -> Col s Bool
(.&&) = liftC2 $ BinOp And
(.||) = liftC2 $ BinOp Or
infixr 3 .&&
infixr 2 .||
ascending, descending :: Order
ascending = Asc
descending = Desc
just :: SqlType a => Col s a -> Col s (Maybe a)
just = cast
is :: forall r s c. SqlType c => Selector r c -> c -> Row s r -> Col s Bool
is s x r = r ! s .== (literal x :: Col s c)
null_ :: SqlType a => Col s (Maybe a)
null_ = literal Nothing
int :: Int -> Col s Int
int = literal
float :: Double -> Col s Double
float = literal
text :: Text -> Col s Text
text = literal
true, false :: Col s Bool
true = literal True
false = literal False
like :: Same s t => Col s Text -> Col t Text -> Col s Bool
like = liftC2 $ BinOp Like
infixl 4 `like`
count :: SqlType a => Col s a -> Aggr s Int
count = aggr "COUNT"
avg :: (SqlType a, Num a) => Col s a -> Aggr s (Maybe a)
avg = aggr "AVG"
max_ :: SqlOrd a => Col s a -> Aggr s (Maybe a)
max_ = aggr "MAX"
min_ :: SqlOrd a => Col s a -> Aggr s (Maybe a)
min_ = aggr "MIN"
sum_ :: forall a b s. (SqlType a, SqlType b, Num a, Num b) => Col s a -> Aggr s b
sum_ = liftAggr (ifNull (0::Col s b) . cast) . aggr "SUM"
round_ :: forall s a. (SqlType a, Num a) => Col s Double -> Col s a
round_ =
case eqT :: Maybe (a :~: Double) of
Just Refl -> fun "ROUND"
_ -> cast . fun "ROUND"
roundTo :: Col s Int -> Col s Double -> Col s Double
roundTo = flip $ fun2 "ROUND"
length_ :: Col s Text -> Col s Int
length_ = fun "LENGTH"
not_ :: Col s Bool -> Col s Bool
not_ = liftC $ UnOp Not
fromBool :: (SqlType a, Num a) => Col s Bool -> Col s a
fromBool = cast
fromInt :: (SqlType a, Num a) => Col s Int -> Col s a
fromInt = cast
toString :: SqlType a => Col s a -> Col s Text
toString = cast
ifThenElse :: (Same s t, Same t u, SqlType a) => Col s Bool -> Col t a -> Col u a -> Col s a
ifThenElse = liftC3 If