{-# LANGUAGE TemplateHaskell #-} module Hasura.Backends.MySQL.SQL ( runSQL, RunSQL (..), ) where import Data.Aeson qualified as J import Data.Aeson.TH import Data.ByteString hiding (null, reverse) import Data.Pool (withResource) import Data.String (fromString) import Data.Text qualified as T import Data.Text.Encoding (decodeUtf8With) import Data.Text.Encoding.Error (lenientDecode) import Database.MySQL.Base (fetchFields, query, storeResult) import Database.MySQL.Base.Types (Field (fieldName)) import Hasura.Backends.MySQL.Connection (fetchAllRows) import Hasura.Backends.MySQL.Types (SourceConfig (..)) import Hasura.Base.Error import Hasura.EncJSON import Hasura.Prelude import Hasura.RQL.DDL.Schema (RunSQLRes (..)) import Hasura.RQL.Types.Common import Hasura.RQL.Types.Metadata import Hasura.RQL.Types.SchemaCache import Hasura.RQL.Types.SchemaCache.Build import Hasura.SQL.Backend data RunSQL = RunSQL { _Sql :: Text, _Source :: SourceName } deriving (Show, Eq) $(deriveJSON hasuraJSON ''RunSQL) runSQL :: (MonadIO m, CacheRWM m, MonadError QErr m, MetadataM m) => RunSQL -> m EncJSON runSQL (RunSQL sql source) = do pool <- scConnectionPool <$> askSourceConfig @'MySQL source result :: [[Maybe ByteString]] <- liftIO $ withResource pool $ \conn -> do query conn (fromString . T.unpack $ sql) r <- storeResult conn fieldNames <- fmap (Just . fieldName) <$> fetchFields r -- fieldNames as Maybes for convenience rows <- fetchAllRows r pure (fieldNames : rows) pure . encJFromJValue $ if null result then RunSQLRes "CommandOK" J.Null else RunSQLRes "TuplesOk" . J.toJSON . (fmap . fmap . fmap) (decodeUtf8With lenientDecode) $ result