{-# 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 (" String -> ShowS
forall a. [a] -> [a] -> [a]
++ BeamSqlBackendCastTargetSyntax be -> String
forall syntax. Sql92DisplaySyntax syntax => syntax -> String
displaySyntax BeamSqlBackendCastTargetSyntax be
syntax String -> ShowS
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_ :: QGenExpr ctxt be s a -> DataType be b -> QGenExpr ctxt be s b
cast_ (QExpr TablePrefix -> BeamSqlBackendExpressionSyntax be
e) (DataType BeamSqlBackendCastTargetSyntax be
dt) = (TablePrefix -> BeamSqlBackendExpressionSyntax be)
-> QGenExpr ctxt be s b
forall context be s t.
(TablePrefix -> BeamSqlBackendExpressionSyntax be)
-> QGenExpr context be s t
QExpr (Sql92UpdateExpressionSyntax
  (Sql92UpdateSyntax (BeamSqlBackendSyntax be))
-> Sql92ExpressionCastTargetSyntax
     (Sql92UpdateExpressionSyntax
        (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
-> Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be))
forall expr.
IsSql92ExpressionSyntax expr =>
expr -> Sql92ExpressionCastTargetSyntax expr -> expr
castE (Sql92UpdateExpressionSyntax
   (Sql92UpdateSyntax (BeamSqlBackendSyntax be))
 -> Sql92ExpressionCastTargetSyntax
      (Sql92UpdateExpressionSyntax
         (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
 -> Sql92UpdateExpressionSyntax
      (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
-> (TablePrefix
    -> Sql92UpdateExpressionSyntax
         (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
-> TablePrefix
-> Sql92ExpressionCastTargetSyntax
     (Sql92UpdateExpressionSyntax
        (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
-> Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TablePrefix
-> Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be))
TablePrefix -> BeamSqlBackendExpressionSyntax be
e (TablePrefix
 -> Sql92ExpressionCastTargetSyntax
      (Sql92UpdateExpressionSyntax
         (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
 -> Sql92UpdateExpressionSyntax
      (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
-> (TablePrefix
    -> Sql92ExpressionCastTargetSyntax
         (Sql92UpdateExpressionSyntax
            (Sql92UpdateSyntax (BeamSqlBackendSyntax be))))
-> TablePrefix
-> Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be))
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Sql92ExpressionCastTargetSyntax
  (Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
-> TablePrefix
-> Sql92ExpressionCastTargetSyntax
     (Sql92UpdateExpressionSyntax
        (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
forall (f :: * -> *) a. Applicative f => a -> f a
pure Sql92ExpressionCastTargetSyntax
  (Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
BeamSqlBackendCastTargetSyntax be
dt)

-- ** Data types

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

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

-- | SQL2008 Optional @BIGINT@ data type
bigint :: ( BeamSqlBackend be, BeamSqlT071Backend be, Integral a )
       => DataType be a
bigint :: DataType be a
bigint = BeamSqlBackendCastTargetSyntax be -> DataType be a
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType BeamSqlBackendCastTargetSyntax be
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 :: Maybe Word -> DataType be Integer
binary Maybe Word
prec = BeamSqlBackendCastTargetSyntax be -> DataType be Integer
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (Maybe Word
-> Sql92ExpressionCastTargetSyntax
     (Sql92UpdateExpressionSyntax
        (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
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 :: Maybe Word -> DataType be Integer
varbinary Maybe Word
prec = BeamSqlBackendCastTargetSyntax be -> DataType be Integer
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (Maybe Word
-> Sql92ExpressionCastTargetSyntax
     (Sql92UpdateExpressionSyntax
        (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
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 :: DataType be Day
date = BeamSqlBackendCastTargetSyntax be -> DataType be Day
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType BeamSqlBackendCastTargetSyntax be
forall dataType. IsSql92DataTypeSyntax dataType => dataType
dateType

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

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

-- | SQL92 @NATIONAL CHARACTER VARYING@ data type
nationalVarchar :: BeamSqlBackend be => Maybe Word -> DataType be Text
nationalVarchar :: Maybe Word -> DataType be TablePrefix
nationalVarchar Maybe Word
prec = BeamSqlBackendCastTargetSyntax be -> DataType be TablePrefix
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (Maybe Word
-> Sql92ExpressionCastTargetSyntax
     (Sql92UpdateExpressionSyntax
        (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
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 :: Maybe Word -> DataType be TablePrefix
nationalChar Maybe Word
prec = BeamSqlBackendCastTargetSyntax be -> DataType be TablePrefix
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (Maybe Word
-> Sql92ExpressionCastTargetSyntax
     (Sql92UpdateExpressionSyntax
        (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
forall dataType.
IsSql92DataTypeSyntax dataType =>
Maybe Word -> dataType
nationalCharType Maybe Word
prec)

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

-- | SQL92 @NUMERIC@ data type
numeric :: BeamSqlBackend be => Maybe (Word, Maybe Word) -> DataType be Scientific
numeric :: Maybe (Word, Maybe Word) -> DataType be Scientific
numeric Maybe (Word, Maybe Word)
x = BeamSqlBackendCastTargetSyntax be -> DataType be Scientific
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (Maybe (Word, Maybe Word)
-> Sql92ExpressionCastTargetSyntax
     (Sql92UpdateExpressionSyntax
        (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
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 :: DataType be LocalTime
timestamptz = BeamSqlBackendCastTargetSyntax be -> DataType be LocalTime
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (Maybe Word
-> Bool
-> Sql92ExpressionCastTargetSyntax
     (Sql92UpdateExpressionSyntax
        (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
forall dataType.
IsSql92DataTypeSyntax dataType =>
Maybe Word -> Bool -> dataType
timestampType Maybe Word
forall a. Maybe a
Nothing Bool
True)

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

-- | SQL92 @TIME@ data type
time :: BeamSqlBackend be => Maybe Word -> DataType be TimeOfDay
time :: Maybe Word -> DataType be TimeOfDay
time Maybe Word
prec = BeamSqlBackendCastTargetSyntax be -> DataType be TimeOfDay
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (Maybe Word
-> Bool
-> Sql92ExpressionCastTargetSyntax
     (Sql92UpdateExpressionSyntax
        (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
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 :: DataType be Bool
boolean = BeamSqlBackendCastTargetSyntax be -> DataType be Bool
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType BeamSqlBackendCastTargetSyntax be
forall dataType. IsSql99DataTypeSyntax dataType => dataType
booleanType

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

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

-- | SQL99 array data types
array :: (Typeable a, BeamSql99DataTypeBackend be)
      => DataType be a -> Int
      -> DataType be (Vector a)
array :: DataType be a -> Int -> DataType be (Vector a)
array (DataType BeamSqlBackendCastTargetSyntax be
ty) Int
sz = BeamSqlBackendCastTargetSyntax be -> DataType be (Vector a)
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType (BeamSqlBackendCastTargetSyntax be
-> Int -> BeamSqlBackendCastTargetSyntax be
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 :: DataType be a -> DataType be (Maybe a)
maybeType (DataType BeamSqlBackendCastTargetSyntax be
sqlTy) = BeamSqlBackendCastTargetSyntax be -> DataType be (Maybe a)
forall be a. BeamSqlBackendCastTargetSyntax be -> DataType be a
DataType BeamSqlBackendCastTargetSyntax be
sqlTy