{- |
Copyright : Flipstone Technology Partners 2023
License   : MIT
Stability : Stable

This module provides functions and types for describing a single-column data
type that exists in PostgreSQL so that Orville can determine how to serialize
Haskell values to and from the SQL type. If you need to use a SQL type that
Orville does not provide support for here, you can construct your own 'SqlType'
value and use 'Orville.PostgreSQL.Marshall.fieldOfType' to build the required
'Orville.PostgreSQL.Marshall.FieldDefinition'.

@since 1.0.0.0
-}
module Orville.PostgreSQL.Marshall.SqlType
  ( SqlType
      ( SqlType
      , sqlTypeExpr
      , sqlTypeReferenceExpr
      , sqlTypeOid
      , sqlTypeMaximumLength
      , sqlTypeToSql
      , sqlTypeFromSql
      , sqlTypeDontDropImplicitDefaultDuringMigrate
      )
  -- numeric types
  , integer
  , serial
  , bigInteger
  , bigSerial
  , smallInteger
  , double
  -- textual-ish types
  , boolean
  , unboundedText
  , fixedText
  , boundedText
  , textSearchVector
  , uuid
  -- date types
  , date
  , timestamp
  , timestampWithoutZone
  -- json types
  , jsonb
  -- postgresql types
  , oid
  -- type conversions
  , foreignRefType
  , convertSqlType
  , tryConvertSqlType
  )
where

import Data.Int (Int16, Int32, Int64)
import Data.Text (Text)
import qualified Data.Time as Time
import qualified Data.UUID as UUID
import qualified Database.PostgreSQL.LibPQ as LibPQ
import qualified Foreign.C.Types as CTypes

import qualified Orville.PostgreSQL.Expr as Expr
import Orville.PostgreSQL.Raw.SqlValue (SqlValue)
import qualified Orville.PostgreSQL.Raw.SqlValue as SqlValue

{- |
  SqlType defines the mapping of a Haskell type (@a@) to a SQL column type in the
  database. This includes both how to convert the type to and from the raw values
  read from the database as well as the schema information required to create
  and migrate columns using the type.

@since 1.0.0.0
-}
data SqlType a = SqlType
  { forall a. SqlType a -> DataType
sqlTypeExpr :: Expr.DataType
  -- ^ The SQL data type expression to use when creating/migrating columns of
  -- this type.
  , forall a. SqlType a -> Maybe DataType
sqlTypeReferenceExpr :: Maybe Expr.DataType
  -- ^ The SQL data type expression to use when creating/migrating columns
  -- with foreign keys to this type. This is used by 'foreignRefType' to build a
  -- new SqlType when making foreign key fields.
  , forall a. SqlType a -> Oid
sqlTypeOid :: LibPQ.Oid
  -- ^ The Oid for the type in PostgreSQL. This will be used during
  -- migrations to determine whether the column type needs to be altered.
  , forall a. SqlType a -> Maybe Int32
sqlTypeMaximumLength :: Maybe Int32
  -- ^ The maximum length for types that take a type parameter (such as
  -- @char@ and @varchar@). This will be used during migration to determine
  -- whether the column type needs to be altered.
  , forall a. SqlType a -> a -> SqlValue
sqlTypeToSql :: a -> SqlValue
  -- ^ A function for converting Haskell values of this type into values to
  -- be stored in the database.
  , forall a. SqlType a -> SqlValue -> Either String a
sqlTypeFromSql :: SqlValue -> Either String a
  -- ^ A function for converting values of this type stored in the database
  -- into Haskell values. This function should return 'Left' to indicate
  -- an error if the conversion is impossible. Otherwise it should return
  -- a 'Right' of the corresponding @a@ value.
  , forall a. SqlType a -> Bool
sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
  -- ^ The SERIAL and BIGSERIAL PostgreSQL types are really pseudo-types that
  -- create an implicit default value. This flag tells Orville's auto-migration
  -- logic to ignore the default value rather than drop it as it normally would.
  }

{- |
  'integer' defines a 32-bit integer type. This corresponds to the "INTEGER" type in SQL.

@since 1.0.0.0
-}
integer :: SqlType Int32
integer :: SqlType Int32
integer =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.int
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
23
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Int32 -> SqlValue
sqlTypeToSql = Int32 -> SqlValue
SqlValue.fromInt32
    , sqlTypeFromSql :: SqlValue -> Either String Int32
sqlTypeFromSql = SqlValue -> Either String Int32
SqlValue.toInt32
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'serial' defines a 32-bit auto-incrementing column type. This corresponds to
  the "SERIAL" type in PostgreSQL.

@since 1.0.0.0
-}
serial :: SqlType Int32
serial :: SqlType Int32
serial =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.serial
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = DataType -> Maybe DataType
forall a. a -> Maybe a
Just DataType
Expr.int
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
23
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Int32 -> SqlValue
sqlTypeToSql = Int32 -> SqlValue
SqlValue.fromInt32
    , sqlTypeFromSql :: SqlValue -> Either String Int32
sqlTypeFromSql = SqlValue -> Either String Int32
SqlValue.toInt32
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
True
    }

{- |
  'bigInteger' defines a 64-bit integer type. This corresponds to the "BIGINT"
  type in SQL.

@since 1.0.0.0
-}
bigInteger :: SqlType Int64
bigInteger :: SqlType Int64
bigInteger =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.bigInt
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
20
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Int64 -> SqlValue
sqlTypeToSql = Int64 -> SqlValue
SqlValue.fromInt64
    , sqlTypeFromSql :: SqlValue -> Either String Int64
sqlTypeFromSql = SqlValue -> Either String Int64
SqlValue.toInt64
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'bigSerial' defines a 64-bit auto-incrementing column type. This corresponds to
  the "BIGSERIAL" type in PostgresSQL.

@since 1.0.0.0
-}
bigSerial :: SqlType Int64
bigSerial :: SqlType Int64
bigSerial =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.bigSerial
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = DataType -> Maybe DataType
forall a. a -> Maybe a
Just DataType
Expr.bigInt
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
20
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Int64 -> SqlValue
sqlTypeToSql = Int64 -> SqlValue
SqlValue.fromInt64
    , sqlTypeFromSql :: SqlValue -> Either String Int64
sqlTypeFromSql = SqlValue -> Either String Int64
SqlValue.toInt64
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
True
    }

{- |
  'smallInteger' defines a 16-bit integer type. This corresponds to the "SMALLINT" type in SQL.

@since 1.0.0.0
-}
smallInteger :: SqlType Int16
smallInteger :: SqlType Int16
smallInteger =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.smallint
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
21
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Int16 -> SqlValue
sqlTypeToSql = Int16 -> SqlValue
SqlValue.fromInt16
    , sqlTypeFromSql :: SqlValue -> Either String Int16
sqlTypeFromSql = SqlValue -> Either String Int16
SqlValue.toInt16
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'double' defines a floating point numeric type. This corresponds to the
  "DOUBLE PRECISION" type in SQL.

@since 1.0.0.0
-}
double :: SqlType Double
double :: SqlType Double
double =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.doublePrecision
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
701
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Double -> SqlValue
sqlTypeToSql = Double -> SqlValue
SqlValue.fromDouble
    , sqlTypeFromSql :: SqlValue -> Either String Double
sqlTypeFromSql = SqlValue -> Either String Double
SqlValue.toDouble
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'boolean' defines a True/False boolean type. This corresponds to the "BOOLEAN"
  type in SQL.

@since 1.0.0.0
-}
boolean :: SqlType Bool
boolean :: SqlType Bool
boolean =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.boolean
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
16
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Bool -> SqlValue
sqlTypeToSql = Bool -> SqlValue
SqlValue.fromBool
    , sqlTypeFromSql :: SqlValue -> Either String Bool
sqlTypeFromSql = SqlValue -> Either String Bool
SqlValue.toBool
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'unboundedText' defines an unbounded length text field type. This corresponds to a
  "TEXT" type in PostgreSQL.

@since 1.0.0.0
-}
unboundedText :: SqlType Text
unboundedText :: SqlType Text
unboundedText =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.text
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
25
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Text -> SqlValue
sqlTypeToSql = Text -> SqlValue
SqlValue.fromText
    , sqlTypeFromSql :: SqlValue -> Either String Text
sqlTypeFromSql = SqlValue -> Either String Text
SqlValue.toText
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'fixedText' defines a fixed length text field type. This corresponds to a
  "CHAR(len)" type in PostgreSQL.

@since 1.0.0.0
-}
fixedText :: Int32 -> SqlType Text
fixedText :: Int32 -> SqlType Text
fixedText Int32
len =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = Int32 -> DataType
Expr.char Int32
len
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
1042
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Int32 -> Maybe Int32
forall a. a -> Maybe a
Just Int32
len
    , sqlTypeToSql :: Text -> SqlValue
sqlTypeToSql = Text -> SqlValue
SqlValue.fromText
    , sqlTypeFromSql :: SqlValue -> Either String Text
sqlTypeFromSql = SqlValue -> Either String Text
SqlValue.toText
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'boundedText' defines a variable length text field type. This corresponds to a
  "VARCHAR(len)" type in PostgreSQL.

@since 1.0.0.0
-}
boundedText :: Int32 -> SqlType Text
boundedText :: Int32 -> SqlType Text
boundedText Int32
len =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = Int32 -> DataType
Expr.varchar Int32
len
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
1043
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Int32 -> Maybe Int32
forall a. a -> Maybe a
Just Int32
len
    , sqlTypeToSql :: Text -> SqlValue
sqlTypeToSql = Text -> SqlValue
SqlValue.fromText
    , sqlTypeFromSql :: SqlValue -> Either String Text
sqlTypeFromSql = SqlValue -> Either String Text
SqlValue.toText
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'textSearchVector' defines a type for indexed text searching. It corresponds to the
  "TSVECTOR" type in PostgreSQL.

@since 1.0.0.0
-}
textSearchVector :: SqlType Text
textSearchVector :: SqlType Text
textSearchVector =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.tsvector
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
3614
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Text -> SqlValue
sqlTypeToSql = Text -> SqlValue
SqlValue.fromText
    , sqlTypeFromSql :: SqlValue -> Either String Text
sqlTypeFromSql = SqlValue -> Either String Text
SqlValue.toText
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'uuid' defines a UUID type. It corresponds to the "UUID" type in PostgreSQL.

@since 1.0.0.0
-}
uuid :: SqlType UUID.UUID
uuid :: SqlType UUID
uuid =
  let
    uuidFromText :: Text -> Either String UUID
uuidFromText Text
t =
      case Text -> Maybe UUID
UUID.fromText Text
t of
        Maybe UUID
Nothing -> String -> Either String UUID
forall a b. a -> Either a b
Left String
"Invalid UUID value"
        Just UUID
validUuid -> UUID -> Either String UUID
forall a b. b -> Either a b
Right UUID
validUuid
  in
    SqlType
      { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.uuid
      , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
      , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
2950
      , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
      , sqlTypeToSql :: UUID -> SqlValue
sqlTypeToSql = Text -> SqlValue
SqlValue.fromText (Text -> SqlValue) -> (UUID -> Text) -> UUID -> SqlValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UUID -> Text
UUID.toText
      , sqlTypeFromSql :: SqlValue -> Either String UUID
sqlTypeFromSql = \SqlValue
a -> Text -> Either String UUID
uuidFromText (Text -> Either String UUID)
-> Either String Text -> Either String UUID
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< SqlValue -> Either String Text
SqlValue.toText SqlValue
a
      , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
      }

{- |
  'date' defines a type representing a calendar date (without time zone). It corresponds
  to the "DATE" type in SQL.

@since 1.0.0.0
-}
date :: SqlType Time.Day
date :: SqlType Day
date =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.date
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
1082
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Day -> SqlValue
sqlTypeToSql = Day -> SqlValue
SqlValue.fromDay
    , sqlTypeFromSql :: SqlValue -> Either String Day
sqlTypeFromSql = SqlValue -> Either String Day
SqlValue.toDay
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'timestamp' defines a type representing a particular point in time without time zone information,
  but can be constructed with a time zone offset.
  It corresponds to the "TIMESTAMP with time zone" type in SQL.

  Note: This is NOT a typo. The "TIMESTAMP with time zone" type in SQL does not include
  any actual time zone information. For an excellent explanation of the complexities
  involving this type, please see Chris Clark's blog post about it:
  http://blog.untrod.com/2016/08/actually-understanding-timezones-in-postgresql.html

@since 1.0.0.0
-}
timestamp :: SqlType Time.UTCTime
timestamp :: SqlType UTCTime
timestamp =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.timestampWithZone
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
1184
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: UTCTime -> SqlValue
sqlTypeToSql = UTCTime -> SqlValue
SqlValue.fromUTCTime
    , sqlTypeFromSql :: SqlValue -> Either String UTCTime
sqlTypeFromSql = SqlValue -> Either String UTCTime
SqlValue.toUTCTime
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'timestampWithoutZone' defines a type representing a particular point in time (without time zone).
  It corresponds to the "TIMESTAMP without time zone" type in SQL.

  http://blog.untrod.com/2016/08/actually-understanding-timezones-in-postgresql.html

@since 1.0.0.0
-}
timestampWithoutZone :: SqlType Time.LocalTime
timestampWithoutZone :: SqlType LocalTime
timestampWithoutZone =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.timestampWithoutZone
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
1114
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: LocalTime -> SqlValue
sqlTypeToSql = LocalTime -> SqlValue
SqlValue.fromLocalTime
    , sqlTypeFromSql :: SqlValue -> Either String LocalTime
sqlTypeFromSql = SqlValue -> Either String LocalTime
SqlValue.toLocalTime
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
   'jsonb' represents any type that can be converted To and From JSON. This corresponds
   to the "JSONB" type in PostgreSQL.

@since 1.0.0.0
-}
jsonb :: SqlType Text
jsonb :: SqlType Text
jsonb =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.jsonb
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
3802
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Text -> SqlValue
sqlTypeToSql = Text -> SqlValue
SqlValue.fromText
    , sqlTypeFromSql :: SqlValue -> Either String Text
sqlTypeFromSql = SqlValue -> Either String Text
SqlValue.toText
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'oid' corresponds to the type used in PostgreSQL for identifying system
  objects.

@since 1.0.0.0
-}
oid :: SqlType LibPQ.Oid
oid :: SqlType Oid
oid =
  SqlType
    { sqlTypeExpr :: DataType
sqlTypeExpr = DataType
Expr.oid
    , sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing
    , sqlTypeOid :: Oid
sqlTypeOid = CUInt -> Oid
LibPQ.Oid CUInt
26
    , sqlTypeMaximumLength :: Maybe Int32
sqlTypeMaximumLength = Maybe Int32
forall a. Maybe a
Nothing
    , sqlTypeToSql :: Oid -> SqlValue
sqlTypeToSql = \(LibPQ.Oid (CTypes.CUInt Word32
word)) -> Word32 -> SqlValue
SqlValue.fromWord32 Word32
word
    , sqlTypeFromSql :: SqlValue -> Either String Oid
sqlTypeFromSql = (Word32 -> Oid) -> Either String Word32 -> Either String Oid
forall a b. (a -> b) -> Either String a -> Either String b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (CUInt -> Oid
LibPQ.Oid (CUInt -> Oid) -> (Word32 -> CUInt) -> Word32 -> Oid
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word32 -> CUInt
CTypes.CUInt) (Either String Word32 -> Either String Oid)
-> (SqlValue -> Either String Word32)
-> SqlValue
-> Either String Oid
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SqlValue -> Either String Word32
SqlValue.toWord32
    , sqlTypeDontDropImplicitDefaultDuringMigrate :: Bool
sqlTypeDontDropImplicitDefaultDuringMigrate = Bool
False
    }

{- |
  'foreignRefType' creates a 'SqlType' suitable for columns that will be
  foreign keys referencing a column of the given 'SqlType'. For most types, the
  underlying SQL type will be identical, but for special types (such as
  auto-incrementing primary keys), the type constructed by 'foreignRefType' will
  have a regular underlying SQL type. Each 'SqlType' definition must specify any
  special handling required when creating foreign reference types by setting
  the 'sqlTypeReferenceExpr' field to an appropriate value.

@since 1.0.0.0
-}
foreignRefType :: SqlType a -> SqlType a
foreignRefType :: forall a. SqlType a -> SqlType a
foreignRefType SqlType a
sqlType =
  case SqlType a -> Maybe DataType
forall a. SqlType a -> Maybe DataType
sqlTypeReferenceExpr SqlType a
sqlType of
    Maybe DataType
Nothing -> SqlType a
sqlType
    Just DataType
refExpr -> SqlType a
sqlType {sqlTypeExpr :: DataType
sqlTypeExpr = DataType
refExpr, sqlTypeReferenceExpr :: Maybe DataType
sqlTypeReferenceExpr = Maybe DataType
forall a. Maybe a
Nothing}

{- |
  'tryConvertSqlType' changes the Haskell type used by a 'SqlType' which
  changes the column type that will be used in the database schema. The
  functions given will be used to convert the now Haskell type to and from the
  original type when reading and writing values from the database. When reading
  an @a@ value from the database, the conversion function should produce 'Left'
  with an error message if the value cannot be successfully converted to a @b@.

@since 1.0.0.0
-}
tryConvertSqlType :: (b -> a) -> (a -> Either String b) -> SqlType a -> SqlType b
tryConvertSqlType :: forall b a.
(b -> a) -> (a -> Either String b) -> SqlType a -> SqlType b
tryConvertSqlType b -> a
bToA a -> Either String b
aToB SqlType a
sqlType =
  SqlType a
sqlType
    { sqlTypeToSql :: b -> SqlValue
sqlTypeToSql = SqlType a -> a -> SqlValue
forall a. SqlType a -> a -> SqlValue
sqlTypeToSql SqlType a
sqlType (a -> SqlValue) -> (b -> a) -> b -> SqlValue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. b -> a
bToA
    , sqlTypeFromSql :: SqlValue -> Either String b
sqlTypeFromSql = \SqlValue
sql -> do
        a
a <- SqlType a -> SqlValue -> Either String a
forall a. SqlType a -> SqlValue -> Either String a
sqlTypeFromSql SqlType a
sqlType SqlValue
sql
        a -> Either String b
aToB a
a
    }

{- |
  'convertSqlType' changes the Haskell type used by a 'SqlType' in the same manner
  as 'tryConvertSqlType' in cases where an @a@ can always be converted to a @b@.

@since 1.0.0.0
-}
convertSqlType :: (b -> a) -> (a -> b) -> SqlType a -> SqlType b
convertSqlType :: forall b a. (b -> a) -> (a -> b) -> SqlType a -> SqlType b
convertSqlType b -> a
bToA a -> b
aToB =
  (b -> a) -> (a -> Either String b) -> SqlType a -> SqlType b
forall b a.
(b -> a) -> (a -> Either String b) -> SqlType a -> SqlType b
tryConvertSqlType b -> a
bToA (b -> Either String b
forall a b. b -> Either a b
Right (b -> Either String b) -> (a -> b) -> a -> Either String b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> b
aToB)