2019-08-14 02:34:37 +03:00
|
|
|
|
-- | Description: Create/delete SQL tables to/from Hasura metadata.
|
|
|
|
|
module Hasura.RQL.DDL.Schema.Table
|
|
|
|
|
( TrackTable(..)
|
|
|
|
|
, runTrackTableQ
|
|
|
|
|
, trackExistingTableOrViewP2
|
2019-07-11 10:41:20 +03:00
|
|
|
|
|
2019-08-14 02:34:37 +03:00
|
|
|
|
, UntrackTable(..)
|
|
|
|
|
, runUntrackTableQ
|
2019-03-18 19:22:21 +03:00
|
|
|
|
|
2019-08-14 02:34:37 +03:00
|
|
|
|
, SetTableIsEnum(..)
|
|
|
|
|
, runSetExistingTableIsEnumQ
|
|
|
|
|
|
|
|
|
|
, buildTableCache
|
|
|
|
|
, delTableAndDirectDeps
|
|
|
|
|
, processTableChanges
|
|
|
|
|
) where
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
2019-03-22 10:08:42 +03:00
|
|
|
|
import Hasura.EncJSON
|
2018-06-27 16:11:32 +03:00
|
|
|
|
import Hasura.Prelude
|
|
|
|
|
import Hasura.RQL.DDL.Deps
|
|
|
|
|
import Hasura.RQL.DDL.Permission
|
2019-08-14 02:34:37 +03:00
|
|
|
|
import {-# SOURCE #-} Hasura.RQL.DDL.Schema.Cache
|
|
|
|
|
import Hasura.RQL.DDL.Schema.Catalog
|
2018-06-27 16:11:32 +03:00
|
|
|
|
import Hasura.RQL.DDL.Schema.Diff
|
2019-07-22 15:47:13 +03:00
|
|
|
|
import Hasura.RQL.DDL.Schema.Enum
|
2019-03-01 12:17:22 +03:00
|
|
|
|
import Hasura.RQL.DDL.Schema.Rename
|
2018-06-27 16:11:32 +03:00
|
|
|
|
import Hasura.RQL.Types
|
2019-05-08 10:36:43 +03:00
|
|
|
|
import Hasura.RQL.Types.Catalog
|
2018-06-27 16:11:32 +03:00
|
|
|
|
import Hasura.SQL.Types
|
|
|
|
|
|
2019-08-14 02:34:37 +03:00
|
|
|
|
import qualified Database.PG.Query as Q
|
|
|
|
|
import qualified Hasura.GraphQL.Schema as GS
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
2019-08-14 02:34:37 +03:00
|
|
|
|
import Control.Lens.Extended hiding ((.=))
|
2018-06-27 16:11:32 +03:00
|
|
|
|
import Data.Aeson
|
|
|
|
|
import Data.Aeson.Casing
|
|
|
|
|
import Data.Aeson.TH
|
2019-08-14 02:34:37 +03:00
|
|
|
|
import Instances.TH.Lift ()
|
|
|
|
|
import Language.Haskell.TH.Syntax (Lift)
|
|
|
|
|
import Network.URI.Extended ()
|
|
|
|
|
|
|
|
|
|
import qualified Data.HashMap.Strict as M
|
|
|
|
|
import qualified Data.Text as T
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
2019-07-22 15:47:13 +03:00
|
|
|
|
data TrackTable
|
2018-06-27 16:11:32 +03:00
|
|
|
|
= TrackTable
|
2019-07-22 15:47:13 +03:00
|
|
|
|
{ tName :: !QualifiedTable
|
|
|
|
|
, tIsEnum :: !Bool
|
|
|
|
|
} deriving (Show, Eq, Lift)
|
|
|
|
|
|
|
|
|
|
instance FromJSON TrackTable where
|
|
|
|
|
parseJSON v = withOptions <|> withoutOptions
|
|
|
|
|
where
|
|
|
|
|
withOptions = flip (withObject "TrackTable") v $ \o -> TrackTable
|
|
|
|
|
<$> o .: "table"
|
|
|
|
|
<*> o .:? "is_enum" .!= False
|
|
|
|
|
withoutOptions = TrackTable <$> parseJSON v <*> pure False
|
|
|
|
|
|
|
|
|
|
instance ToJSON TrackTable where
|
|
|
|
|
toJSON (TrackTable name isEnum)
|
|
|
|
|
| isEnum = object [ "table" .= name, "is_enum" .= isEnum ]
|
|
|
|
|
| otherwise = toJSON name
|
|
|
|
|
|
|
|
|
|
data SetTableIsEnum
|
|
|
|
|
= SetTableIsEnum
|
|
|
|
|
{ stieTable :: !QualifiedTable
|
|
|
|
|
, stieIsEnum :: !Bool
|
|
|
|
|
} deriving (Show, Eq, Lift)
|
|
|
|
|
$(deriveJSON (aesonDrop 4 snakeCase) ''SetTableIsEnum)
|
|
|
|
|
|
|
|
|
|
data UntrackTable =
|
|
|
|
|
UntrackTable
|
|
|
|
|
{ utTable :: !QualifiedTable
|
|
|
|
|
, utCascade :: !(Maybe Bool)
|
|
|
|
|
} deriving (Show, Eq, Lift)
|
|
|
|
|
$(deriveJSON (aesonDrop 2 snakeCase){omitNothingFields=True} ''UntrackTable)
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
2019-07-11 10:41:20 +03:00
|
|
|
|
-- | Track table/view, Phase 1:
|
|
|
|
|
-- Validate table tracking operation. Fails if table is already being tracked,
|
|
|
|
|
-- or if a function with the same name is being tracked.
|
2018-12-13 10:26:15 +03:00
|
|
|
|
trackExistingTableOrViewP1
|
|
|
|
|
:: (CacheRM m, UserInfoM m, QErrM m) => TrackTable -> m ()
|
2019-07-22 15:47:13 +03:00
|
|
|
|
trackExistingTableOrViewP1 TrackTable { tName = vn } = do
|
2018-06-27 16:11:32 +03:00
|
|
|
|
adminOnly
|
2018-12-13 10:26:15 +03:00
|
|
|
|
rawSchemaCache <- askSchemaCache
|
2018-06-27 16:11:32 +03:00
|
|
|
|
when (M.member vn $ scTables rawSchemaCache) $
|
|
|
|
|
throw400 AlreadyTracked $ "view/table already tracked : " <>> vn
|
2019-07-11 10:41:20 +03:00
|
|
|
|
let qf = fmap (FunctionName . getTableTxt) vn
|
|
|
|
|
when (M.member qf $ scFunctions rawSchemaCache) $
|
|
|
|
|
throw400 NotSupported $ "function with name " <> vn <<> " already exists"
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
2018-06-29 14:00:22 +03:00
|
|
|
|
trackExistingTableOrViewP2
|
2019-07-22 15:47:13 +03:00
|
|
|
|
:: (QErrM m, CacheRWM m, MonadTx m, MonadIO m, HasHttpManager m, HasSQLGenCtx m)
|
|
|
|
|
=> TrackTable -> m EncJSON
|
2019-08-14 02:34:37 +03:00
|
|
|
|
trackExistingTableOrViewP2 (TrackTable tableName isEnum) = do
|
2018-11-23 16:02:46 +03:00
|
|
|
|
sc <- askSchemaCache
|
|
|
|
|
let defGCtx = scDefaultRemoteGCtx sc
|
2019-07-22 15:47:13 +03:00
|
|
|
|
GS.checkConflictingNode defGCtx $ GS.qualObjectToName tableName
|
2019-08-14 02:34:37 +03:00
|
|
|
|
saveTableToCatalog tableName isEnum
|
2019-07-22 15:47:13 +03:00
|
|
|
|
buildSchemaCacheFor (MOTable tableName)
|
2018-06-27 16:11:32 +03:00
|
|
|
|
return successMsg
|
|
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
|
runTrackTableQ
|
2019-07-22 15:47:13 +03:00
|
|
|
|
:: (QErrM m, CacheRWM m, MonadTx m, UserInfoM m, MonadIO m, HasHttpManager m, HasSQLGenCtx m)
|
2019-03-18 19:22:21 +03:00
|
|
|
|
=> TrackTable -> m EncJSON
|
2018-12-13 10:26:15 +03:00
|
|
|
|
runTrackTableQ q = do
|
|
|
|
|
trackExistingTableOrViewP1 q
|
2019-07-22 15:47:13 +03:00
|
|
|
|
trackExistingTableOrViewP2 q
|
|
|
|
|
|
|
|
|
|
runSetExistingTableIsEnumQ
|
|
|
|
|
:: (QErrM m, CacheRWM m, MonadTx m, UserInfoM m, MonadIO m, HasHttpManager m, HasSQLGenCtx m)
|
|
|
|
|
=> SetTableIsEnum -> m EncJSON
|
|
|
|
|
runSetExistingTableIsEnumQ (SetTableIsEnum tableName isEnum) = do
|
|
|
|
|
adminOnly
|
|
|
|
|
void $ askTabInfo tableName -- assert that table is tracked
|
2019-08-14 02:34:37 +03:00
|
|
|
|
updateTableIsEnumInCatalog tableName isEnum
|
2019-07-22 15:47:13 +03:00
|
|
|
|
buildSchemaCacheFor (MOTable tableName)
|
|
|
|
|
return successMsg
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
2019-07-22 15:47:13 +03:00
|
|
|
|
unTrackExistingTableOrViewP1
|
|
|
|
|
:: (CacheRM m, UserInfoM m, QErrM m) => UntrackTable -> m ()
|
|
|
|
|
unTrackExistingTableOrViewP1 (UntrackTable vn _) = do
|
|
|
|
|
adminOnly
|
|
|
|
|
rawSchemaCache <- askSchemaCache
|
|
|
|
|
case M.lookup vn (scTables rawSchemaCache) of
|
|
|
|
|
Just ti ->
|
|
|
|
|
-- Check if table/view is system defined
|
|
|
|
|
when (_tiSystemDefined ti) $ throw400 NotSupported $
|
|
|
|
|
vn <<> " is system defined, cannot untrack"
|
|
|
|
|
Nothing -> throw400 AlreadyUntracked $
|
|
|
|
|
"view/table already untracked : " <>> vn
|
|
|
|
|
|
|
|
|
|
unTrackExistingTableOrViewP2
|
|
|
|
|
:: (QErrM m, CacheRWM m, MonadTx m)
|
|
|
|
|
=> UntrackTable -> m EncJSON
|
|
|
|
|
unTrackExistingTableOrViewP2 (UntrackTable qtn cascade) = do
|
|
|
|
|
sc <- askSchemaCache
|
|
|
|
|
|
|
|
|
|
-- Get relational, query template and function dependants
|
|
|
|
|
let allDeps = getDependentObjs sc (SOTable qtn)
|
|
|
|
|
indirectDeps = filter (not . isDirectDep) allDeps
|
|
|
|
|
|
|
|
|
|
-- Report bach with an error if cascade is not set
|
|
|
|
|
when (indirectDeps /= [] && not (or cascade)) $ reportDepsExt indirectDeps []
|
|
|
|
|
|
2019-08-14 02:34:37 +03:00
|
|
|
|
-- Purge all the dependents from state
|
|
|
|
|
mapM_ purgeDependentObject indirectDeps
|
2019-07-22 15:47:13 +03:00
|
|
|
|
|
|
|
|
|
-- delete the table and its direct dependencies
|
|
|
|
|
delTableAndDirectDeps qtn
|
|
|
|
|
|
|
|
|
|
return successMsg
|
|
|
|
|
where
|
|
|
|
|
isDirectDep = \case
|
|
|
|
|
(SOTableObj dtn _) -> qtn == dtn
|
|
|
|
|
_ -> False
|
|
|
|
|
|
|
|
|
|
runUntrackTableQ
|
|
|
|
|
:: (QErrM m, CacheRWM m, MonadTx m, UserInfoM m)
|
|
|
|
|
=> UntrackTable -> m EncJSON
|
|
|
|
|
runUntrackTableQ q = do
|
|
|
|
|
unTrackExistingTableOrViewP1 q
|
|
|
|
|
unTrackExistingTableOrViewP2 q
|
|
|
|
|
|
2019-03-01 12:17:22 +03:00
|
|
|
|
processTableChanges :: (MonadTx m, CacheRWM m)
|
2019-08-11 18:34:38 +03:00
|
|
|
|
=> TableInfo PGColumnInfo -> TableDiff -> m Bool
|
2018-06-27 16:11:32 +03:00
|
|
|
|
processTableChanges ti tableDiff = do
|
2019-03-01 12:17:22 +03:00
|
|
|
|
-- If table rename occurs then don't replace constraints and
|
|
|
|
|
-- process dropped/added columns, because schema reload happens eventually
|
2018-06-27 16:11:32 +03:00
|
|
|
|
sc <- askSchemaCache
|
2019-07-22 15:47:13 +03:00
|
|
|
|
let tn = _tiName ti
|
2019-03-01 12:17:22 +03:00
|
|
|
|
withOldTabName = do
|
|
|
|
|
replaceConstraints tn
|
2019-09-17 04:51:11 +03:00
|
|
|
|
-- replace description
|
|
|
|
|
replaceDescription tn
|
|
|
|
|
-- for all the dropped columns
|
2019-03-01 12:17:22 +03:00
|
|
|
|
procDroppedCols tn
|
|
|
|
|
procAddedCols tn
|
|
|
|
|
procAlteredCols sc tn
|
|
|
|
|
|
|
|
|
|
withNewTabName newTN = do
|
|
|
|
|
let tnGQL = GS.qualObjectToName newTN
|
|
|
|
|
defGCtx = scDefaultRemoteGCtx sc
|
|
|
|
|
-- check for GraphQL schema conflicts on new name
|
|
|
|
|
GS.checkConflictingNode defGCtx tnGQL
|
|
|
|
|
void $ procAlteredCols sc tn
|
|
|
|
|
-- update new table in catalog
|
|
|
|
|
renameTableInCatalog newTN tn
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
maybe withOldTabName withNewTabName mNewName
|
|
|
|
|
|
2018-06-27 16:11:32 +03:00
|
|
|
|
where
|
2019-09-17 04:51:11 +03:00
|
|
|
|
TableDiff mNewName droppedCols addedCols alteredCols _ constraints descM = tableDiff
|
2019-03-01 12:17:22 +03:00
|
|
|
|
replaceConstraints tn = flip modTableInCache tn $ \tInfo ->
|
2019-07-22 15:47:13 +03:00
|
|
|
|
return $ tInfo {_tiUniqOrPrimConstraints = constraints}
|
2019-03-01 12:17:22 +03:00
|
|
|
|
|
2019-09-17 04:51:11 +03:00
|
|
|
|
replaceDescription tn = flip modTableInCache tn $ \tInfo ->
|
|
|
|
|
return $ tInfo {_tiDescription = descM}
|
|
|
|
|
|
2019-03-01 12:17:22 +03:00
|
|
|
|
procDroppedCols tn =
|
|
|
|
|
forM_ droppedCols $ \droppedCol ->
|
|
|
|
|
-- Drop the column from the cache
|
|
|
|
|
delColFromCache droppedCol tn
|
|
|
|
|
|
|
|
|
|
procAddedCols tn =
|
|
|
|
|
-- In the newly added columns check that there is no conflict with relationships
|
2019-09-17 04:51:11 +03:00
|
|
|
|
forM_ addedCols $ \rawInfo -> do
|
|
|
|
|
let colName = prciName rawInfo
|
2019-07-22 15:47:13 +03:00
|
|
|
|
case M.lookup (fromPGCol colName) $ _tiFieldInfoMap ti of
|
2019-03-01 12:17:22 +03:00
|
|
|
|
Just (FIRelationship _) ->
|
|
|
|
|
throw400 AlreadyExists $ "cannot add column " <> colName
|
|
|
|
|
<<> " in table " <> tn <<>
|
|
|
|
|
" as a relationship with the name already exists"
|
2019-07-22 15:47:13 +03:00
|
|
|
|
_ -> do
|
|
|
|
|
info <- processColumnInfoUsingCache tn rawInfo
|
|
|
|
|
addColToCache colName info tn
|
2019-03-01 12:17:22 +03:00
|
|
|
|
|
2019-05-02 15:31:32 +03:00
|
|
|
|
procAlteredCols sc tn = fmap or $ forM alteredCols $
|
2019-09-17 04:51:11 +03:00
|
|
|
|
\( PGRawColumnInfo oldName oldType _ _ _
|
|
|
|
|
, newRawInfo@(PGRawColumnInfo newName newType _ _ _) ) -> do
|
2019-07-22 15:47:13 +03:00
|
|
|
|
let performColumnUpdate = do
|
|
|
|
|
newInfo <- processColumnInfoUsingCache tn newRawInfo
|
|
|
|
|
updColInCache newName newInfo tn
|
|
|
|
|
|
|
|
|
|
if | oldName /= newName -> renameColInCatalog oldName newName tn ti $> True
|
|
|
|
|
|
|
|
|
|
| oldType /= newType -> do
|
|
|
|
|
let colId = SOTableObj tn $ TOCol oldName
|
|
|
|
|
typeDepObjs = getDependentObjsWith (== DROnType) sc colId
|
|
|
|
|
|
|
|
|
|
unless (null typeDepObjs) $ throw400 DependencyError $
|
|
|
|
|
"cannot change type of column " <> oldName <<> " in table "
|
|
|
|
|
<> tn <<> " because of the following dependencies : " <>
|
|
|
|
|
reportSchemaObjs typeDepObjs
|
|
|
|
|
|
|
|
|
|
performColumnUpdate
|
|
|
|
|
|
|
|
|
|
-- If any dependent permissions found with the column whose type being altered is
|
|
|
|
|
-- provided with a session variable, then rebuild permission info and update the cache
|
|
|
|
|
let sessVarDepObjs = getDependentObjsWith (== DRSessionVariable) sc colId
|
|
|
|
|
forM_ sessVarDepObjs $ \case
|
|
|
|
|
SOTableObj qt (TOPerm rn pt) -> rebuildPermInfo qt rn pt
|
|
|
|
|
_ -> throw500 "unexpected schema dependency found for altering column type"
|
|
|
|
|
pure False
|
|
|
|
|
|
|
|
|
|
| otherwise -> performColumnUpdate $> False
|
2018-06-27 16:11:32 +03:00
|
|
|
|
|
2018-12-13 10:26:15 +03:00
|
|
|
|
delTableAndDirectDeps
|
|
|
|
|
:: (QErrM m, CacheRWM m, MonadTx m) => QualifiedTable -> m ()
|
2019-01-25 06:31:54 +03:00
|
|
|
|
delTableAndDirectDeps qtn@(QualifiedObject sn tn) = do
|
2018-11-16 15:40:23 +03:00
|
|
|
|
liftTx $ Q.catchE defaultTxErrorHandler $ do
|
|
|
|
|
Q.unitQ [Q.sql|
|
|
|
|
|
DELETE FROM "hdb_catalog"."hdb_relationship"
|
|
|
|
|
WHERE table_schema = $1 AND table_name = $2
|
|
|
|
|
|] (sn, tn) False
|
|
|
|
|
Q.unitQ [Q.sql|
|
|
|
|
|
DELETE FROM "hdb_catalog"."hdb_permission"
|
|
|
|
|
WHERE table_schema = $1 AND table_name = $2
|
|
|
|
|
|] (sn, tn) False
|
|
|
|
|
Q.unitQ [Q.sql|
|
|
|
|
|
DELETE FROM "hdb_catalog"."event_triggers"
|
|
|
|
|
WHERE schema_name = $1 AND table_name = $2
|
|
|
|
|
|] (sn, tn) False
|
2019-08-14 02:34:37 +03:00
|
|
|
|
deleteTableFromCatalog qtn
|
2018-11-16 15:40:23 +03:00
|
|
|
|
delTableFromCache qtn
|
|
|
|
|
|
2019-08-11 18:34:38 +03:00
|
|
|
|
-- | Builds an initial @'TableCache' 'PGColumnInfo'@ from catalog information. Does not fill in
|
2019-07-22 15:47:13 +03:00
|
|
|
|
-- '_tiRolePermInfoMap' or '_tiEventTriggerInfoMap' at all, and '_tiFieldInfoMap' only contains
|
|
|
|
|
-- columns, not relationships; those pieces of information are filled in by later stages.
|
|
|
|
|
buildTableCache
|
|
|
|
|
:: forall m. (MonadTx m, CacheRWM m)
|
2019-08-11 18:34:38 +03:00
|
|
|
|
=> [CatalogTable] -> m (TableCache PGColumnInfo)
|
2019-07-22 15:47:13 +03:00
|
|
|
|
buildTableCache = processTableCache <=< buildRawTableCache
|
2018-06-27 16:11:32 +03:00
|
|
|
|
where
|
2019-07-22 15:47:13 +03:00
|
|
|
|
withTable name = withSchemaObject $
|
|
|
|
|
InconsistentMetadataObj (MOTable name) MOTTable (toJSON name)
|
|
|
|
|
|
|
|
|
|
-- Step 1: Build the raw table cache from metadata information.
|
2019-08-11 18:34:38 +03:00
|
|
|
|
buildRawTableCache :: [CatalogTable] -> m (TableCache PGRawColumnInfo)
|
2019-07-22 15:47:13 +03:00
|
|
|
|
buildRawTableCache catalogTables = fmap (M.fromList . catMaybes) . for catalogTables $
|
|
|
|
|
\(CatalogTable name isSystemDefined isEnum maybeInfo) -> withTable name $ do
|
|
|
|
|
catalogInfo <- onNothing maybeInfo $
|
|
|
|
|
throw400 NotExists $ "no such table/view exists in postgres: " <>> name
|
|
|
|
|
|
2019-09-17 04:51:11 +03:00
|
|
|
|
let CatalogTableInfo columns constraints primaryKeyColumnNames viewInfo maybeDesc = catalogInfo
|
2019-07-22 15:47:13 +03:00
|
|
|
|
columnFields = M.fromList . flip map columns $ \column ->
|
|
|
|
|
(fromPGCol $ prciName column, FIColumn column)
|
|
|
|
|
|
|
|
|
|
primaryKeyColumns = flip filter columns $ \column ->
|
|
|
|
|
prciName column `elem` primaryKeyColumnNames
|
|
|
|
|
fetchEnumValues = fetchAndValidateEnumValues name primaryKeyColumns columns
|
|
|
|
|
|
|
|
|
|
maybeEnumValues <- if isEnum then Just <$> fetchEnumValues else pure Nothing
|
|
|
|
|
|
|
|
|
|
let info = TableInfo
|
|
|
|
|
{ _tiName = name
|
|
|
|
|
, _tiSystemDefined = isSystemDefined
|
|
|
|
|
, _tiFieldInfoMap = columnFields
|
|
|
|
|
, _tiRolePermInfoMap = mempty
|
|
|
|
|
, _tiUniqOrPrimConstraints = constraints
|
|
|
|
|
, _tiPrimaryKeyCols = primaryKeyColumnNames
|
|
|
|
|
, _tiViewInfo = viewInfo
|
|
|
|
|
, _tiEventTriggerInfoMap = mempty
|
2019-09-17 04:51:11 +03:00
|
|
|
|
, _tiEnumValues = maybeEnumValues
|
|
|
|
|
, _tiDescription = maybeDesc
|
|
|
|
|
}
|
2019-07-22 15:47:13 +03:00
|
|
|
|
pure (name, info)
|
|
|
|
|
|
|
|
|
|
-- Step 2: Process the raw table cache to replace Postgres column types with logical column
|
|
|
|
|
-- types.
|
2019-08-11 18:34:38 +03:00
|
|
|
|
processTableCache :: TableCache PGRawColumnInfo -> m (TableCache PGColumnInfo)
|
2019-07-22 15:47:13 +03:00
|
|
|
|
processTableCache rawTables = fmap (M.mapMaybe id) . for rawTables $ \rawInfo -> do
|
|
|
|
|
let tableName = _tiName rawInfo
|
|
|
|
|
withTable tableName $ rawInfo
|
|
|
|
|
& tiFieldInfoMap.traverse._FIColumn %%~ processColumnInfo enumTables tableName
|
|
|
|
|
where
|
|
|
|
|
enumTables = M.mapMaybe _tiEnumValues rawTables
|
|
|
|
|
|
2019-08-11 18:34:38 +03:00
|
|
|
|
-- | “Processes” a 'PGRawColumnInfo' into a 'PGColumnInfo' by resolving its type using a map of known
|
2019-07-22 15:47:13 +03:00
|
|
|
|
-- enum tables.
|
|
|
|
|
processColumnInfo
|
|
|
|
|
:: (QErrM m)
|
|
|
|
|
=> M.HashMap QualifiedTable EnumValues -- ^ known enum tables
|
|
|
|
|
-> QualifiedTable -- ^ the table this column belongs to
|
2019-08-11 18:34:38 +03:00
|
|
|
|
-> PGRawColumnInfo -- ^ the column’s raw information
|
|
|
|
|
-> m PGColumnInfo
|
2019-07-22 15:47:13 +03:00
|
|
|
|
processColumnInfo enumTables tableName rawInfo = do
|
|
|
|
|
resolvedType <- resolveColumnType
|
2019-08-11 18:34:38 +03:00
|
|
|
|
pure PGColumnInfo
|
2019-07-22 15:47:13 +03:00
|
|
|
|
{ pgiName = prciName rawInfo
|
|
|
|
|
, pgiType = resolvedType
|
2019-09-17 04:51:11 +03:00
|
|
|
|
, pgiIsNullable = prciIsNullable rawInfo
|
|
|
|
|
, pgiDescription = prciDescription rawInfo
|
|
|
|
|
}
|
2019-07-22 15:47:13 +03:00
|
|
|
|
where
|
|
|
|
|
resolveColumnType =
|
|
|
|
|
case prciReferences rawInfo of
|
|
|
|
|
-- no referenced tables? definitely not an enum
|
|
|
|
|
[] -> pure $ PGColumnScalar (prciType rawInfo)
|
|
|
|
|
|
|
|
|
|
-- one referenced table? might be an enum, so check if the referenced table is an enum
|
|
|
|
|
[referencedTableName] -> pure $ M.lookup referencedTableName enumTables & maybe
|
|
|
|
|
(PGColumnScalar $ prciType rawInfo)
|
|
|
|
|
(PGColumnEnumReference . EnumReference referencedTableName)
|
|
|
|
|
|
|
|
|
|
-- multiple referenced tables? we could check if any of them are enums, but the schema is
|
|
|
|
|
-- strange, so let’s just reject it
|
|
|
|
|
referencedTables -> throw400 ConstraintViolation
|
|
|
|
|
$ "cannot handle exotic schema: column " <> prciName rawInfo <<> " in table "
|
|
|
|
|
<> tableName <<> " references multiple foreign tables ("
|
|
|
|
|
<> T.intercalate ", " (map dquote referencedTables) <> ")?"
|
|
|
|
|
|
|
|
|
|
-- | Like 'processColumnInfo', but uses the information in the current schema cache to resolve a
|
|
|
|
|
-- column’s type.
|
2019-08-11 18:34:38 +03:00
|
|
|
|
processColumnInfoUsingCache :: (CacheRM m, QErrM m) => QualifiedTable -> PGRawColumnInfo -> m PGColumnInfo
|
2019-07-22 15:47:13 +03:00
|
|
|
|
processColumnInfoUsingCache tableName rawInfo = do
|
|
|
|
|
tables <- scTables <$> askSchemaCache
|
|
|
|
|
processColumnInfo (M.mapMaybe _tiEnumValues tables) tableName rawInfo
|