{-# LANGUAGE GADTs, OverloadedStrings, ScopedTypeVariables #-} {-# LANGUAGE TypeOperators, FlexibleInstances, UndecidableInstances #-} -- | SQL AST and parameters for prepared statements. module Database.Selda.SQL where import Database.Selda.Column import Database.Selda.SqlType import Database.Selda.Types import Control.Exception import Data.Monoid import System.IO.Unsafe -- | A source for an SQL query. data SqlSource = TableName !TableName | Product ![SQL] | LeftJoin !(Exp Bool) !SQL !SQL | Values ![SomeCol] ![[Param]] | EmptyTable -- | AST for SQL queries. data SQL = SQL { cols :: ![SomeCol] , source :: !SqlSource , restricts :: ![Exp Bool] , groups :: ![SomeCol] , ordering :: ![(Order, SomeCol)] , limits :: !(Maybe (Int, Int)) } -- | The order in which to sort result rows. data Order = Asc | Desc deriving (Show, Ord, Eq) -- | A parameter to a prepared SQL statement. data Param where Param :: !(Lit a) -> Param instance Show Param where show (Param l) = "Param " <> show l instance Eq Param where Param a == Param b = compLit a b == EQ instance Ord Param where compare (Param a) (Param b) = compLit a b -- | Exception indicating the use of a default value. -- If any values throwing this during evaluation of @param xs@ will be -- replaced by their default value. data DefaultValueException = DefaultValueException deriving Show instance Exception DefaultValueException -- | An inductive tuple of Haskell-level values (i.e. @Int :*: Maybe Text@) -- which can be inserted into a table. class Insert a where params :: a -> [Either Param Param] instance (SqlType a, Insert b) => Insert (a :*: b) where params (a :*: b) = unsafePerformIO $ do res <- try $ return $! a return $ case res of Right a' -> Right (Param (mkLit a')) : params b Left DefaultValueException -> Left (Param (defaultValue :: Lit a)) : params b instance {-# OVERLAPPABLE #-} SqlType a => Insert a where params a = unsafePerformIO $ do res <- try $ return $! a return $ case res of Right a' -> [Right $ Param (mkLit a')] Left DefaultValueException -> [Left $ Param (defaultValue :: Lit a)]