{-# LANGUAGE UndecidableInstances #-}
module Database.Beam.Query.DataTypes where

import Database.Beam.Backend.SQL
import Database.Beam.Query.Internal

import Data.Text (Text)
import Data.Time (LocalTime, Day, TimeOfDay)
import Data.Scientific (Scientific)
import Data.Typeable (Typeable)
import Data.Vector (Vector)

-- | A data type in a given 'IsSql92DataTypeSyntax' which describes a SQL type
-- mapping to the Haskell type @a@
newtype DataType be a = DataType (BeamSqlBackendCastTargetSyntax be)

instance Sql92DisplaySyntax (BeamSqlBackendCastTargetSyntax be) => Show (DataType be a) where
  show :: DataType be a -> String
show (DataType BeamSqlBackendCastTargetSyntax be
syntax) = String
"DataType (" forall a. [a] -> [a] -> [a]
++ forall syntax. Sql92DisplaySyntax syntax => syntax -> String
displaySyntax BeamSqlBackendCastTargetSyntax be
syntax forall a. [a] -> [a] -> [a]
++ String
")"
deriving instance Eq (BeamSqlBackendCastTargetSyntax be) => Eq (DataType be a)

-- | Cast a value to a specific data type, specified using 'DataType'.
--
-- Note: this may fail at run-time if the cast is invalid for a particular value
cast_ :: BeamSqlBackend be => QGenExpr ctxt be s a -> DataType be b -> QGenExpr ctxt be s b
cast_ :: forall be ctxt s a b.
BeamSqlBackend be =>
QGenExpr ctxt be s a -> DataType be b -> QGenExpr ctxt be s b
cast_ (QExpr TablePrefix
-> Sql92SelectTableExpressionSyntax
     (Sql92SelectSelectTableSyntax
        (Sql92SelectSyntax (BeamSqlBackendSyntax be)))
e) (DataType BeamSqlBackendCastTargetSyntax be
dt) = forall context be s t.
(TablePrefix -> BeamSqlBackendExpressionSyntax be)
-> QGenExpr context be s t
QExpr (forall expr.
IsSql92ExpressionSyntax expr =>
expr -> Sql92ExpressionCastTargetSyntax expr -> expr
castE forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TablePrefix
-> Sql92SelectTableExpressionSyntax
     (Sql92SelectSelectTableSyntax
        (Sql92SelectSyntax (BeamSqlBackendSyntax be)))
e forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (f :: * -> *) a. Applicative f => a -> f a
pure BeamSqlBackendCastTargetSyntax be
dt)

-- ** Data types

-- | SQL92 @INTEGER@ data type
int :: (BeamSqlBackend be, Integral a) => DataType be a
int :: forall be a. (BeamSqlBackend be, Integral a) => DataType be a
int = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType forall dataType. IsSql92DataTypeSyntax dataType => dataType
intType

-- | SQL92 @SMALLINT@ data type
smallint :: (BeamSqlBackend be, Integral a) => DataType be a
smallint :: forall be a. (BeamSqlBackend be, Integral a) => DataType be a
smallint = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType forall dataType. IsSql92DataTypeSyntax dataType => dataType
smallIntType

-- | SQL2008 Optional @BIGINT@ data type
bigint :: ( BeamSqlBackend be, BeamSqlT071Backend be, Integral a )
       => DataType be a
bigint :: forall be a.
(BeamSqlBackend be, BeamSqlT071Backend be, Integral a) =>
DataType be a
bigint = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType forall dataType. IsSql2008BigIntDataTypeSyntax dataType => dataType
bigIntType

-- TODO is Integer the right type to use here?
-- | SQL2003 Optional @BINARY@ data type
binary :: ( BeamSqlBackend be, BeamSqlT021Backend be )
       => Maybe Word -> DataType be Integer
binary :: forall be.
(BeamSqlBackend be, BeamSqlT021Backend be) =>
Maybe Word -> DataType be Integer
binary Maybe Word
prec = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (forall dataType.
IsSql2003BinaryAndVarBinaryDataTypeSyntax dataType =>
Maybe Word -> dataType
binaryType Maybe Word
prec)

-- | SQL2003 Optional @VARBINARY@ data type
varbinary :: ( BeamSqlBackend be, BeamSqlT021Backend be )
          => Maybe Word -> DataType be Integer
varbinary :: forall be.
(BeamSqlBackend be, BeamSqlT021Backend be) =>
Maybe Word -> DataType be Integer
varbinary Maybe Word
prec = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (forall dataType.
IsSql2003BinaryAndVarBinaryDataTypeSyntax dataType =>
Maybe Word -> dataType
varBinaryType Maybe Word
prec)

-- TODO should this be Day or something?
-- | SQL92 @DATE@ data type
date :: BeamSqlBackend be => DataType be Day
date :: forall be. BeamSqlBackend be => DataType be Day
date = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType forall dataType. IsSql92DataTypeSyntax dataType => dataType
dateType

-- | SQL92 @CHAR@ data type
char :: BeamSqlBackend be => Maybe Word -> DataType be Text
char :: forall be.
BeamSqlBackend be =>
Maybe Word -> DataType be TablePrefix
char Maybe Word
prec = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (forall dataType.
IsSql92DataTypeSyntax dataType =>
Maybe Word -> Maybe TablePrefix -> dataType
charType Maybe Word
prec forall a. Maybe a
Nothing)

-- | SQL92 @VARCHAR@ data type
varchar :: BeamSqlBackend be => Maybe Word -> DataType be Text
varchar :: forall be.
BeamSqlBackend be =>
Maybe Word -> DataType be TablePrefix
varchar Maybe Word
prec = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (forall dataType.
IsSql92DataTypeSyntax dataType =>
Maybe Word -> Maybe TablePrefix -> dataType
varCharType Maybe Word
prec forall a. Maybe a
Nothing)

-- | SQL92 @NATIONAL CHARACTER VARYING@ data type
nationalVarchar :: BeamSqlBackend be => Maybe Word -> DataType be Text
nationalVarchar :: forall be.
BeamSqlBackend be =>
Maybe Word -> DataType be TablePrefix
nationalVarchar Maybe Word
prec = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (forall dataType.
IsSql92DataTypeSyntax dataType =>
Maybe Word -> dataType
nationalVarCharType Maybe Word
prec)

-- | SQL92 @NATIONAL CHARACTER@ data type
nationalChar :: BeamSqlBackend be => Maybe Word -> DataType be Text
nationalChar :: forall be.
BeamSqlBackend be =>
Maybe Word -> DataType be TablePrefix
nationalChar Maybe Word
prec = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (forall dataType.
IsSql92DataTypeSyntax dataType =>
Maybe Word -> dataType
nationalCharType Maybe Word
prec)

-- | SQL92 @DOUBLE@ data type
double :: BeamSqlBackend be => DataType be Double
double :: forall be. BeamSqlBackend be => DataType be Double
double = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType forall dataType. IsSql92DataTypeSyntax dataType => dataType
doubleType

-- | SQL92 @NUMERIC@ data type
numeric :: BeamSqlBackend be => Maybe (Word, Maybe Word) -> DataType be Scientific
numeric :: forall be.
BeamSqlBackend be =>
Maybe (Word, Maybe Word) -> DataType be Scientific
numeric Maybe (Word, Maybe Word)
x = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (forall dataType.
IsSql92DataTypeSyntax dataType =>
Maybe (Word, Maybe Word) -> dataType
numericType Maybe (Word, Maybe Word)
x)

-- | SQL92 @TIMESTAMP WITH TIME ZONE@ data type
timestamptz :: BeamSqlBackend be => DataType be LocalTime
timestamptz :: forall be. BeamSqlBackend be => DataType be LocalTime
timestamptz = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (forall dataType.
IsSql92DataTypeSyntax dataType =>
Maybe Word -> Bool -> dataType
timestampType forall a. Maybe a
Nothing Bool
True)

-- | SQL92 @TIMESTAMP WITHOUT TIME ZONE@ data type
timestamp :: BeamSqlBackend be => DataType be LocalTime
timestamp :: forall be. BeamSqlBackend be => DataType be LocalTime
timestamp = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (forall dataType.
IsSql92DataTypeSyntax dataType =>
Maybe Word -> Bool -> dataType
timestampType forall a. Maybe a
Nothing Bool
False)

-- | SQL92 @TIME@ data type
time :: BeamSqlBackend be => Maybe Word -> DataType be TimeOfDay
time :: forall be. BeamSqlBackend be => Maybe Word -> DataType be TimeOfDay
time Maybe Word
prec = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (forall dataType.
IsSql92DataTypeSyntax dataType =>
Maybe Word -> Bool -> dataType
timeType Maybe Word
prec Bool
False)

-- | SQL99 @BOOLEAN@ data type
boolean :: BeamSql99DataTypeBackend be => DataType be Bool
boolean :: forall be. BeamSql99DataTypeBackend be => DataType be Bool
boolean = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType forall dataType. IsSql99DataTypeSyntax dataType => dataType
booleanType

-- | SQL99 @CLOB@ data type
characterLargeObject :: BeamSql99DataTypeBackend be => DataType be Text
characterLargeObject :: forall be. BeamSql99DataTypeBackend be => DataType be TablePrefix
characterLargeObject = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType forall dataType. IsSql99DataTypeSyntax dataType => dataType
characterLargeObjectType

-- | SQL99 @BLOB@ data type
binaryLargeObject :: BeamSql99DataTypeBackend be => DataType be Text
binaryLargeObject :: forall be. BeamSql99DataTypeBackend be => DataType be TablePrefix
binaryLargeObject = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType forall dataType. IsSql99DataTypeSyntax dataType => dataType
binaryLargeObjectType

-- | SQL99 array data types
array :: (Typeable a, BeamSql99DataTypeBackend be)
      => DataType be a -> Int
      -> DataType be (Vector a)
array :: forall a be.
(Typeable a, BeamSql99DataTypeBackend be) =>
DataType be a -> Int -> DataType be (Vector a)
array (DataType BeamSqlBackendCastTargetSyntax be
ty) Int
sz = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (forall dataType.
IsSql99DataTypeSyntax dataType =>
dataType -> Int -> dataType
arrayType BeamSqlBackendCastTargetSyntax be
ty Int
sz)

-- | Haskell requires 'DataType's to match exactly. Use this function to convert
-- a 'DataType' that expects a concrete value to one expecting a 'Maybe'
maybeType :: DataType be a -> DataType be (Maybe a)
maybeType :: forall be a. DataType be a -> DataType be (Maybe a)
maybeType (DataType BeamSqlBackendCastTargetSyntax be
sqlTy) = forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType BeamSqlBackendCastTargetSyntax be
sqlTy