2018-06-27 16:11:32 +03:00
|
|
|
module Ops
|
|
|
|
( initCatalogSafe
|
|
|
|
, cleanCatalog
|
|
|
|
, execQuery
|
|
|
|
) where
|
|
|
|
|
2018-09-14 14:41:51 +03:00
|
|
|
import Data.Time.Clock (UTCTime)
|
2019-01-02 14:24:17 +03:00
|
|
|
import Language.Haskell.TH.Syntax (Q, TExp, unTypeQ)
|
2019-03-01 12:17:22 +03:00
|
|
|
import Migrate (curCatalogVer)
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
|
|
import Hasura.Prelude
|
|
|
|
import Hasura.RQL.DDL.Schema.Table
|
|
|
|
import Hasura.RQL.Types
|
|
|
|
import Hasura.Server.Query
|
|
|
|
import Hasura.SQL.Types
|
|
|
|
|
2018-09-14 14:41:51 +03:00
|
|
|
import qualified Data.Aeson as A
|
|
|
|
import qualified Data.ByteString.Lazy as BL
|
|
|
|
import qualified Data.Text as T
|
2019-01-02 14:24:17 +03:00
|
|
|
import qualified Data.Yaml.TH as Y
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2018-09-14 14:41:51 +03:00
|
|
|
import qualified Database.PG.Query as Q
|
|
|
|
import qualified Database.PG.Query.Connection as Q
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
initCatalogSafe
|
|
|
|
:: (QErrM m, UserInfoM m, CacheRWM m, MonadTx m, MonadIO m, HasHttpManager m)
|
|
|
|
=> UTCTime -> m String
|
|
|
|
initCatalogSafe initTime = do
|
|
|
|
hdbCatalogExists <- liftTx $ Q.catchE defaultTxErrorHandler $
|
2018-06-27 16:11:32 +03:00
|
|
|
doesSchemaExist $ SchemaName "hdb_catalog"
|
2018-12-13 10:26:15 +03:00
|
|
|
bool (initCatalogStrict True initTime) onCatalogExists hdbCatalogExists
|
2018-06-27 16:11:32 +03:00
|
|
|
where
|
|
|
|
onCatalogExists = do
|
2018-12-13 10:26:15 +03:00
|
|
|
versionExists <- liftTx $ Q.catchE defaultTxErrorHandler $
|
2018-06-27 16:11:32 +03:00
|
|
|
doesVersionTblExist
|
|
|
|
(SchemaName "hdb_catalog") (TableName "hdb_version")
|
2018-12-13 10:26:15 +03:00
|
|
|
bool (initCatalogStrict False initTime) (return initialisedMsg) versionExists
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2019-01-02 14:24:17 +03:00
|
|
|
initialisedMsg = "the state is already initialised"
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
|
|
doesVersionTblExist sn tblN =
|
|
|
|
(runIdentity . Q.getRow) <$> Q.withQ [Q.sql|
|
|
|
|
SELECT EXISTS (
|
|
|
|
SELECT 1
|
|
|
|
FROM pg_tables
|
|
|
|
WHERE schemaname = $1 AND tablename = $2)
|
|
|
|
|] (sn, tblN) False
|
|
|
|
|
|
|
|
doesSchemaExist sn =
|
|
|
|
(runIdentity . Q.getRow) <$> Q.withQ [Q.sql|
|
|
|
|
SELECT EXISTS (
|
|
|
|
SELECT 1
|
|
|
|
FROM information_schema.schemata
|
|
|
|
WHERE schema_name = $1
|
|
|
|
)
|
|
|
|
|] (Identity sn) False
|
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
initCatalogStrict
|
|
|
|
:: (QErrM m, UserInfoM m, CacheRWM m, MonadTx m, MonadIO m, HasHttpManager m)
|
|
|
|
=> Bool -> UTCTime -> m String
|
|
|
|
initCatalogStrict createSchema initTime = do
|
|
|
|
liftTx $ Q.catchE defaultTxErrorHandler $
|
2018-06-27 16:11:32 +03:00
|
|
|
when createSchema $ do
|
|
|
|
Q.unitQ "CREATE SCHEMA hdb_catalog" () False
|
|
|
|
-- This is where the generated views and triggers are stored
|
|
|
|
Q.unitQ "CREATE SCHEMA hdb_views" () False
|
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
pgcryptoExtExists <- liftTx $
|
|
|
|
Q.catchE defaultTxErrorHandler $ isExtAvailable "pgcrypto"
|
|
|
|
|
2018-09-14 14:41:51 +03:00
|
|
|
if pgcryptoExtExists
|
|
|
|
-- only if we created the schema, create the extension
|
2018-12-13 10:26:15 +03:00
|
|
|
then when createSchema $ liftTx $ Q.unitQE needsPgCryptoExt
|
|
|
|
"CREATE EXTENSION IF NOT EXISTS pgcrypto SCHEMA public" () False
|
2019-01-08 12:05:25 +03:00
|
|
|
else throw500 pgcryptoNotAvlMsg
|
2018-09-14 14:41:51 +03:00
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
liftTx $ Q.catchE defaultTxErrorHandler $ do
|
2018-06-27 16:11:32 +03:00
|
|
|
Q.Discard () <- Q.multiQ $(Q.sqlFromFile "src-rsr/initialise.sql")
|
|
|
|
return ()
|
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
-- add default metadata
|
|
|
|
void $ runQueryM metadataQuery
|
2018-06-27 16:11:32 +03:00
|
|
|
|
2018-09-05 14:42:20 +03:00
|
|
|
setAllAsSystemDefined >> addVersion initTime
|
2019-01-02 14:24:17 +03:00
|
|
|
return "successfully initialised"
|
2018-09-14 14:41:51 +03:00
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
where
|
2018-12-13 10:26:15 +03:00
|
|
|
metadataQuery =
|
|
|
|
$(unTypeQ (Y.decodeFile "src-rsr/hdb_metadata.yaml" :: Q (TExp RQLQuery)))
|
2018-09-14 14:41:51 +03:00
|
|
|
needsPgCryptoExt :: Q.PGTxErr -> QErr
|
|
|
|
needsPgCryptoExt e@(Q.PGTxErr _ _ _ err) =
|
|
|
|
case err of
|
|
|
|
Q.PGIUnexpected _ -> (err500 PostgresError pgcryptoReqdMsg) { qeInternal = Just $ A.toJSON e }
|
|
|
|
Q.PGIStatement pgErr ->
|
|
|
|
case Q.edStatusCode pgErr of
|
|
|
|
Just "42501" -> err500 PostgresError pgcryptoPermsMsg
|
|
|
|
_ -> (err500 PostgresError pgcryptoReqdMsg) { qeInternal = Just $ A.toJSON e }
|
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
addVersion modTime = liftTx $ Q.catchE defaultTxErrorHandler $
|
2018-06-27 16:11:32 +03:00
|
|
|
Q.unitQ [Q.sql|
|
2019-01-28 16:55:28 +03:00
|
|
|
INSERT INTO "hdb_catalog"."hdb_version"
|
|
|
|
(version, upgraded_on) VALUES ($1, $2)
|
2018-06-27 16:11:32 +03:00
|
|
|
|] (curCatalogVer, modTime) False
|
|
|
|
|
2018-09-14 14:41:51 +03:00
|
|
|
isExtAvailable :: T.Text -> Q.Tx Bool
|
|
|
|
isExtAvailable sn =
|
2018-06-27 16:11:32 +03:00
|
|
|
(runIdentity . Q.getRow) <$> Q.withQ [Q.sql|
|
|
|
|
SELECT EXISTS (
|
|
|
|
SELECT 1
|
|
|
|
FROM pg_catalog.pg_available_extensions
|
|
|
|
WHERE name = $1
|
|
|
|
)
|
|
|
|
|] (Identity sn) False
|
|
|
|
|
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
setAllAsSystemDefined :: (MonadTx m) => m ()
|
|
|
|
setAllAsSystemDefined = liftTx $ Q.catchE defaultTxErrorHandler $ do
|
2018-09-05 14:42:20 +03:00
|
|
|
Q.unitQ "UPDATE hdb_catalog.hdb_table SET is_system_defined = 'true'" () False
|
|
|
|
Q.unitQ "UPDATE hdb_catalog.hdb_relationship SET is_system_defined = 'true'" () False
|
|
|
|
Q.unitQ "UPDATE hdb_catalog.hdb_permission SET is_system_defined = 'true'" () False
|
|
|
|
Q.unitQ "UPDATE hdb_catalog.hdb_query_template SET is_system_defined = 'true'" () False
|
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
cleanCatalog :: (MonadTx m) => m ()
|
|
|
|
cleanCatalog = liftTx $ Q.catchE defaultTxErrorHandler $ do
|
2018-06-27 16:11:32 +03:00
|
|
|
-- This is where the generated views and triggers are stored
|
|
|
|
Q.unitQ "DROP SCHEMA IF EXISTS hdb_views CASCADE" () False
|
|
|
|
Q.unitQ "DROP SCHEMA hdb_catalog CASCADE" () False
|
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
execQuery
|
|
|
|
:: (MonadTx m, CacheRWM m, MonadIO m, UserInfoM m, HasHttpManager m)
|
|
|
|
=> BL.ByteString -> m BL.ByteString
|
|
|
|
execQuery queryBs = do
|
2018-06-27 16:11:32 +03:00
|
|
|
query <- case A.decode queryBs of
|
|
|
|
Just jVal -> decodeValue jVal
|
|
|
|
Nothing -> throw400 InvalidJSON "invalid json"
|
2018-12-13 10:26:15 +03:00
|
|
|
buildSchemaCache
|
|
|
|
runQueryM query
|
2018-09-14 14:41:51 +03:00
|
|
|
|
|
|
|
|
|
|
|
-- error messages
|
|
|
|
pgcryptoReqdMsg :: T.Text
|
|
|
|
pgcryptoReqdMsg =
|
2019-01-08 12:05:25 +03:00
|
|
|
"pgcrypto extension is required, but could not install; encountered unknown postgres error"
|
2018-09-14 14:41:51 +03:00
|
|
|
|
|
|
|
pgcryptoPermsMsg :: T.Text
|
|
|
|
pgcryptoPermsMsg =
|
|
|
|
"pgcrypto extension is required, but current user doesn't have permission to create it. "
|
|
|
|
<> "Please grant superuser permission or setup initial schema via "
|
|
|
|
<> "https://docs.hasura.io/1.0/graphql/manual/deployment/postgres-permissions.html"
|
2019-01-08 12:05:25 +03:00
|
|
|
|
|
|
|
pgcryptoNotAvlMsg :: T.Text
|
|
|
|
pgcryptoNotAvlMsg =
|
|
|
|
"pgcrypto extension is required, but could not find the extension in the "
|
|
|
|
<> "PostgreSQL server. Please make sure this extension is available."
|