mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 20:41:49 +03:00
511 lines
19 KiB
Haskell
511 lines
19 KiB
Haskell
module Hasura.GraphQL.Resolve.Insert
|
|
(convertInsert)
|
|
where
|
|
|
|
import Data.Has
|
|
import Hasura.Prelude
|
|
import Hasura.Server.Utils
|
|
|
|
import qualified Data.Aeson as J
|
|
import qualified Data.Aeson.Casing as J
|
|
import qualified Data.Aeson.TH as J
|
|
import qualified Data.HashMap.Strict as Map
|
|
import qualified Data.HashMap.Strict.InsOrd as OMap
|
|
import qualified Data.Sequence as Seq
|
|
import qualified Data.Text as T
|
|
import qualified Language.GraphQL.Draft.Syntax as G
|
|
|
|
import qualified Database.PG.Query as Q
|
|
import qualified Hasura.RQL.DML.Insert as RI
|
|
import qualified Hasura.RQL.DML.Returning as RR
|
|
import qualified Hasura.RQL.DML.Select as RS
|
|
import qualified Hasura.RQL.GBoolExp as RB
|
|
|
|
import qualified Hasura.SQL.DML as S
|
|
|
|
import Hasura.GraphQL.Resolve.Context
|
|
import Hasura.GraphQL.Resolve.InputValue
|
|
import Hasura.GraphQL.Resolve.Mutation
|
|
import Hasura.GraphQL.Resolve.Select
|
|
import Hasura.GraphQL.Validate.Field
|
|
import Hasura.GraphQL.Validate.Types
|
|
import Hasura.RQL.DML.Internal (dmlTxErrorHandler)
|
|
import Hasura.RQL.GBoolExp (toSQLBoolExp)
|
|
import Hasura.RQL.Types
|
|
import Hasura.SQL.Types
|
|
import Hasura.SQL.Value
|
|
|
|
newtype InsResp
|
|
= InsResp
|
|
{ _irResponse :: Maybe J.Object
|
|
} deriving (Show, Eq)
|
|
$(J.deriveJSON (J.aesonDrop 3 J.snakeCase) ''InsResp)
|
|
|
|
data AnnIns a
|
|
= AnnIns
|
|
{ _aiInsObj :: !a
|
|
, _aiConflictClause :: !(Maybe RI.ConflictClauseP1)
|
|
, _aiView :: !QualifiedTable
|
|
, _aiTableCols :: ![PGColInfo]
|
|
, _aiDefVals :: !(Map.HashMap PGCol S.SQLExp)
|
|
} deriving (Show, Eq)
|
|
|
|
type SingleObjIns = AnnIns AnnInsObj
|
|
type MultiObjIns = AnnIns [AnnInsObj]
|
|
|
|
data RelIns a
|
|
= RelIns
|
|
{ _riAnnIns :: !a
|
|
, _riRelInfo :: !RelInfo
|
|
} deriving (Show, Eq)
|
|
|
|
type ObjRelIns = RelIns SingleObjIns
|
|
type ArrRelIns = RelIns MultiObjIns
|
|
|
|
type PGColWithValue = (PGCol, PGColValue)
|
|
|
|
data InsWithExp
|
|
= InsWithExp
|
|
{ _iweExp :: !S.CTE
|
|
, _iweConflictCtx :: !(Maybe RI.ConflictCtx)
|
|
, _iwePrepArgs :: !(Seq.Seq Q.PrepArg)
|
|
} deriving (Show, Eq)
|
|
|
|
data AnnInsObj
|
|
= AnnInsObj
|
|
{ _aioColumns :: ![(PGCol, PGColType, PGColValue)]
|
|
, _aioObjRels :: ![ObjRelIns]
|
|
, _aioArrRels :: ![ArrRelIns]
|
|
} deriving (Show, Eq)
|
|
|
|
mkAnnInsObj
|
|
:: (MonadError QErr m, Has InsCtxMap r, MonadReader r m)
|
|
=> RelationInfoMap
|
|
-> AnnGObject
|
|
-> m AnnInsObj
|
|
mkAnnInsObj relInfoMap annObj =
|
|
foldrM (traverseInsObj relInfoMap) emptyInsObj $ OMap.toList annObj
|
|
where
|
|
emptyInsObj = AnnInsObj [] [] []
|
|
|
|
traverseInsObj
|
|
:: (MonadError QErr m, Has InsCtxMap r, MonadReader r m)
|
|
=> RelationInfoMap
|
|
-> (G.Name, AnnGValue)
|
|
-> AnnInsObj
|
|
-> m AnnInsObj
|
|
traverseInsObj rim (gName, annVal) defVal@(AnnInsObj cols objRels arrRels) =
|
|
case annVal of
|
|
AGScalar colty mColVal -> do
|
|
let col = PGCol $ G.unName gName
|
|
colVal = fromMaybe (PGNull colty) mColVal
|
|
return (AnnInsObj ((col, colty, colVal):cols) objRels arrRels)
|
|
|
|
_ -> do
|
|
objM <- asObjectM annVal
|
|
-- if relational insert input is 'null' then ignore
|
|
-- return default value
|
|
fmap (fromMaybe defVal) $ forM objM $ \obj -> do
|
|
let relName = RelName $ G.unName gName
|
|
onConflictM = OMap.lookup "on_conflict" obj
|
|
dataVal <- onNothing (OMap.lookup "data" obj) $
|
|
throw500 "\"data\" object not found"
|
|
relInfo <- onNothing (Map.lookup relName rim) $
|
|
throw500 $ "relation " <> relName <<> " not found"
|
|
|
|
let rTable = riRTable relInfo
|
|
InsCtx rtView rtCols rtDefVals rtRelInfoMap rtUpdPerm <- getInsCtx rTable
|
|
|
|
withPathK (G.unName gName) $ case riType relInfo of
|
|
ObjRel -> do
|
|
dataObj <- asObject dataVal
|
|
annDataObj <- mkAnnInsObj rtRelInfoMap dataObj
|
|
ccM <- forM onConflictM $ parseOnConflict rTable rtUpdPerm
|
|
let singleObjIns = AnnIns annDataObj ccM rtView rtCols rtDefVals
|
|
objRelIns = RelIns singleObjIns relInfo
|
|
return (AnnInsObj cols (objRelIns:objRels) arrRels)
|
|
|
|
ArrRel -> do
|
|
arrDataVals <- asArray dataVal
|
|
let withNonEmptyArrData = do
|
|
annDataObjs <- forM arrDataVals $ \arrDataVal -> do
|
|
dataObj <- asObject arrDataVal
|
|
mkAnnInsObj rtRelInfoMap dataObj
|
|
ccM <- forM onConflictM $ parseOnConflict rTable rtUpdPerm
|
|
let multiObjIns = AnnIns annDataObjs ccM rtView rtCols rtDefVals
|
|
arrRelIns = RelIns multiObjIns relInfo
|
|
return (AnnInsObj cols objRels (arrRelIns:arrRels))
|
|
-- if array relation insert input data has empty objects
|
|
-- then ignore and return default value
|
|
bool withNonEmptyArrData (return defVal) $ null arrDataVals
|
|
|
|
parseOnConflict
|
|
:: (MonadError QErr m)
|
|
=> QualifiedTable -> Maybe UpdPermForIns
|
|
-> AnnGValue -> m RI.ConflictClauseP1
|
|
parseOnConflict tn updFiltrM val = withPathK "on_conflict" $
|
|
flip withObject val $ \_ obj -> do
|
|
constraint <- RI.Constraint <$> parseConstraint obj
|
|
updCols <- getUpdCols obj
|
|
case updCols of
|
|
[] -> return $ RI.CP1DoNothing $ Just constraint
|
|
_ -> do
|
|
(_, updFiltr) <- onNothing updFiltrM $ throw500
|
|
"cannot update columns since update permission is not defined"
|
|
return $ RI.CP1Update constraint updCols $ toSQLBoolExp (S.mkQual tn) updFiltr
|
|
|
|
where
|
|
getUpdCols o = do
|
|
updColsVal <- onNothing (OMap.lookup "update_columns" o) $ throw500
|
|
"\"update_columns\" argument in expected in \"on_conflict\" field "
|
|
parseColumns updColsVal
|
|
|
|
parseConstraint o = do
|
|
v <- onNothing (OMap.lookup "constraint" o) $ throw500
|
|
"\"constraint\" is expected, but not found"
|
|
(_, enumVal) <- asEnumVal v
|
|
return $ ConstraintName $ G.unName $ G.unEnumValue enumVal
|
|
|
|
toSQLExps :: (MonadError QErr m, MonadState PrepArgs m)
|
|
=> [(PGCol, AnnGValue)] -> m [(PGCol, S.SQLExp)]
|
|
toSQLExps cols =
|
|
forM cols $ \(c, v) -> do
|
|
prepExpM <- asPGColValM v >>= mapM prepare
|
|
let prepExp = fromMaybe (S.SEUnsafe "NULL") prepExpM
|
|
return (c, prepExp)
|
|
|
|
mkSQLRow :: Map.HashMap PGCol S.SQLExp -> [(PGCol, S.SQLExp)] -> [S.SQLExp]
|
|
mkSQLRow defVals withPGCol =
|
|
Map.elems $ Map.union (Map.fromList withPGCol) defVals
|
|
|
|
mkInsertQ :: MonadError QErr m => QualifiedTable
|
|
-> Maybe RI.ConflictClauseP1 -> [(PGCol, AnnGValue)]
|
|
-> [PGCol] -> Map.HashMap PGCol S.SQLExp -> RoleName
|
|
-> m InsWithExp
|
|
mkInsertQ vn onConflictM insCols tableCols defVals role = do
|
|
(givenCols, args) <- flip runStateT Seq.Empty $ toSQLExps insCols
|
|
let sqlConflict = RI.toSQLConflict <$> onConflictM
|
|
sqlExps = mkSQLRow defVals givenCols
|
|
sqlInsert = S.SQLInsert vn tableCols [sqlExps] sqlConflict $ Just S.returningStar
|
|
adminIns = return $ InsWithExp (S.CTEInsert sqlInsert) Nothing args
|
|
nonAdminInsert = do
|
|
ccM <- mapM RI.extractConflictCtx onConflictM
|
|
let cteIns = S.CTEInsert sqlInsert{S.siConflict=Nothing}
|
|
return $ InsWithExp cteIns ccM args
|
|
|
|
bool nonAdminInsert adminIns $ isAdmin role
|
|
|
|
mkBoolExp
|
|
:: (MonadError QErr m, MonadState PrepArgs m)
|
|
=> QualifiedTable -> [(PGColInfo, PGColValue)]
|
|
-> m S.BoolExp
|
|
mkBoolExp tn colInfoVals =
|
|
RB.toSQLBoolExp (S.mkQual tn) . BoolAnd <$>
|
|
mapM (fmap BoolFld . uncurry f) colInfoVals
|
|
where
|
|
f ci@(PGColInfo _ colTy _) colVal =
|
|
AVCol ci . pure . AEQ True <$> prepare (colTy, colVal)
|
|
|
|
mkSelQ :: MonadError QErr m => QualifiedTable
|
|
-> [PGColInfo] -> [PGColWithValue] -> m InsWithExp
|
|
mkSelQ tn allColInfos pgColsWithVal = do
|
|
(whereExp, args) <- flip runStateT Seq.Empty $ mkBoolExp tn colWithInfos
|
|
let sqlSel = S.mkSelect { S.selExtr = [S.selectStar]
|
|
, S.selFrom = Just $ S.mkSimpleFromExp tn
|
|
, S.selWhere = Just $ S.WhereFrag whereExp
|
|
}
|
|
|
|
return $ InsWithExp (S.CTESelect sqlSel) Nothing args
|
|
where
|
|
colWithInfos = mergeListsWith pgColsWithVal allColInfos
|
|
(\(c, _) ci -> c == pgiName ci)
|
|
(\(_, v) ci -> (ci, v))
|
|
|
|
execWithExp
|
|
:: QualifiedTable
|
|
-> InsWithExp
|
|
-> RR.MutFlds
|
|
-> Q.TxE QErr RespBody
|
|
execWithExp tn (InsWithExp withExp ccM args) flds = do
|
|
RI.setConflictCtx ccM
|
|
runIdentity . Q.getRow
|
|
<$> Q.rawQE dmlTxErrorHandler (Q.fromBuilder sqlBuilder) (toList args) True
|
|
where
|
|
sqlBuilder = toSQL $ RR.mkSelWith tn withExp flds True
|
|
|
|
|
|
insertAndRetCols
|
|
:: QualifiedTable
|
|
-> InsWithExp
|
|
-> T.Text
|
|
-> [PGColInfo]
|
|
-> Q.TxE QErr [PGColWithValue]
|
|
insertAndRetCols tn withExp errMsg retCols = do
|
|
resBS <- execWithExp tn withExp [("response", RR.MRet annSelFlds)]
|
|
insResp <- decodeFromBS resBS
|
|
resObj <- onNothing (_irResponse insResp) $ throwVE errMsg
|
|
forM retCols $ \(PGColInfo col colty _) -> do
|
|
val <- onNothing (Map.lookup (getPGColTxt col) resObj) $
|
|
throw500 $ "column " <> col <<> "not returned by postgres"
|
|
pgColVal <- RB.pgValParser colty val
|
|
return (col, pgColVal)
|
|
where
|
|
annSelFlds = flip map retCols $ \pgci ->
|
|
(fromPGCol $ pgiName pgci, RS.FCol pgci)
|
|
|
|
-- | validate an insert object based on insert columns,
|
|
-- | insert object relations and additional columns from parent
|
|
validateInsert
|
|
:: (MonadError QErr m)
|
|
=> [PGCol] -- ^ inserting columns
|
|
-> [RelInfo] -- ^ object relation inserts
|
|
-> [PGCol] -- ^ additional fields from parent
|
|
-> m ()
|
|
validateInsert insCols objRels addCols = do
|
|
-- validate insertCols
|
|
unless (null insConflictCols) $ throwVE $
|
|
"cannot insert " <> showPGCols insConflictCols
|
|
<> " columns as their values are already being determined by parent insert"
|
|
|
|
forM_ objRels $ \relInfo -> do
|
|
let lCols = map fst $ riMapping relInfo
|
|
relName = riName relInfo
|
|
relNameTxt = getRelTxt relName
|
|
lColConflicts = lCols `intersect` (addCols <> insCols)
|
|
withPathK relNameTxt $ unless (null lColConflicts) $ throwVE $
|
|
"cannot insert object relation ship " <> relName
|
|
<<> " as " <> showPGCols lColConflicts
|
|
<> " column values are already determined"
|
|
where
|
|
insConflictCols = insCols `intersect` addCols
|
|
|
|
-- | insert an object relationship and return affected rows
|
|
-- | and parent dependent columns
|
|
insertObjRel
|
|
:: RoleName
|
|
-> ObjRelIns
|
|
-> Q.TxE QErr (Int, [PGColWithValue])
|
|
insertObjRel role objRelIns =
|
|
withPathK relNameTxt $ do
|
|
(aRows, withExp) <- insertObj role tn singleObjIns []
|
|
let errMsg = "cannot proceed to insert object relation "
|
|
<> relName <<> " since insert to table " <> tn <<> " affects zero rows"
|
|
retColsWithVals <- insertAndRetCols tn withExp errMsg $
|
|
getColInfos rCols allCols
|
|
let c = mergeListsWith mapCols retColsWithVals
|
|
(\(_, rCol) (col, _) -> rCol == col)
|
|
(\(lCol, _) (_, colVal) -> (lCol, colVal))
|
|
return (aRows, c)
|
|
where
|
|
RelIns singleObjIns relInfo = objRelIns
|
|
relName = riName relInfo
|
|
relNameTxt = getRelTxt relName
|
|
mapCols = riMapping relInfo
|
|
tn = riRTable relInfo
|
|
rCols = map snd mapCols
|
|
allCols = _aiTableCols singleObjIns
|
|
|
|
-- | insert an array relationship and return affected rows
|
|
insertArrRel
|
|
:: RoleName
|
|
-> [PGColWithValue]
|
|
-> ArrRelIns
|
|
-> Q.TxE QErr Int
|
|
insertArrRel role resCols arrRelIns =
|
|
withPathK relNameTxt $ do
|
|
let addCols = mergeListsWith resCols colMapping
|
|
(\(col, _) (lCol, _) -> col == lCol)
|
|
(\(_, colVal) (_, rCol) -> (rCol, colVal))
|
|
|
|
resBS <- insertMultipleObjects role tn multiObjIns addCols mutFlds "data"
|
|
resObj <- decodeFromBS resBS
|
|
onNothing (Map.lookup ("affected_rows" :: T.Text) resObj) $
|
|
throw500 "affected_rows not returned in array rel insert"
|
|
where
|
|
RelIns multiObjIns relInfo = arrRelIns
|
|
colMapping = riMapping relInfo
|
|
tn = riRTable relInfo
|
|
relNameTxt = getRelTxt $ riName relInfo
|
|
mutFlds = [("affected_rows", RR.MCount)]
|
|
|
|
-- | insert an object with object and array relationships
|
|
insertObj
|
|
:: RoleName
|
|
-> QualifiedTable
|
|
-> SingleObjIns
|
|
-> [PGColWithValue] -- ^ additional fields
|
|
-> Q.TxE QErr (Int, InsWithExp)
|
|
insertObj role tn singleObjIns addCols = do
|
|
-- validate insert
|
|
validateInsert (map _1 cols) (map _riRelInfo objRels) $ map fst addCols
|
|
|
|
-- insert all object relations and fetch this insert dependent column values
|
|
objInsRes <- forM objRels $ insertObjRel role
|
|
|
|
-- prepare final insert columns
|
|
let objInsAffRows = sum $ map fst objInsRes
|
|
objRelDeterminedCols = concatMap snd objInsRes
|
|
objRelInsCols = mkPGColWithTypeAndVal allCols objRelDeterminedCols
|
|
addInsCols = mkPGColWithTypeAndVal allCols addCols
|
|
finalInsCols = map pgColToAnnGVal (cols <> objRelInsCols <> addInsCols)
|
|
|
|
-- prepare final returning columns
|
|
let arrDepCols = concatMap (map fst . riMapping . _riRelInfo) arrRels
|
|
arrDepColsWithInfo = getColInfos arrDepCols allCols
|
|
|
|
-- prepare insert query as with expression
|
|
insQ <- mkInsertQ vn onConflictM finalInsCols (map pgiName allCols) defVals role
|
|
|
|
let preArrRelInsAffRows = objInsAffRows + 1
|
|
insertWithArrRels = withArrRels preArrRelInsAffRows insQ
|
|
arrDepColsWithInfo
|
|
insertWithoutArrRels = return (preArrRelInsAffRows, insQ)
|
|
|
|
-- insert object
|
|
bool insertWithArrRels insertWithoutArrRels $ null arrDepColsWithInfo
|
|
where
|
|
AnnIns annObj onConflictM vn allCols defVals = singleObjIns
|
|
AnnInsObj cols objRels arrRels = annObj
|
|
|
|
withArrRels preAffRows insQ arrDepColsWithType = do
|
|
arrDepColsWithVal <-
|
|
insertAndRetCols tn insQ cannotInsArrRelErr arrDepColsWithType
|
|
|
|
arrInsARows <- forM arrRels $ insertArrRel role arrDepColsWithVal
|
|
|
|
let totalAffRows = preAffRows + sum arrInsARows
|
|
|
|
selQ <- mkSelQ tn allCols arrDepColsWithVal
|
|
return (totalAffRows, selQ)
|
|
|
|
cannotInsArrRelErr =
|
|
"cannot proceed to insert array relations since insert to table "
|
|
<> tn <<> " affects zero rows"
|
|
|
|
|
|
-- | insert multiple Objects in postgres
|
|
insertMultipleObjects
|
|
:: RoleName
|
|
-> QualifiedTable
|
|
-> MultiObjIns
|
|
-> [PGColWithValue] -- ^ additional fields
|
|
-> RR.MutFlds
|
|
-> T.Text -- ^ error path
|
|
-> Q.TxE QErr RespBody
|
|
insertMultipleObjects role tn multiObjIns addCols mutFlds errP =
|
|
bool withoutRelsInsert withRelsInsert anyRelsToInsert
|
|
where
|
|
AnnIns insObjs onConflictM vn tableColInfos defVals = multiObjIns
|
|
singleObjInserts = flip map insObjs $ \o ->
|
|
AnnIns o onConflictM vn tableColInfos defVals
|
|
insCols = map _aioColumns insObjs
|
|
allInsObjRels = concatMap _aioObjRels insObjs
|
|
allInsArrRels = concatMap _aioArrRels insObjs
|
|
anyRelsToInsert = not $ null allInsArrRels && null allInsObjRels
|
|
|
|
withErrPath = withPathK errP
|
|
|
|
-- insert all column rows at one go
|
|
withoutRelsInsert = withErrPath $ do
|
|
indexedForM_ insCols $ \insCol ->
|
|
validateInsert (map _1 insCol) [] $ map fst addCols
|
|
|
|
let addColsWithType = mkPGColWithTypeAndVal tableColInfos addCols
|
|
withAddCols = flip map insCols $ union addColsWithType
|
|
tableCols = map pgiName tableColInfos
|
|
|
|
(sqlRows, prepArgs) <- flip runStateT Seq.Empty $ do
|
|
rowsWithCol <- mapM (toSQLExps . map pgColToAnnGVal) withAddCols
|
|
return $ map (mkSQLRow defVals) rowsWithCol
|
|
|
|
let insQP1 = RI.InsertQueryP1 tn vn tableCols sqlRows onConflictM mutFlds
|
|
p1 = (insQP1, prepArgs)
|
|
bool (RI.nonAdminInsert p1) (RI.insertP2 p1) $ isAdmin role
|
|
|
|
-- insert each object with relations
|
|
withRelsInsert = withErrPath $ do
|
|
insResps <- indexedForM singleObjInserts $ \objIns ->
|
|
insertObj role tn objIns addCols
|
|
|
|
let affRows = sum $ map fst insResps
|
|
withExps = map snd insResps
|
|
retFlds = mapMaybe getRet mutFlds
|
|
rawResps <- forM withExps
|
|
$ \withExp -> execWithExp tn withExp retFlds
|
|
respVals :: [J.Object] <- mapM decodeFromBS rawResps
|
|
respTups <- forM mutFlds $ \(t, mutFld) -> do
|
|
jsonVal <- case mutFld of
|
|
RR.MCount -> return $ J.toJSON affRows
|
|
RR.MExp txt -> return $ J.toJSON txt
|
|
RR.MRet _ -> J.toJSON <$> mapM (fetchVal t) respVals
|
|
return (t, jsonVal)
|
|
return $ J.encode $ OMap.fromList respTups
|
|
|
|
getRet (t, r@(RR.MRet _)) = Just (t, r)
|
|
getRet _ = Nothing
|
|
|
|
prefixErrPath :: (MonadError QErr m) => Field -> m a -> m a
|
|
prefixErrPath fld =
|
|
withPathK "selectionSet" . fieldAsPath fld . withPathK "args"
|
|
|
|
convertInsert
|
|
:: RoleName
|
|
-> QualifiedTable -- table
|
|
-> Field -- the mutation field
|
|
-> Convert RespTx
|
|
convertInsert role tn fld = prefixErrPath fld $ do
|
|
mutFlds <- convertMutResp (_fType fld) $ _fSelSet fld
|
|
annVals <- withArg arguments "objects" asArray
|
|
-- if insert input objects is empty array then
|
|
-- do not perform insert and return mutation response
|
|
bool (withNonEmptyObjs annVals mutFlds) (buildEmptyMutResp mutFlds) $ null annVals
|
|
where
|
|
withNonEmptyObjs annVals mutFlds = do
|
|
InsCtx vn tableCols defValMap relInfoMap updPerm <- getInsCtx tn
|
|
annObjs <- mapM asObject annVals
|
|
annInsObjs <- forM annObjs $ mkAnnInsObj relInfoMap
|
|
conflictClauseM <- forM onConflictM $ parseOnConflict tn updPerm
|
|
let multiObjIns = AnnIns annInsObjs conflictClauseM vn tableCols defValMap
|
|
return $ prefixErrPath fld $ insertMultipleObjects role tn
|
|
multiObjIns [] mutFlds "objects"
|
|
|
|
arguments = _fArguments fld
|
|
onConflictM = Map.lookup "on_conflict" arguments
|
|
|
|
-- helper functions
|
|
getInsCtx
|
|
:: (MonadError QErr m, MonadReader r m, Has InsCtxMap r)
|
|
=> QualifiedTable -> m InsCtx
|
|
getInsCtx tn = do
|
|
ctxMap <- asks getter
|
|
insCtx <- onNothing (Map.lookup tn ctxMap) $
|
|
throw500 $ "table " <> tn <<> " not found"
|
|
let defValMap = S.mkColDefValMap $ map pgiName $ icColumns insCtx
|
|
setCols = icSet insCtx
|
|
return $ insCtx {icSet = Map.union setCols defValMap}
|
|
|
|
fetchVal :: (MonadError QErr m)
|
|
=> T.Text -> Map.HashMap T.Text a -> m a
|
|
fetchVal t m = onNothing (Map.lookup t m) $ throw500 $
|
|
"key " <> t <> " not found in hashmap"
|
|
|
|
mergeListsWith
|
|
:: [a] -> [b] -> (a -> b -> Bool) -> (a -> b -> c) -> [c]
|
|
mergeListsWith _ [] _ _ = []
|
|
mergeListsWith [] _ _ _ = []
|
|
mergeListsWith (x:xs) l b f = case find (b x) l of
|
|
Nothing -> mergeListsWith xs l b f
|
|
Just y -> f x y : mergeListsWith xs l b f
|
|
|
|
mkPGColWithTypeAndVal :: [PGColInfo] -> [PGColWithValue]
|
|
-> [(PGCol, PGColType, PGColValue)]
|
|
mkPGColWithTypeAndVal pgColInfos pgColWithVal =
|
|
mergeListsWith pgColInfos pgColWithVal
|
|
(\ci (c, _) -> pgiName ci == c)
|
|
(\ci (c, v) -> (c, pgiType ci, v))
|
|
|
|
pgColToAnnGVal
|
|
:: (PGCol, PGColType, PGColValue)
|
|
-> (PGCol, AnnGValue)
|
|
pgColToAnnGVal (col, colTy, colVal) = (col, pgColValToAnnGVal colTy colVal)
|