2021-05-27 18:06:13 +03:00
|
|
|
{-# LANGUAGE ViewPatterns #-}
|
|
|
|
|
2021-02-23 20:37:27 +03:00
|
|
|
module Hasura.Backends.MSSQL.DDL.RunSQL
|
2021-09-24 01:56:37 +03:00
|
|
|
( runSQL,
|
|
|
|
MSSQLRunSQL (..),
|
|
|
|
sqlContainsDDLKeyword,
|
2021-02-25 21:15:55 +03:00
|
|
|
)
|
2021-02-23 20:37:27 +03:00
|
|
|
where
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
import Data.Aeson qualified as J
|
|
|
|
import Data.Aeson.TH
|
|
|
|
import Data.HashMap.Strict qualified as M
|
|
|
|
import Data.HashSet qualified as HS
|
|
|
|
import Data.String (fromString)
|
|
|
|
import Data.Text qualified as T
|
|
|
|
import Database.ODBC.Internal qualified as ODBC
|
|
|
|
import Hasura.Backends.MSSQL.Connection
|
|
|
|
import Hasura.Backends.MSSQL.Meta
|
|
|
|
import Hasura.Base.Error
|
|
|
|
import Hasura.EncJSON
|
|
|
|
import Hasura.Prelude
|
|
|
|
import Hasura.RQL.DDL.Schema
|
|
|
|
import Hasura.RQL.DDL.Schema.Diff
|
|
|
|
import Hasura.RQL.Types hiding (TableName, tmTable)
|
|
|
|
import Hasura.Server.Utils (quoteRegex)
|
|
|
|
import Text.Regex.TDFA qualified as TDFA
|
2021-02-23 20:37:27 +03:00
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
data MSSQLRunSQL = MSSQLRunSQL
|
|
|
|
{ _mrsSql :: Text,
|
|
|
|
_mrsSource :: !SourceName
|
|
|
|
}
|
|
|
|
deriving (Show, Eq)
|
2021-02-25 21:15:55 +03:00
|
|
|
|
|
|
|
$(deriveJSON hasuraJSON ''MSSQLRunSQL)
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
runSQL ::
|
|
|
|
(MonadIO m, CacheRWM m, MonadError QErr m, MetadataM m) =>
|
|
|
|
MSSQLRunSQL ->
|
|
|
|
m EncJSON
|
2021-02-23 20:37:27 +03:00
|
|
|
runSQL (MSSQLRunSQL sqlText source) = do
|
2021-09-23 15:37:56 +03:00
|
|
|
SourceInfo _ tableCache _ sourceConfig _ <- askSourceInfo @'MSSQL source
|
2021-05-27 18:06:13 +03:00
|
|
|
let pool = _mscConnectionPool sourceConfig
|
|
|
|
results <- if sqlContainsDDLKeyword sqlText then withMetadataCheck tableCache pool else runSQLQuery pool
|
2021-02-25 21:15:55 +03:00
|
|
|
pure $ encJFromJValue $ toResult results
|
2021-05-27 18:06:13 +03:00
|
|
|
where
|
|
|
|
runSQLQuery pool = withMSSQLPool pool $ \conn ->
|
|
|
|
ODBC.query conn $ fromString $ T.unpack sqlText
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
toTableMeta dbTablesMeta =
|
|
|
|
M.toList dbTablesMeta <&> \(table, dbTableMeta) ->
|
|
|
|
TableMeta table dbTableMeta [] -- No computed fields
|
2021-05-27 18:06:13 +03:00
|
|
|
withMetadataCheck tableCache pool = do
|
|
|
|
-- If the SQL modifies the schema of the database then check for any metadata changes
|
|
|
|
preActionTablesMeta <- toTableMeta <$> loadDBMetadata pool
|
|
|
|
results <- runSQLQuery pool
|
|
|
|
postActionTablesMeta <- toTableMeta <$> loadDBMetadata pool
|
|
|
|
let trackedTablesMeta = filter (flip M.member tableCache . tmTable) preActionTablesMeta
|
|
|
|
schemaDiff = getSchemaDiff trackedTablesMeta postActionTablesMeta
|
|
|
|
metadataUpdater <- execWriterT $ processSchemaDiff source tableCache schemaDiff
|
|
|
|
-- Build schema cache with updated metadata
|
|
|
|
withNewInconsistentObjsCheck $
|
2021-09-24 01:56:37 +03:00
|
|
|
buildSchemaCacheWithInvalidations mempty {ciSources = HS.singleton source} metadataUpdater
|
2021-05-27 18:06:13 +03:00
|
|
|
pure results
|
|
|
|
|
|
|
|
sqlContainsDDLKeyword :: Text -> Bool
|
2021-09-24 01:56:37 +03:00
|
|
|
sqlContainsDDLKeyword =
|
|
|
|
TDFA.match
|
|
|
|
$$( quoteRegex
|
|
|
|
TDFA.defaultCompOpt
|
|
|
|
{ TDFA.caseSensitive = False,
|
|
|
|
TDFA.multiline = True,
|
|
|
|
TDFA.lastStarGreedy = True
|
|
|
|
}
|
|
|
|
TDFA.defaultExecOpt
|
|
|
|
{ TDFA.captureGroups = False
|
|
|
|
}
|
|
|
|
"\\balter\\b|\\bdrop\\b|\\bsp_rename\\b"
|
|
|
|
)
|
2021-02-23 20:37:27 +03:00
|
|
|
|
|
|
|
toResult :: [[(ODBC.Column, ODBC.Value)]] -> RunSQLRes
|
|
|
|
toResult result = case result of
|
2021-09-24 01:56:37 +03:00
|
|
|
[] -> RunSQLRes "CommandOk" J.Null
|
|
|
|
(firstRow : _) -> RunSQLRes "TuplesOk" $ J.toJSON $ toHeader firstRow : toRows result
|
2021-02-23 20:37:27 +03:00
|
|
|
where
|
2021-09-24 01:56:37 +03:00
|
|
|
toRows = map $ map $ odbcValueToJValue . snd
|
2021-02-23 20:37:27 +03:00
|
|
|
toHeader = map $ J.String . ODBC.columnName . fst
|