{-# LANGUAGE TemplateHaskell #-}

module Hasura.Backends.MySQL.Meta
  ( getMetadata,
  )
where

import Control.Exception (throw)
import Data.ByteString.Char8 qualified as B8
import Data.FileEmbed (embedFile, makeRelativeToProject)
import Data.HashMap.Strict qualified as HM
import Data.HashMap.Strict.NonEmpty qualified as NEHashMap
import Data.HashSet qualified as HS
import Data.Sequence.NonEmpty qualified as NESeq
import Data.String (fromString)
import Database.MySQL.Base (Connection)
import Database.MySQL.Base.Types (Field (..))
import Database.MySQL.Simple (Only (Only), query)
import Database.MySQL.Simple.QueryResults (QueryResults (..), convertError)
import Database.MySQL.Simple.Result (Result, ResultError (..), convert)
import Hasura.Backends.MySQL.Instances.Types ()
import Hasura.Backends.MySQL.Types
import Hasura.Prelude
import Hasura.RQL.Types.Column
import Hasura.RQL.Types.Common
import Hasura.RQL.Types.Table
import Hasura.SQL.Backend
import Language.GraphQL.Draft.Syntax qualified as G

getMetadata :: ConnSourceConfig -> Connection -> IO (DBTablesMetadata 'MySQL)
getMetadata :: ConnSourceConfig -> Connection -> IO (DBTablesMetadata 'MySQL)
getMetadata ConnSourceConfig {Text
_cscDatabase :: ConnSourceConfig -> Text
_cscDatabase :: Text
_cscDatabase} Connection
scConnection = do
  let sql :: ByteString
sql = $(makeRelativeToProject "src-rsr/mysql_table_metadata.sql" >>= embedFile)
  [InformationSchema]
results :: [InformationSchema] <- Connection -> Query -> Only Text -> IO [InformationSchema]
forall q r.
(QueryParams q, QueryResults r) =>
Connection -> Query -> q -> IO [r]
query Connection
scConnection (String -> Query
forall a. IsString a => String -> a
fromString (String -> Query) -> (ByteString -> String) -> ByteString -> Query
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> String
B8.unpack (ByteString -> Query) -> ByteString -> Query
forall a b. (a -> b) -> a -> b
$ ByteString
sql) (Text -> Only Text
forall a. a -> Only a
Only Text
_cscDatabase)
  HashMap TableName (DBTableMetadata 'MySQL)
-> IO (HashMap TableName (DBTableMetadata 'MySQL))
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([InformationSchema] -> DBTablesMetadata 'MySQL
mkMetadata [InformationSchema]
results)

mkMetadata :: [InformationSchema] -> DBTablesMetadata 'MySQL
mkMetadata :: [InformationSchema] -> DBTablesMetadata 'MySQL
mkMetadata = (InformationSchema
 -> HashMap TableName (DBTableMetadata 'MySQL)
 -> HashMap TableName (DBTableMetadata 'MySQL))
-> HashMap TableName (DBTableMetadata 'MySQL)
-> [InformationSchema]
-> HashMap TableName (DBTableMetadata 'MySQL)
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr InformationSchema
-> DBTablesMetadata 'MySQL -> DBTablesMetadata 'MySQL
InformationSchema
-> HashMap TableName (DBTableMetadata 'MySQL)
-> HashMap TableName (DBTableMetadata 'MySQL)
mergeMetadata HashMap TableName (DBTableMetadata 'MySQL)
forall k v. HashMap k v
HM.empty

mergeMetadata :: InformationSchema -> DBTablesMetadata 'MySQL -> DBTablesMetadata 'MySQL
mergeMetadata :: InformationSchema
-> DBTablesMetadata 'MySQL -> DBTablesMetadata 'MySQL
mergeMetadata InformationSchema {Maybe Word
Maybe Text
Word
Text
InformationSchemaColumnKey
isReferencedColumnName :: InformationSchema -> Maybe Text
isReferencedTableName :: InformationSchema -> Maybe Text
isReferencedTableSchema :: InformationSchema -> Maybe Text
isPositionInUniqueConstraint :: InformationSchema -> Maybe Word
isConstraintOrdinalPosition :: InformationSchema -> Maybe Word
isConstraintName :: InformationSchema -> Maybe Text
isColumnComment :: InformationSchema -> Text
isColumnKey :: InformationSchema -> InformationSchemaColumnKey
isColumnType :: InformationSchema -> Text
isDataType :: InformationSchema -> Maybe Text
isIsNullable :: InformationSchema -> Text
isColumnDefault :: InformationSchema -> Maybe Text
isOrdinalPosition :: InformationSchema -> Word
isColumnName :: InformationSchema -> Text
isTableName :: InformationSchema -> Text
isTableSchema :: InformationSchema -> Text
isReferencedColumnName :: Maybe Text
isReferencedTableName :: Maybe Text
isReferencedTableSchema :: Maybe Text
isPositionInUniqueConstraint :: Maybe Word
isConstraintOrdinalPosition :: Maybe Word
isConstraintName :: Maybe Text
isColumnComment :: Text
isColumnKey :: InformationSchemaColumnKey
isColumnType :: Text
isDataType :: Maybe Text
isIsNullable :: Text
isColumnDefault :: Maybe Text
isOrdinalPosition :: Word
isColumnName :: Text
isTableName :: Text
isTableSchema :: Text
..} =
  (DBTableMetadata 'MySQL
 -> DBTableMetadata 'MySQL -> DBTableMetadata 'MySQL)
-> TableName
-> DBTableMetadata 'MySQL
-> HashMap TableName (DBTableMetadata 'MySQL)
-> HashMap TableName (DBTableMetadata 'MySQL)
forall k v.
(Eq k, Hashable k) =>
(v -> v -> v) -> k -> v -> HashMap k v -> HashMap k v
HM.insertWith
    DBTableMetadata 'MySQL
-> DBTableMetadata 'MySQL -> DBTableMetadata 'MySQL
mergeDBTableMetadata
    (TableName :: Text -> Maybe Text -> TableName
TableName {name :: Text
name = Text
isTableName, schema :: Maybe Text
schema = Text -> Maybe Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
isTableSchema})
    (DBTableMetadata 'MySQL
 -> HashMap TableName (DBTableMetadata 'MySQL)
 -> HashMap TableName (DBTableMetadata 'MySQL))
-> DBTableMetadata 'MySQL
-> HashMap TableName (DBTableMetadata 'MySQL)
-> HashMap TableName (DBTableMetadata 'MySQL)
forall a b. (a -> b) -> a -> b
$ DBTableMetadata :: forall (b :: BackendType).
OID
-> [RawColumnInfo b]
-> Maybe (PrimaryKey b (Column b))
-> HashSet (UniqueConstraint b)
-> HashSet (ForeignKeyMetadata b)
-> Maybe ViewInfo
-> Maybe PGDescription
-> ExtraTableMetadata b
-> DBTableMetadata b
DBTableMetadata
      { _ptmiOid :: OID
_ptmiOid = Int -> OID
OID Int
0,
        _ptmiColumns :: [RawColumnInfo 'MySQL]
_ptmiColumns =
          [ RawColumnInfo :: forall (b :: BackendType).
Column b
-> Int
-> ScalarType b
-> Bool
-> Maybe Description
-> ColumnMutability
-> RawColumnInfo b
RawColumnInfo
              { rciName :: Column 'MySQL
rciName = Text -> Column
Column Text
isColumnName,
                rciPosition :: Int
rciPosition = Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
isOrdinalPosition,
                rciType :: ScalarType 'MySQL
rciType = Text -> ScalarType
parseMySQLScalarType Text
isColumnType, -- TODO: This needs to become more precise by considering Field length and character-set
                rciIsNullable :: Bool
rciIsNullable = Text
isIsNullable Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"YES", -- ref: https://dev.mysql.com/doc/refman/8.0/en/information-schema-columns-table.html
                rciDescription :: Maybe Description
rciDescription = Description -> Maybe Description
forall a. a -> Maybe a
Just (Description -> Maybe Description)
-> Description -> Maybe Description
forall a b. (a -> b) -> a -> b
$ Text -> Description
G.Description Text
isColumnComment,
                rciMutability :: ColumnMutability
rciMutability = ColumnMutability :: Bool -> Bool -> ColumnMutability
ColumnMutability {_cmIsInsertable :: Bool
_cmIsInsertable = Bool
True, _cmIsUpdatable :: Bool
_cmIsUpdatable = Bool
True}
              }
          ],
        _ptmiPrimaryKey :: Maybe (PrimaryKey 'MySQL (Column 'MySQL))
_ptmiPrimaryKey =
          if InformationSchemaColumnKey
isColumnKey InformationSchemaColumnKey -> InformationSchemaColumnKey -> Bool
forall a. Eq a => a -> a -> Bool
== InformationSchemaColumnKey
PRI
            then
              PrimaryKey 'MySQL Column -> Maybe (PrimaryKey 'MySQL Column)
forall a. a -> Maybe a
Just (PrimaryKey 'MySQL Column -> Maybe (PrimaryKey 'MySQL Column))
-> PrimaryKey 'MySQL Column -> Maybe (PrimaryKey 'MySQL Column)
forall a b. (a -> b) -> a -> b
$
                Constraint 'MySQL -> NESeq Column -> PrimaryKey 'MySQL Column
forall (b :: BackendType) a.
Constraint b -> NESeq a -> PrimaryKey b a
PrimaryKey
                  ( ConstraintName 'MySQL -> OID -> Constraint 'MySQL
forall (b :: BackendType). ConstraintName b -> OID -> Constraint b
Constraint
                      (Text -> ConstraintName
ConstraintName (Text -> ConstraintName) -> Text -> ConstraintName
forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
"" Maybe Text
isConstraintName)
                      (Int -> OID
OID (Int -> OID) -> Int -> OID
forall a b. (a -> b) -> a -> b
$ Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word -> Int) -> Word -> Int
forall a b. (a -> b) -> a -> b
$ Word -> Maybe Word -> Word
forall a. a -> Maybe a -> a
fromMaybe Word
0 Maybe Word
isConstraintOrdinalPosition)
                  )
                  (Column -> NESeq Column
forall a. a -> NESeq a
NESeq.singleton (Text -> Column
Column Text
isColumnName))
            else Maybe (PrimaryKey 'MySQL (Column 'MySQL))
forall a. Maybe a
Nothing,
        _ptmiUniqueConstraints :: HashSet (UniqueConstraint 'MySQL)
_ptmiUniqueConstraints =
          if InformationSchemaColumnKey
isColumnKey InformationSchemaColumnKey -> InformationSchemaColumnKey -> Bool
forall a. Eq a => a -> a -> Bool
== InformationSchemaColumnKey
UNI
            then
              UniqueConstraint 'MySQL -> HashSet (UniqueConstraint 'MySQL)
forall a. Hashable a => a -> HashSet a
HS.singleton
                ( UniqueConstraint :: forall (b :: BackendType).
Constraint b -> HashSet (Column b) -> UniqueConstraint b
UniqueConstraint
                    { _ucConstraint :: Constraint 'MySQL
_ucConstraint =
                        ConstraintName 'MySQL -> OID -> Constraint 'MySQL
forall (b :: BackendType). ConstraintName b -> OID -> Constraint b
Constraint
                          (Text -> ConstraintName
ConstraintName (Text -> ConstraintName) -> Text -> ConstraintName
forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
"" Maybe Text
isConstraintName)
                          (Int -> OID
OID (Int -> OID) -> Int -> OID
forall a b. (a -> b) -> a -> b
$ Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word -> Int) -> Word -> Int
forall a b. (a -> b) -> a -> b
$ Word -> Maybe Word -> Word
forall a. a -> Maybe a -> a
fromMaybe Word
0 Maybe Word
isConstraintOrdinalPosition),
                      _ucColumns :: HashSet (Column 'MySQL)
_ucColumns = Column -> HashSet Column
forall a. Hashable a => a -> HashSet a
HS.singleton (Text -> Column
Column Text
isColumnName)
                    }
                )
            else HashSet (UniqueConstraint 'MySQL)
forall a. HashSet a
HS.empty,
        _ptmiForeignKeys :: HashSet (ForeignKeyMetadata 'MySQL)
_ptmiForeignKeys =
          if InformationSchemaColumnKey
isColumnKey InformationSchemaColumnKey -> InformationSchemaColumnKey -> Bool
forall a. Eq a => a -> a -> Bool
== InformationSchemaColumnKey
MUL
            then
              ForeignKeyMetadata 'MySQL -> HashSet (ForeignKeyMetadata 'MySQL)
forall a. Hashable a => a -> HashSet a
HS.singleton
                ( ForeignKey 'MySQL -> ForeignKeyMetadata 'MySQL
forall (b :: BackendType). ForeignKey b -> ForeignKeyMetadata b
ForeignKeyMetadata
                    ( Constraint 'MySQL
-> TableName 'MySQL
-> NEHashMap (Column 'MySQL) (Column 'MySQL)
-> ForeignKey 'MySQL
forall (b :: BackendType).
Constraint b
-> TableName b -> NEHashMap (Column b) (Column b) -> ForeignKey b
ForeignKey
                        ( ConstraintName 'MySQL -> OID -> Constraint 'MySQL
forall (b :: BackendType). ConstraintName b -> OID -> Constraint b
Constraint
                            (Text -> ConstraintName
ConstraintName (Text -> ConstraintName) -> Text -> ConstraintName
forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
"" Maybe Text
isConstraintName)
                            (Int -> OID
OID (Int -> OID) -> Int -> OID
forall a b. (a -> b) -> a -> b
$ Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word -> Int) -> Word -> Int
forall a b. (a -> b) -> a -> b
$ Word -> Maybe Word -> Word
forall a. a -> Maybe a -> a
fromMaybe Word
0 Maybe Word
isConstraintOrdinalPosition)
                        )
                        ( TableName :: Text -> Maybe Text -> TableName
TableName
                            { name :: Text
name = (Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
"" Maybe Text
isReferencedTableName),
                              schema :: Maybe Text
schema = Maybe Text
isReferencedTableSchema
                            }
                        )
                        ( Column -> Column -> NEHashMap Column Column
forall k v. Hashable k => k -> v -> NEHashMap k v
NEHashMap.singleton
                            (Text -> Column
Column Text
isColumnName)
                            (Text -> Column
Column (Text -> Column) -> Text -> Column
forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
"" Maybe Text
isReferencedColumnName)
                        )
                    )
                )
            else HashSet (ForeignKeyMetadata 'MySQL)
forall a. HashSet a
HS.empty,
        _ptmiViewInfo :: Maybe ViewInfo
_ptmiViewInfo = Maybe ViewInfo
forall a. Maybe a
Nothing,
        _ptmiDescription :: Maybe PGDescription
_ptmiDescription = Maybe PGDescription
forall a. Maybe a
Nothing,
        _ptmiExtraTableMetadata :: ExtraTableMetadata 'MySQL
_ptmiExtraTableMetadata = ()
      }

mergeDBTableMetadata :: DBTableMetadata 'MySQL -> DBTableMetadata 'MySQL -> DBTableMetadata 'MySQL
mergeDBTableMetadata :: DBTableMetadata 'MySQL
-> DBTableMetadata 'MySQL -> DBTableMetadata 'MySQL
mergeDBTableMetadata DBTableMetadata 'MySQL
new DBTableMetadata 'MySQL
existing =
  DBTableMetadata :: forall (b :: BackendType).
OID
-> [RawColumnInfo b]
-> Maybe (PrimaryKey b (Column b))
-> HashSet (UniqueConstraint b)
-> HashSet (ForeignKeyMetadata b)
-> Maybe ViewInfo
-> Maybe PGDescription
-> ExtraTableMetadata b
-> DBTableMetadata b
DBTableMetadata
    { _ptmiOid :: OID
_ptmiOid = Int -> OID
OID Int
0,
      _ptmiColumns :: [RawColumnInfo 'MySQL]
_ptmiColumns = DBTableMetadata 'MySQL -> [RawColumnInfo 'MySQL]
forall (b :: BackendType). DBTableMetadata b -> [RawColumnInfo b]
_ptmiColumns DBTableMetadata 'MySQL
existing [RawColumnInfo 'MySQL]
-> [RawColumnInfo 'MySQL] -> [RawColumnInfo 'MySQL]
forall a. Semigroup a => a -> a -> a
<> DBTableMetadata 'MySQL -> [RawColumnInfo 'MySQL]
forall (b :: BackendType). DBTableMetadata b -> [RawColumnInfo b]
_ptmiColumns DBTableMetadata 'MySQL
new,
      _ptmiPrimaryKey :: Maybe (PrimaryKey 'MySQL (Column 'MySQL))
_ptmiPrimaryKey = DBTableMetadata 'MySQL -> Maybe (PrimaryKey 'MySQL (Column 'MySQL))
forall (b :: BackendType).
DBTableMetadata b -> Maybe (PrimaryKey b (Column b))
_ptmiPrimaryKey DBTableMetadata 'MySQL
existing Maybe (PrimaryKey 'MySQL Column)
-> Maybe (PrimaryKey 'MySQL Column)
-> Maybe (PrimaryKey 'MySQL Column)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> DBTableMetadata 'MySQL -> Maybe (PrimaryKey 'MySQL (Column 'MySQL))
forall (b :: BackendType).
DBTableMetadata b -> Maybe (PrimaryKey b (Column b))
_ptmiPrimaryKey DBTableMetadata 'MySQL
new, -- Only one column can be a PRIMARY KEY, so this is just a courtesy choice.
      _ptmiUniqueConstraints :: HashSet (UniqueConstraint 'MySQL)
_ptmiUniqueConstraints = DBTableMetadata 'MySQL -> HashSet (UniqueConstraint 'MySQL)
forall (b :: BackendType).
DBTableMetadata b -> HashSet (UniqueConstraint b)
_ptmiUniqueConstraints DBTableMetadata 'MySQL
existing HashSet (UniqueConstraint 'MySQL)
-> HashSet (UniqueConstraint 'MySQL)
-> HashSet (UniqueConstraint 'MySQL)
forall a. Semigroup a => a -> a -> a
<> DBTableMetadata 'MySQL -> HashSet (UniqueConstraint 'MySQL)
forall (b :: BackendType).
DBTableMetadata b -> HashSet (UniqueConstraint b)
_ptmiUniqueConstraints DBTableMetadata 'MySQL
new, -- union
      _ptmiForeignKeys :: HashSet (ForeignKeyMetadata 'MySQL)
_ptmiForeignKeys = DBTableMetadata 'MySQL -> HashSet (ForeignKeyMetadata 'MySQL)
forall (b :: BackendType).
DBTableMetadata b -> HashSet (ForeignKeyMetadata b)
_ptmiForeignKeys DBTableMetadata 'MySQL
existing HashSet (ForeignKeyMetadata 'MySQL)
-> HashSet (ForeignKeyMetadata 'MySQL)
-> HashSet (ForeignKeyMetadata 'MySQL)
forall a. Semigroup a => a -> a -> a
<> DBTableMetadata 'MySQL -> HashSet (ForeignKeyMetadata 'MySQL)
forall (b :: BackendType).
DBTableMetadata b -> HashSet (ForeignKeyMetadata b)
_ptmiForeignKeys DBTableMetadata 'MySQL
new, -- union
      _ptmiViewInfo :: Maybe ViewInfo
_ptmiViewInfo = DBTableMetadata 'MySQL -> Maybe ViewInfo
forall (b :: BackendType). DBTableMetadata b -> Maybe ViewInfo
_ptmiViewInfo DBTableMetadata 'MySQL
existing Maybe ViewInfo -> Maybe ViewInfo -> Maybe ViewInfo
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> DBTableMetadata 'MySQL -> Maybe ViewInfo
forall (b :: BackendType). DBTableMetadata b -> Maybe ViewInfo
_ptmiViewInfo DBTableMetadata 'MySQL
new,
      _ptmiDescription :: Maybe PGDescription
_ptmiDescription = DBTableMetadata 'MySQL -> Maybe PGDescription
forall (b :: BackendType). DBTableMetadata b -> Maybe PGDescription
_ptmiDescription DBTableMetadata 'MySQL
existing Maybe PGDescription -> Maybe PGDescription -> Maybe PGDescription
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> DBTableMetadata 'MySQL -> Maybe PGDescription
forall (b :: BackendType). DBTableMetadata b -> Maybe PGDescription
_ptmiDescription DBTableMetadata 'MySQL
new,
      _ptmiExtraTableMetadata :: ExtraTableMetadata 'MySQL
_ptmiExtraTableMetadata = ()
    }

data InformationSchema = InformationSchema
  { InformationSchema -> Text
isTableSchema :: Text,
    InformationSchema -> Text
isTableName :: Text,
    InformationSchema -> Text
isColumnName :: Text,
    InformationSchema -> Word
isOrdinalPosition :: Word,
    InformationSchema -> Maybe Text
isColumnDefault :: Maybe Text,
    InformationSchema -> Text
isIsNullable :: Text,
    InformationSchema -> Maybe Text
isDataType :: Maybe Text,
    InformationSchema -> Text
isColumnType :: Text,
    InformationSchema -> InformationSchemaColumnKey
isColumnKey :: InformationSchemaColumnKey,
    InformationSchema -> Text
isColumnComment :: Text,
    InformationSchema -> Maybe Text
isConstraintName :: Maybe Text,
    InformationSchema -> Maybe Word
isConstraintOrdinalPosition :: Maybe Word,
    InformationSchema -> Maybe Word
isPositionInUniqueConstraint :: Maybe Word,
    InformationSchema -> Maybe Text
isReferencedTableSchema :: Maybe Text,
    InformationSchema -> Maybe Text
isReferencedTableName :: Maybe Text,
    InformationSchema -> Maybe Text
isReferencedColumnName :: Maybe Text
  }
  deriving (Int -> InformationSchema -> ShowS
[InformationSchema] -> ShowS
InformationSchema -> String
(Int -> InformationSchema -> ShowS)
-> (InformationSchema -> String)
-> ([InformationSchema] -> ShowS)
-> Show InformationSchema
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [InformationSchema] -> ShowS
$cshowList :: [InformationSchema] -> ShowS
show :: InformationSchema -> String
$cshow :: InformationSchema -> String
showsPrec :: Int -> InformationSchema -> ShowS
$cshowsPrec :: Int -> InformationSchema -> ShowS
Show, InformationSchema -> InformationSchema -> Bool
(InformationSchema -> InformationSchema -> Bool)
-> (InformationSchema -> InformationSchema -> Bool)
-> Eq InformationSchema
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: InformationSchema -> InformationSchema -> Bool
$c/= :: InformationSchema -> InformationSchema -> Bool
== :: InformationSchema -> InformationSchema -> Bool
$c== :: InformationSchema -> InformationSchema -> Bool
Eq, (forall x. InformationSchema -> Rep InformationSchema x)
-> (forall x. Rep InformationSchema x -> InformationSchema)
-> Generic InformationSchema
forall x. Rep InformationSchema x -> InformationSchema
forall x. InformationSchema -> Rep InformationSchema x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep InformationSchema x -> InformationSchema
$cfrom :: forall x. InformationSchema -> Rep InformationSchema x
Generic)

instance QueryResults InformationSchema where
  convertResults :: [Field] -> [Maybe ByteString] -> InformationSchema
convertResults
    [ Field
fisTableSchema,
      Field
fisTableName,
      Field
fisColumnName,
      Field
fisOrdinalPosition,
      Field
fisColumnDefault,
      Field
fisIsNullable,
      Field
fisDataType,
      Field
fisColumnType,
      Field
fisColumnKey,
      Field
fisColumnComment,
      Field
fisConstraintName,
      Field
fisConstraintOrdinalPosition,
      Field
fisPositionInUniqueConstraint,
      Field
fisReferencedTableSchema,
      Field
fisReferencedTableName,
      Field
fisReferencedColumnName
      ]
    [ Maybe ByteString
visTableSchema,
      Maybe ByteString
visTableName,
      Maybe ByteString
visColumnName,
      Maybe ByteString
visOrdinalPosition,
      Maybe ByteString
visColumnDefault,
      Maybe ByteString
visIsNullable,
      Maybe ByteString
visDataType,
      Maybe ByteString
visColumnType,
      Maybe ByteString
visColumnKey,
      Maybe ByteString
visColumnComment,
      Maybe ByteString
visConstraintName,
      Maybe ByteString
visConstraintOrdinalPosition,
      Maybe ByteString
visPositionInUniqueConstraint,
      Maybe ByteString
visReferencedTableSchema,
      Maybe ByteString
visReferencedTableName,
      Maybe ByteString
visReferencedColumnName
      ] =
      Text
-> Text
-> Text
-> Word
-> Maybe Text
-> Text
-> Maybe Text
-> Text
-> InformationSchemaColumnKey
-> Text
-> Maybe Text
-> Maybe Word
-> Maybe Word
-> Maybe Text
-> Maybe Text
-> Maybe Text
-> InformationSchema
InformationSchema
        (Field -> Maybe ByteString -> Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisTableSchema Maybe ByteString
visTableSchema)
        (Field -> Maybe ByteString -> Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisTableName Maybe ByteString
visTableName)
        (Field -> Maybe ByteString -> Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisColumnName Maybe ByteString
visColumnName)
        (Field -> Maybe ByteString -> Word
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisOrdinalPosition Maybe ByteString
visOrdinalPosition)
        (Field -> Maybe ByteString -> Maybe Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisColumnDefault Maybe ByteString
visColumnDefault)
        (Field -> Maybe ByteString -> Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisIsNullable Maybe ByteString
visIsNullable)
        (Field -> Maybe ByteString -> Maybe Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisDataType Maybe ByteString
visDataType)
        (Field -> Maybe ByteString -> Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisColumnType Maybe ByteString
visColumnType)
        (Field -> Maybe ByteString -> InformationSchemaColumnKey
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisColumnKey Maybe ByteString
visColumnKey)
        (Field -> Maybe ByteString -> Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisColumnComment Maybe ByteString
visColumnComment)
        (Field -> Maybe ByteString -> Maybe Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisConstraintName Maybe ByteString
visConstraintName)
        (Field -> Maybe ByteString -> Maybe Word
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisConstraintOrdinalPosition Maybe ByteString
visConstraintOrdinalPosition)
        (Field -> Maybe ByteString -> Maybe Word
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisPositionInUniqueConstraint Maybe ByteString
visPositionInUniqueConstraint)
        (Field -> Maybe ByteString -> Maybe Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisReferencedTableSchema Maybe ByteString
visReferencedTableSchema)
        (Field -> Maybe ByteString -> Maybe Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisReferencedTableName Maybe ByteString
visReferencedTableName)
        (Field -> Maybe ByteString -> Maybe Text
forall a. Result a => Field -> Maybe ByteString -> a
convert Field
fisReferencedColumnName Maybe ByteString
visReferencedColumnName)
  convertResults [Field]
fs [Maybe ByteString]
vs = [Field] -> [Maybe ByteString] -> Int -> InformationSchema
forall a. [Field] -> [Maybe ByteString] -> Int -> a
convertError [Field]
fs [Maybe ByteString]
vs Int
16

data InformationSchemaColumnKey
  = PRI
  | UNI
  | MUL
  | -- | This field isn't NULLable and uses empty strings, by the looks of it.
    BLANK
  deriving (Int -> InformationSchemaColumnKey -> ShowS
[InformationSchemaColumnKey] -> ShowS
InformationSchemaColumnKey -> String
(Int -> InformationSchemaColumnKey -> ShowS)
-> (InformationSchemaColumnKey -> String)
-> ([InformationSchemaColumnKey] -> ShowS)
-> Show InformationSchemaColumnKey
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [InformationSchemaColumnKey] -> ShowS
$cshowList :: [InformationSchemaColumnKey] -> ShowS
show :: InformationSchemaColumnKey -> String
$cshow :: InformationSchemaColumnKey -> String
showsPrec :: Int -> InformationSchemaColumnKey -> ShowS
$cshowsPrec :: Int -> InformationSchemaColumnKey -> ShowS
Show, ReadPrec [InformationSchemaColumnKey]
ReadPrec InformationSchemaColumnKey
Int -> ReadS InformationSchemaColumnKey
ReadS [InformationSchemaColumnKey]
(Int -> ReadS InformationSchemaColumnKey)
-> ReadS [InformationSchemaColumnKey]
-> ReadPrec InformationSchemaColumnKey
-> ReadPrec [InformationSchemaColumnKey]
-> Read InformationSchemaColumnKey
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [InformationSchemaColumnKey]
$creadListPrec :: ReadPrec [InformationSchemaColumnKey]
readPrec :: ReadPrec InformationSchemaColumnKey
$creadPrec :: ReadPrec InformationSchemaColumnKey
readList :: ReadS [InformationSchemaColumnKey]
$creadList :: ReadS [InformationSchemaColumnKey]
readsPrec :: Int -> ReadS InformationSchemaColumnKey
$creadsPrec :: Int -> ReadS InformationSchemaColumnKey
Read, InformationSchemaColumnKey -> InformationSchemaColumnKey -> Bool
(InformationSchemaColumnKey -> InformationSchemaColumnKey -> Bool)
-> (InformationSchemaColumnKey
    -> InformationSchemaColumnKey -> Bool)
-> Eq InformationSchemaColumnKey
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: InformationSchemaColumnKey -> InformationSchemaColumnKey -> Bool
$c/= :: InformationSchemaColumnKey -> InformationSchemaColumnKey -> Bool
== :: InformationSchemaColumnKey -> InformationSchemaColumnKey -> Bool
$c== :: InformationSchemaColumnKey -> InformationSchemaColumnKey -> Bool
Eq, (forall x.
 InformationSchemaColumnKey -> Rep InformationSchemaColumnKey x)
-> (forall x.
    Rep InformationSchemaColumnKey x -> InformationSchemaColumnKey)
-> Generic InformationSchemaColumnKey
forall x.
Rep InformationSchemaColumnKey x -> InformationSchemaColumnKey
forall x.
InformationSchemaColumnKey -> Rep InformationSchemaColumnKey x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x.
Rep InformationSchemaColumnKey x -> InformationSchemaColumnKey
$cfrom :: forall x.
InformationSchemaColumnKey -> Rep InformationSchemaColumnKey x
Generic)

instance Result InformationSchemaColumnKey where
  convert :: Field -> Maybe ByteString -> InformationSchemaColumnKey
convert Field
f Maybe ByteString
mbs =
    case Maybe ByteString
mbs of
      Maybe ByteString
Nothing ->
        ResultError -> InformationSchemaColumnKey
forall a e. Exception e => e -> a
throw (ResultError -> InformationSchemaColumnKey)
-> ResultError -> InformationSchemaColumnKey
forall a b. (a -> b) -> a -> b
$
          String -> String -> String -> String -> ResultError
UnexpectedNull
            (ScalarType -> String
forall a. Show a => a -> String
show (ScalarType -> String) -> ScalarType -> String
forall a b. (a -> b) -> a -> b
$ Field -> ScalarType
fieldType Field
f)
            String
"InformationSchemaColumnKey"
            (ByteString -> String
B8.unpack (ByteString -> String) -> ByteString -> String
forall a b. (a -> b) -> a -> b
$ Field -> ByteString
fieldName Field
f)
            String
"COLUMN_KEY in INFORMATION_SCHEMA cannot be NULL"
      Just ByteString
bs -> case ByteString
bs of
        -- Could have used 'readMaybe' here, but we need the specific errors.
        ByteString
"PRI" -> InformationSchemaColumnKey
PRI -- primary key
        ByteString
"UNI" -> InformationSchemaColumnKey
UNI -- unique key
        ByteString
"MUL" -> InformationSchemaColumnKey
MUL -- foreign key (`MUL`tiple allowed, non-unique key)
        ByteString
"" -> InformationSchemaColumnKey
BLANK
        ByteString
x ->
          ResultError -> InformationSchemaColumnKey
forall a e. Exception e => e -> a
throw (ResultError -> InformationSchemaColumnKey)
-> ResultError -> InformationSchemaColumnKey
forall a b. (a -> b) -> a -> b
$
            String -> String -> String -> String -> ResultError
ConversionFailed
              (ScalarType -> String
forall a. Show a => a -> String
show (ScalarType -> String) -> ScalarType -> String
forall a b. (a -> b) -> a -> b
$ Field -> ScalarType
fieldType Field
f)
              String
"InformationSchemaColumnKey"
              (ByteString -> String
B8.unpack (ByteString -> String) -> ByteString -> String
forall a b. (a -> b) -> a -> b
$ Field -> ByteString
fieldName Field
f)
              (String
"COLUMN_KEY in INFORMATION_SCHEMA has value extraneous to the expected ENUM: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> ByteString -> String
B8.unpack ByteString
x)