2021-08-04 14:42:24 +03:00
|
|
|
module Hasura.Backends.MySQL.Connection
|
|
|
|
( runJSONPathQuery
|
|
|
|
, resolveSourceConfig
|
|
|
|
, resolveDatabaseMetadata
|
|
|
|
, fetchAllRows
|
|
|
|
)
|
|
|
|
where
|
2021-07-13 16:32:15 +03:00
|
|
|
|
2021-07-23 15:25:16 +03:00
|
|
|
|
2021-08-04 14:42:24 +03:00
|
|
|
import Data.Aeson hiding (Result)
|
|
|
|
import qualified Data.Aeson as J
|
|
|
|
import Data.Aeson.Text (encodeToTextBuilder)
|
|
|
|
import Data.ByteString (ByteString)
|
|
|
|
import qualified Data.Environment as Env
|
|
|
|
import qualified Data.HashMap.Strict as HM
|
|
|
|
import Data.Pool
|
|
|
|
import Data.Scientific (fromFloatDigits)
|
|
|
|
import qualified Data.Text as T
|
|
|
|
import Data.Text.Encoding (decodeUtf8)
|
|
|
|
import Data.Text.Lazy (toStrict)
|
|
|
|
import Data.Text.Lazy.Builder (toLazyText)
|
2021-07-13 16:32:15 +03:00
|
|
|
import Database.MySQL.Base
|
2021-08-04 14:42:24 +03:00
|
|
|
import Database.MySQL.Base.Types (Field (..))
|
|
|
|
import qualified Database.MySQL.Simple.Result as MySQL
|
|
|
|
import Hasura.Backends.MySQL.Meta (getMetadata)
|
|
|
|
import Hasura.Backends.MySQL.ToQuery (Query (..))
|
2021-07-13 16:32:15 +03:00
|
|
|
import Hasura.Backends.MySQL.Types
|
|
|
|
import Hasura.Base.Error
|
2021-08-04 14:42:24 +03:00
|
|
|
import Hasura.Prelude
|
2021-07-13 16:32:15 +03:00
|
|
|
import Hasura.RQL.Types.Common
|
2021-07-15 15:44:26 +03:00
|
|
|
import Hasura.RQL.Types.Source
|
|
|
|
import Hasura.SQL.Backend
|
2021-07-13 16:32:15 +03:00
|
|
|
|
|
|
|
|
2021-09-23 15:37:56 +03:00
|
|
|
resolveSourceConfig
|
|
|
|
:: (MonadIO m)
|
|
|
|
=> SourceName -> ConnSourceConfig -> Env.Environment -> m (Either QErr SourceConfig)
|
2021-07-23 15:25:16 +03:00
|
|
|
resolveSourceConfig _name csc@ConnSourceConfig{_cscPoolSettings = ConnPoolSettings{..}, ..} _env = do
|
2021-07-13 16:32:15 +03:00
|
|
|
let connectInfo =
|
|
|
|
defaultConnectInfo
|
|
|
|
{ connectHost = T.unpack _cscHost
|
|
|
|
, connectPort = _cscPort
|
|
|
|
, connectUser = T.unpack _cscUser
|
|
|
|
, connectPassword = T.unpack _cscPassword
|
|
|
|
, connectDatabase = T.unpack _cscDatabase
|
|
|
|
}
|
2021-07-23 15:25:16 +03:00
|
|
|
runExceptT $
|
|
|
|
SourceConfig csc <$>
|
|
|
|
liftIO
|
|
|
|
(createPool
|
|
|
|
(connect connectInfo)
|
|
|
|
close
|
|
|
|
1
|
|
|
|
(fromIntegral _cscIdleTimeout)
|
|
|
|
(fromIntegral _cscMaxConnections))
|
2021-07-15 15:44:26 +03:00
|
|
|
|
|
|
|
|
2021-08-04 14:42:24 +03:00
|
|
|
|
|
|
|
resolveDatabaseMetadata :: (MonadIO m) => SourceConfig -> m (Either QErr (ResolvedSource 'MySQL))
|
2021-07-15 15:44:26 +03:00
|
|
|
resolveDatabaseMetadata sc@SourceConfig{..} =
|
|
|
|
runExceptT $ do
|
|
|
|
metadata <- liftIO $ withResource scConnectionPool (getMetadata scConfig)
|
|
|
|
pure $ ResolvedSource sc metadata mempty mempty
|
2021-08-04 14:42:24 +03:00
|
|
|
|
|
|
|
|
|
|
|
parseFieldResult :: Field -> Maybe ByteString -> Value
|
|
|
|
parseFieldResult f@Field{..} mBs =
|
|
|
|
case fieldType of
|
|
|
|
Long ->
|
|
|
|
let fvalue :: Double = MySQL.convert f mBs
|
|
|
|
in Number $ fromFloatDigits fvalue
|
|
|
|
VarString ->
|
|
|
|
let fvalue :: Text = MySQL.convert f mBs
|
|
|
|
in J.String fvalue
|
|
|
|
DateTime -> maybe J.Null (J.String . decodeUtf8) mBs
|
|
|
|
_ -> error $ "parseResult: not implemented yet " <> show f <> " " <> show mBs
|
2021-08-17 01:19:24 +03:00
|
|
|
-- TODO: handle remaining cases
|
2021-08-04 14:42:24 +03:00
|
|
|
|
|
|
|
|
|
|
|
fieldsToAeson :: [Field] -> [[Maybe ByteString]] -> [Value]
|
|
|
|
fieldsToAeson column rows =
|
|
|
|
[ Object $ HM.fromList $
|
|
|
|
[ (decodeUtf8 (fieldName c)) .= (parseFieldResult c r)
|
|
|
|
| (c, r) <- (zip column row :: [(Field, Maybe ByteString)]) ]
|
|
|
|
| row <- (rows :: [[Maybe ByteString]]) ]
|
|
|
|
|
|
|
|
|
|
|
|
runJSONPathQuery :: (MonadError QErr m, MonadIO m)
|
|
|
|
=> (Pool Connection)
|
|
|
|
-> Query
|
|
|
|
-> m Text
|
|
|
|
runJSONPathQuery pool (Query querySql) = do
|
|
|
|
result <- liftIO $
|
|
|
|
withResource pool $ \conn -> do
|
|
|
|
query conn querySql
|
|
|
|
result <- storeResult conn
|
|
|
|
fields <- fetchFields result
|
|
|
|
rows <- fetchAllRows result
|
|
|
|
pure $ fieldsToAeson fields rows
|
|
|
|
pure $ toStrict $ toLazyText $ encodeToTextBuilder $ toJSON result
|
|
|
|
|
|
|
|
|
|
|
|
fetchAllRows :: Result -> IO [[Maybe ByteString]]
|
|
|
|
fetchAllRows r = reverse <$> go [] r
|
|
|
|
where
|
|
|
|
go acc res =
|
|
|
|
fetchRow res >>= \case
|
|
|
|
[] -> pure acc
|
|
|
|
r' -> go (r' : acc) res
|