mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
remove hdb_views for inserts (#3598)
* WIP: Remove hdb_views for inserts
* Show failing row in check constraint error
* Revert "Show failing row in check constraint error"
This reverts commit dd2cac29d0
.
* Use the better query plan
* Simplify things
* fix cli test
* Update downgrading.rst
* remove 1.1 asset for cli
This commit is contained in:
parent
f6a43fe3ba
commit
9ed8f717a7
@ -103,6 +103,12 @@ You can downgrade the catalogue from a particular version to its previous versio
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
From 30 to 29
|
||||
"""""""""""""
|
||||
.. code-block:: plpgsql
|
||||
|
||||
DROP FUNCTION hdb_catalog.check_violation();
|
||||
|
||||
From 27 to 26
|
||||
"""""""""""""
|
||||
|
@ -280,7 +280,6 @@ library
|
||||
, Hasura.RQL.DDL.Relationship
|
||||
, Hasura.RQL.DDL.Deps
|
||||
, Hasura.RQL.DDL.Permission.Internal
|
||||
, Hasura.RQL.DDL.Permission.Triggers
|
||||
, Hasura.RQL.DDL.Permission
|
||||
, Hasura.RQL.DDL.Relationship.Rename
|
||||
, Hasura.RQL.DDL.Relationship.Types
|
||||
|
@ -29,7 +29,10 @@ 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 (convPartialSQLExp, dmlTxErrorHandler,
|
||||
import Hasura.RQL.DML.Insert (insertCheckExpr)
|
||||
import Hasura.RQL.DML.Internal (convPartialSQLExp,
|
||||
convAnnBoolExpPartialSQL,
|
||||
dmlTxErrorHandler,
|
||||
sessVarFromCurrentSetting)
|
||||
import Hasura.RQL.DML.Mutation
|
||||
import Hasura.RQL.GBoolExp (toSQLBoolExp)
|
||||
@ -47,7 +50,7 @@ data AnnIns a
|
||||
= AnnIns
|
||||
{ _aiInsObj :: !a
|
||||
, _aiConflictClause :: !(Maybe RI.ConflictClauseP1)
|
||||
, _aiView :: !QualifiedTable
|
||||
, _aiCheckCond :: AnnBoolExpPartialSQL
|
||||
, _aiTableCols :: ![PGColumnInfo]
|
||||
, _aiDefVals :: !(Map.HashMap PGCol S.SQLExp)
|
||||
} deriving (Show, Eq, Functor, Foldable, Traversable)
|
||||
@ -131,7 +134,7 @@ traverseInsObj rim allColMap (gName, annVal) defVal@(AnnInsObj cols objRels arrR
|
||||
throw500 $ "relation " <> relName <<> " not found"
|
||||
|
||||
let rTable = riRTable relInfo
|
||||
InsCtx rtView rtColMap rtDefVals rtRelInfoMap rtUpdPerm <- getInsCtx rTable
|
||||
InsCtx rtColMap checkCond rtDefVals rtRelInfoMap rtUpdPerm <- getInsCtx rTable
|
||||
let rtCols = Map.elems rtColMap
|
||||
rtDefValsRes <- mapM (convPartialSQLExp sessVarFromCurrentSetting) rtDefVals
|
||||
|
||||
@ -140,7 +143,7 @@ traverseInsObj rim allColMap (gName, annVal) defVal@(AnnInsObj cols objRels arrR
|
||||
dataObj <- asObject dataVal
|
||||
annDataObj <- mkAnnInsObj rtRelInfoMap rtColMap dataObj
|
||||
ccM <- forM onConflictM $ parseOnConflict rTable rtUpdPerm rtColMap
|
||||
let singleObjIns = AnnIns annDataObj ccM rtView rtCols rtDefValsRes
|
||||
let singleObjIns = AnnIns annDataObj ccM checkCond rtCols rtDefValsRes
|
||||
objRelIns = RelIns singleObjIns relInfo
|
||||
return (AnnInsObj cols (objRelIns:objRels) arrRels)
|
||||
|
||||
@ -151,8 +154,7 @@ traverseInsObj rim allColMap (gName, annVal) defVal@(AnnInsObj cols objRels arrR
|
||||
dataObj <- asObject arrDataVal
|
||||
mkAnnInsObj rtRelInfoMap rtColMap dataObj
|
||||
ccM <- forM onConflictM $ parseOnConflict rTable rtUpdPerm rtColMap
|
||||
let multiObjIns = AnnIns annDataObjs ccM rtView
|
||||
rtCols rtDefValsRes
|
||||
let multiObjIns = AnnIns annDataObjs ccM checkCond rtCols rtDefValsRes
|
||||
arrRelIns = RelIns multiObjIns relInfo
|
||||
return (AnnInsObj cols objRels (arrRelIns:arrRels))
|
||||
-- if array relation insert input data has empty objects
|
||||
@ -224,20 +226,26 @@ mkInsertQ
|
||||
-> [PGColWithValue]
|
||||
-> Map.HashMap PGCol S.SQLExp
|
||||
-> RoleName
|
||||
-> m (CTEExp, Maybe RI.ConflictCtx)
|
||||
mkInsertQ vn onConflictM insCols defVals role = do
|
||||
-> AnnBoolExpSQL
|
||||
-> m CTEExp
|
||||
mkInsertQ tn onConflictM insCols defVals role checkCond = do
|
||||
(givenCols, args) <- flip runStateT Seq.Empty $ toSQLExps insCols
|
||||
let sqlConflict = RI.toSQLConflict <$> onConflictM
|
||||
sqlExps = mkSQLRow defVals givenCols
|
||||
valueExp = S.ValuesExp [S.TupleExp sqlExps]
|
||||
tableCols = Map.keys defVals
|
||||
sqlInsert =
|
||||
S.SQLInsert vn tableCols valueExp sqlConflict $ Just S.returningStar
|
||||
adminIns = return (CTEExp (S.CTEInsert sqlInsert) args, Nothing)
|
||||
S.SQLInsert tn tableCols valueExp sqlConflict
|
||||
. Just
|
||||
$ S.RetExp
|
||||
[ S.selectStar
|
||||
, insertCheckExpr (toSQLBoolExp (S.QualTable tn) checkCond)
|
||||
]
|
||||
|
||||
adminIns = return (CTEExp (S.CTEInsert sqlInsert) args)
|
||||
nonAdminInsert = do
|
||||
ccM <- mapM RI.extractConflictCtx onConflictM
|
||||
let cteIns = S.CTEInsert sqlInsert{S.siConflict=Nothing}
|
||||
return (CTEExp cteIns args, ccM)
|
||||
let cteIns = S.CTEInsert sqlInsert
|
||||
return (CTEExp cteIns args)
|
||||
|
||||
bool nonAdminInsert adminIns $ isAdmin role
|
||||
|
||||
@ -400,10 +408,11 @@ insertObj strfyNum role tn singleObjIns addCols = do
|
||||
finalInsCols = cols <> objRelDeterminedCols <> addCols
|
||||
|
||||
-- prepare insert query as with expression
|
||||
(CTEExp cte insPArgs, ccM) <-
|
||||
mkInsertQ vn onConflictM finalInsCols defVals role
|
||||
checkExpr <- convAnnBoolExpPartialSQL sessVarFromCurrentSetting checkCond
|
||||
|
||||
CTEExp cte insPArgs <-
|
||||
mkInsertQ tn onConflictM finalInsCols defVals role checkExpr
|
||||
|
||||
RI.setConflictCtx ccM
|
||||
MutateResp affRows colVals <- mutateAndFetchCols tn allCols (cte, insPArgs) strfyNum
|
||||
colValM <- asSingleObject colVals
|
||||
cteExp <- mkSelCTE tn allCols colValM
|
||||
@ -413,7 +422,7 @@ insertObj strfyNum role tn singleObjIns addCols = do
|
||||
|
||||
return (totAffRows, cteExp)
|
||||
where
|
||||
AnnIns annObj onConflictM vn allCols defVals = singleObjIns
|
||||
AnnIns annObj onConflictM checkCond allCols defVals = singleObjIns
|
||||
AnnInsObj cols objRels arrRels = annObj
|
||||
|
||||
arrRelDepCols = flip getColInfos allCols $
|
||||
@ -445,7 +454,7 @@ insertMultipleObjects
|
||||
insertMultipleObjects strfyNum role tn multiObjIns addCols mutFlds errP =
|
||||
bool withoutRelsInsert withRelsInsert anyRelsToInsert
|
||||
where
|
||||
AnnIns insObjs onConflictM vn tableColInfos defVals = multiObjIns
|
||||
AnnIns insObjs onConflictM checkCond tableColInfos defVals = multiObjIns
|
||||
singleObjInserts = multiToSingles multiObjIns
|
||||
insCols = map _aioColumns insObjs
|
||||
allInsObjRels = concatMap _aioObjRels insObjs
|
||||
@ -465,10 +474,13 @@ insertMultipleObjects strfyNum role tn multiObjIns addCols mutFlds errP =
|
||||
(sqlRows, prepArgs) <- flip runStateT Seq.Empty $ do
|
||||
rowsWithCol <- mapM toSQLExps withAddCols
|
||||
return $ map (mkSQLRow defVals) rowsWithCol
|
||||
|
||||
let insQP1 = RI.InsertQueryP1 tn vn tableCols sqlRows onConflictM mutFlds tableColInfos
|
||||
|
||||
checkExpr <- convAnnBoolExpPartialSQL sessVarFromCurrentSetting checkCond
|
||||
|
||||
let insQP1 = RI.InsertQueryP1 tn tableCols sqlRows onConflictM
|
||||
(Just checkExpr) mutFlds tableColInfos
|
||||
p1 = (insQP1, prepArgs)
|
||||
bool (RI.nonAdminInsert strfyNum p1) (RI.insertP2 strfyNum p1) $ isAdmin role
|
||||
RI.insertP2 strfyNum p1
|
||||
|
||||
-- insert each object with relations
|
||||
withRelsInsert = withErrPath $ do
|
||||
@ -513,14 +525,14 @@ convertInsert role tn fld = prefixErrPath fld $ do
|
||||
(withEmptyObjs mutFldsRes) $ null annVals
|
||||
where
|
||||
withNonEmptyObjs annVals mutFlds = do
|
||||
InsCtx vn tableColMap defValMap relInfoMap updPerm <- getInsCtx tn
|
||||
InsCtx tableColMap checkCond defValMap relInfoMap updPerm <- getInsCtx tn
|
||||
annObjs <- mapM asObject annVals
|
||||
annInsObjs <- forM annObjs $ mkAnnInsObj relInfoMap tableColMap
|
||||
conflictClauseM <- forM onConflictM $ parseOnConflict tn updPerm tableColMap
|
||||
defValMapRes <- mapM (convPartialSQLExp sessVarFromCurrentSetting)
|
||||
defValMap
|
||||
let multiObjIns = AnnIns annInsObjs conflictClauseM
|
||||
vn tableCols defValMapRes
|
||||
let multiObjIns = AnnIns annInsObjs conflictClauseM checkCond
|
||||
tableCols defValMapRes
|
||||
tableCols = Map.elems tableColMap
|
||||
strfyNum <- stringifyNum <$> asks getter
|
||||
return $ prefixErrPath fld $ insertMultipleObjects strfyNum role tn
|
||||
|
@ -179,8 +179,8 @@ data UpdPermForIns
|
||||
|
||||
data InsCtx
|
||||
= InsCtx
|
||||
{ icView :: !QualifiedTable
|
||||
, icAllCols :: !PGColGNameMap
|
||||
{ icAllCols :: !PGColGNameMap
|
||||
, icCheck :: !AnnBoolExpPartialSQL
|
||||
, icSet :: !PreSetColsPartial
|
||||
, icRelations :: !RelationInfoMap
|
||||
, icUpdPerm :: !(Maybe UpdPermForIns)
|
||||
|
@ -509,13 +509,13 @@ mkInsCtx role tableCache fields insPermInfo updPermM = do
|
||||
isInsertable insPermM viewInfoM && isValidRel relName remoteTable
|
||||
|
||||
let relInfoMap = Map.fromList $ catMaybes relTupsM
|
||||
return $ InsCtx iView gNamePGColMap setCols relInfoMap updPermForIns
|
||||
return $ InsCtx gNamePGColMap checkCond setCols relInfoMap updPermForIns
|
||||
where
|
||||
gNamePGColMap = mkPGColGNameMap allCols
|
||||
allCols = getCols fields
|
||||
rels = getValidRels fields
|
||||
iView = ipiView insPermInfo
|
||||
setCols = ipiSet insPermInfo
|
||||
checkCond = ipiCheck insPermInfo
|
||||
updPermForIns = mkUpdPermForIns <$> updPermM
|
||||
mkUpdPermForIns upi = UpdPermForIns (toList $ upiCols upi)
|
||||
(upiFilter upi) (upiSet upi)
|
||||
@ -525,11 +525,10 @@ mkInsCtx role tableCache fields insPermInfo updPermM = do
|
||||
|
||||
mkAdminInsCtx
|
||||
:: MonadError QErr m
|
||||
=> QualifiedTable
|
||||
-> TableCache
|
||||
=> TableCache
|
||||
-> FieldInfoMap FieldInfo
|
||||
-> m InsCtx
|
||||
mkAdminInsCtx tn tc fields = do
|
||||
mkAdminInsCtx tc fields = do
|
||||
relTupsM <- forM rels $ \relInfo -> do
|
||||
let remoteTable = riRTable relInfo
|
||||
relName = riName relInfo
|
||||
@ -541,7 +540,7 @@ mkAdminInsCtx tn tc fields = do
|
||||
let relInfoMap = Map.fromList $ catMaybes relTupsM
|
||||
updPerm = UpdPermForIns updCols noFilter Map.empty
|
||||
|
||||
return $ InsCtx tn colGNameMap Map.empty relInfoMap (Just updPerm)
|
||||
return $ InsCtx colGNameMap noFilter Map.empty relInfoMap (Just updPerm)
|
||||
where
|
||||
allCols = getCols fields
|
||||
colGNameMap = mkPGColGNameMap allCols
|
||||
@ -667,7 +666,7 @@ mkGCtxMapTable tableCache funcCache tabInfo = do
|
||||
m <- flip Map.traverseWithKey rolePerms $
|
||||
mkGCtxRole tableCache tn descM fields primaryKey validConstraints
|
||||
tabFuncs viewInfo customConfig
|
||||
adminInsCtx <- mkAdminInsCtx tn tableCache fields
|
||||
adminInsCtx <- mkAdminInsCtx tableCache fields
|
||||
adminSelFlds <- mkAdminSelFlds fields tableCache
|
||||
let adminCtx = mkGCtxRole' tn descM (Just (cols, icRelations adminInsCtx))
|
||||
(Just (True, adminSelFlds)) (Just cols) (Just ())
|
||||
|
@ -7,37 +7,26 @@ module Hasura.RQL.DDL.Permission
|
||||
, InsPerm(..)
|
||||
, InsPermDef
|
||||
, CreateInsPerm
|
||||
, clearInsInfra
|
||||
, buildInsInfra
|
||||
, buildInsPermInfo
|
||||
, DropInsPerm
|
||||
, dropInsPermP2
|
||||
|
||||
, SelPerm(..)
|
||||
, SelPermDef
|
||||
, CreateSelPerm
|
||||
, buildSelPermInfo
|
||||
, DropSelPerm
|
||||
, dropSelPermP2
|
||||
|
||||
, UpdPerm(..)
|
||||
, UpdPermDef
|
||||
, CreateUpdPerm
|
||||
, buildUpdPermInfo
|
||||
, DropUpdPerm
|
||||
, dropUpdPermP2
|
||||
|
||||
, DelPerm(..)
|
||||
, DelPermDef
|
||||
, CreateDelPerm
|
||||
, buildDelPermInfo
|
||||
, DropDelPerm
|
||||
, dropDelPermP2
|
||||
|
||||
, IsPerm(..)
|
||||
, addPermP2
|
||||
|
||||
, dropView
|
||||
, DropPerm
|
||||
, runDropPerm
|
||||
|
||||
@ -51,21 +40,17 @@ import Hasura.EncJSON
|
||||
import Hasura.Incremental (Cacheable)
|
||||
import Hasura.Prelude
|
||||
import Hasura.RQL.DDL.Permission.Internal
|
||||
import Hasura.RQL.DDL.Permission.Triggers
|
||||
import Hasura.RQL.DML.Internal hiding (askPermInfo)
|
||||
import Hasura.RQL.GBoolExp
|
||||
import Hasura.RQL.Types
|
||||
import Hasura.SQL.Types
|
||||
|
||||
import qualified Database.PG.Query as Q
|
||||
import qualified Hasura.SQL.DML as S
|
||||
|
||||
import Data.Aeson
|
||||
import Data.Aeson.Casing
|
||||
import Data.Aeson.TH
|
||||
import Language.Haskell.TH.Syntax (Lift)
|
||||
|
||||
import qualified Crypto.Hash as CH
|
||||
import qualified Data.HashMap.Strict as HM
|
||||
import qualified Data.HashSet as HS
|
||||
import qualified Data.Text as T
|
||||
@ -83,31 +68,6 @@ $(deriveJSON (aesonDrop 2 snakeCase){omitNothingFields=True} ''InsPerm)
|
||||
type InsPermDef = PermDef InsPerm
|
||||
type CreateInsPerm = CreatePerm InsPerm
|
||||
|
||||
buildViewName :: QualifiedTable -> RoleName -> PermType -> QualifiedTable
|
||||
buildViewName qt rn pt = QualifiedObject hdbViewsSchema tableName
|
||||
where
|
||||
-- Generate a unique hash for view name from role name, permission type and qualified table.
|
||||
-- See Note [Postgres identifier length limitations].
|
||||
-- Black2b_224 generates 56 character hash. See Note [Blake2b faster than SHA-256].
|
||||
-- Refer https://github.com/hasura/graphql-engine/issues/3444.
|
||||
tableName = TableName $ T.pack $ show hash
|
||||
hash :: CH.Digest CH.Blake2b_224 =
|
||||
CH.hash $ txtToBs $ roleNameToTxt rn <> "__" <> T.pack (show pt) <> "__" <> qualObjectToText qt
|
||||
|
||||
buildView :: QualifiedTable -> QualifiedTable -> Q.Query
|
||||
buildView tn vn =
|
||||
Q.fromBuilder $ mconcat
|
||||
[ "CREATE VIEW " <> toSQL vn
|
||||
, " AS SELECT * FROM " <> toSQL tn
|
||||
]
|
||||
|
||||
dropView :: QualifiedTable -> Q.Tx ()
|
||||
dropView vn =
|
||||
Q.unitQ dropViewS () False
|
||||
where
|
||||
dropViewS = Q.fromBuilder $
|
||||
"DROP VIEW IF EXISTS " <> toSQL vn
|
||||
|
||||
procSetObj
|
||||
:: (QErrM m)
|
||||
=> QualifiedTable
|
||||
@ -136,7 +96,7 @@ buildInsPermInfo
|
||||
-> FieldInfoMap FieldInfo
|
||||
-> PermDef InsPerm
|
||||
-> m (WithDeps InsPermInfo)
|
||||
buildInsPermInfo tn fieldInfoMap (PermDef rn (InsPerm chk set mCols) _) =
|
||||
buildInsPermInfo tn fieldInfoMap (PermDef _rn (InsPerm chk set mCols) _) =
|
||||
withPathK "permission" $ do
|
||||
(be, beDeps) <- withPathK "check" $ procBoolExp tn fieldInfoMap chk
|
||||
(setColsSQL, setHdrs, setColDeps) <- procSetObj tn fieldInfoMap set
|
||||
@ -147,56 +107,19 @@ buildInsPermInfo tn fieldInfoMap (PermDef rn (InsPerm chk set mCols) _) =
|
||||
insColDeps = map (mkColDep DRUntyped tn) insCols
|
||||
deps = mkParentDep tn : beDeps ++ setColDeps ++ insColDeps
|
||||
insColsWithoutPresets = insCols \\ HM.keys setColsSQL
|
||||
return (InsPermInfo (HS.fromList insColsWithoutPresets) vn be setColsSQL reqHdrs, deps)
|
||||
return (InsPermInfo (HS.fromList insColsWithoutPresets) be setColsSQL reqHdrs, deps)
|
||||
where
|
||||
vn = buildViewName tn rn PTInsert
|
||||
allCols = map pgiColumn $ getCols fieldInfoMap
|
||||
insCols = fromMaybe allCols $ convColSpec fieldInfoMap <$> mCols
|
||||
|
||||
buildInsInfra :: QualifiedTable -> InsPermInfo -> Q.TxE QErr ()
|
||||
buildInsInfra tn (InsPermInfo _ vn be _ _) = do
|
||||
resolvedBoolExp <- {-# SCC "buildInsInfra/convAnnBoolExpPartialSQL" #-} convAnnBoolExpPartialSQL sessVarFromCurrentSetting be
|
||||
let trigFnQ = {-# SCC "buildInsInfra/buildInsTrigFn" #-} buildInsTrigFn vn tn $ toSQLBoolExp (S.QualVar "NEW") resolvedBoolExp
|
||||
{-# SCC "buildInsInfra/execute" #-} Q.catchE defaultTxErrorHandler $ do
|
||||
-- Create the view
|
||||
dropView vn
|
||||
Q.unitQ (buildView tn vn) () False
|
||||
-- Inject defaults on the view
|
||||
Q.discardQ (injectDefaults vn tn) () False
|
||||
-- Construct a trigger function
|
||||
Q.unitQ trigFnQ () False
|
||||
-- Add trigger for check expression
|
||||
Q.unitQ (buildInsTrig vn) () False
|
||||
|
||||
clearInsInfra :: QualifiedTable -> Q.TxE QErr ()
|
||||
clearInsInfra vn =
|
||||
Q.catchE defaultTxErrorHandler $ do
|
||||
dropView vn
|
||||
Q.unitQ (dropInsTrigFn vn) () False
|
||||
|
||||
type DropInsPerm = DropPerm InsPerm
|
||||
|
||||
dropInsPermP2 :: (MonadTx m) => DropInsPerm -> QualifiedTable -> m ()
|
||||
dropInsPermP2 = dropPermP2
|
||||
|
||||
type instance PermInfo InsPerm = InsPermInfo
|
||||
|
||||
instance IsPerm InsPerm where
|
||||
|
||||
type DropPermP1Res InsPerm = QualifiedTable
|
||||
|
||||
permAccessor = PAInsert
|
||||
|
||||
buildPermInfo = buildInsPermInfo
|
||||
|
||||
addPermP2Setup qt _ = liftTx . buildInsInfra qt
|
||||
|
||||
buildDropPermP1Res dp =
|
||||
ipiView <$> dropPermP1 dp
|
||||
|
||||
dropPermP2Setup _ vn =
|
||||
liftTx $ clearInsInfra vn
|
||||
|
||||
-- Select constraint
|
||||
data SelPerm
|
||||
= SelPerm
|
||||
@ -263,29 +186,16 @@ buildSelPermInfo tn fieldInfoMap sp = withPathK "permission" $ do
|
||||
|
||||
type SelPermDef = PermDef SelPerm
|
||||
type CreateSelPerm = CreatePerm SelPerm
|
||||
type DropSelPerm = DropPerm SelPerm
|
||||
|
||||
type instance PermInfo SelPerm = SelPermInfo
|
||||
|
||||
dropSelPermP2 :: (MonadTx m) => DropSelPerm -> m ()
|
||||
dropSelPermP2 dp = dropPermP2 dp ()
|
||||
|
||||
instance IsPerm SelPerm where
|
||||
|
||||
type DropPermP1Res SelPerm = ()
|
||||
|
||||
permAccessor = PASelect
|
||||
|
||||
buildPermInfo tn fieldInfoMap (PermDef _ a _) =
|
||||
buildSelPermInfo tn fieldInfoMap a
|
||||
|
||||
buildDropPermP1Res =
|
||||
void . dropPermP1
|
||||
|
||||
addPermP2Setup _ _ _ = return ()
|
||||
|
||||
dropPermP2Setup _ _ = return ()
|
||||
|
||||
-- Update constraint
|
||||
data UpdPerm
|
||||
= UpdPerm
|
||||
@ -330,27 +240,13 @@ buildUpdPermInfo tn fieldInfoMap (UpdPerm colSpec set fltr) = do
|
||||
|
||||
type instance PermInfo UpdPerm = UpdPermInfo
|
||||
|
||||
type DropUpdPerm = DropPerm UpdPerm
|
||||
|
||||
dropUpdPermP2 :: (MonadTx m) => DropUpdPerm -> m ()
|
||||
dropUpdPermP2 dp = dropPermP2 dp ()
|
||||
|
||||
instance IsPerm UpdPerm where
|
||||
|
||||
type DropPermP1Res UpdPerm = ()
|
||||
|
||||
permAccessor = PAUpdate
|
||||
|
||||
buildPermInfo tn fieldInfoMap (PermDef _ a _) =
|
||||
buildUpdPermInfo tn fieldInfoMap a
|
||||
|
||||
addPermP2Setup _ _ _ = return ()
|
||||
|
||||
buildDropPermP1Res =
|
||||
void . dropPermP1
|
||||
|
||||
dropPermP2Setup _ _ = return ()
|
||||
|
||||
-- Delete permission
|
||||
data DelPerm
|
||||
= DelPerm { dcFilter :: !BoolExp }
|
||||
@ -374,29 +270,15 @@ buildDelPermInfo tn fieldInfoMap (DelPerm fltr) = do
|
||||
depHeaders = getDependentHeaders fltr
|
||||
return (DelPermInfo tn be depHeaders, deps)
|
||||
|
||||
type DropDelPerm = DropPerm DelPerm
|
||||
|
||||
dropDelPermP2 :: (MonadTx m) => DropDelPerm -> m ()
|
||||
dropDelPermP2 dp = dropPermP2 dp ()
|
||||
|
||||
type instance PermInfo DelPerm = DelPermInfo
|
||||
|
||||
instance IsPerm DelPerm where
|
||||
|
||||
type DropPermP1Res DelPerm = ()
|
||||
|
||||
permAccessor = PADelete
|
||||
|
||||
buildPermInfo tn fieldInfoMap (PermDef _ a _) =
|
||||
buildDelPermInfo tn fieldInfoMap a
|
||||
|
||||
addPermP2Setup _ _ _ = return ()
|
||||
|
||||
buildDropPermP1Res =
|
||||
void . dropPermP1
|
||||
|
||||
dropPermP2Setup _ _ = return ()
|
||||
|
||||
data SetPermComment
|
||||
= SetPermComment
|
||||
{ apTable :: !QualifiedTable
|
||||
@ -443,13 +325,13 @@ setPermCommentTx (SetPermComment (QualifiedObject sn tn) rn pt comment) =
|
||||
AND perm_type = $5
|
||||
|] (comment, sn, tn, rn, permTypeToCode pt) True
|
||||
|
||||
purgePerm :: (MonadTx m) => QualifiedTable -> RoleName -> PermType -> m ()
|
||||
purgePerm qt rn pt =
|
||||
case pt of
|
||||
PTInsert -> dropInsPermP2 dp $ buildViewName qt rn PTInsert
|
||||
PTSelect -> dropSelPermP2 dp
|
||||
PTUpdate -> dropUpdPermP2 dp
|
||||
PTDelete -> dropDelPermP2 dp
|
||||
purgePerm :: MonadTx m => QualifiedTable -> RoleName -> PermType -> m ()
|
||||
purgePerm qt rn pt =
|
||||
case pt of
|
||||
PTInsert -> dropPermP2 @InsPerm dp
|
||||
PTSelect -> dropPermP2 @SelPerm dp
|
||||
PTUpdate -> dropPermP2 @UpdPerm dp
|
||||
PTDelete -> dropPermP2 @DelPerm dp
|
||||
where
|
||||
dp :: DropPerm a
|
||||
dp = DropPerm qt rn
|
||||
|
@ -239,8 +239,6 @@ type family PermInfo a = r | r -> a
|
||||
|
||||
class (ToJSON a) => IsPerm a where
|
||||
|
||||
type DropPermP1Res a
|
||||
|
||||
permAccessor
|
||||
:: PermAccessor (PermInfo a)
|
||||
|
||||
@ -251,16 +249,6 @@ class (ToJSON a) => IsPerm a where
|
||||
-> PermDef a
|
||||
-> m (WithDeps (PermInfo a))
|
||||
|
||||
addPermP2Setup
|
||||
:: (MonadTx m) => QualifiedTable -> PermDef a -> PermInfo a -> m ()
|
||||
|
||||
buildDropPermP1Res
|
||||
:: (QErrM m, CacheRM m, UserInfoM m)
|
||||
=> DropPerm a
|
||||
-> m (DropPermP1Res a)
|
||||
|
||||
dropPermP2Setup :: (MonadTx m) => DropPerm a -> DropPermP1Res a -> m ()
|
||||
|
||||
getPermAcc1
|
||||
:: PermDef a -> PermAccessor (PermInfo a)
|
||||
getPermAcc1 _ = permAccessor
|
||||
@ -268,7 +256,7 @@ class (ToJSON a) => IsPerm a where
|
||||
getPermAcc2
|
||||
:: DropPerm a -> PermAccessor (PermInfo a)
|
||||
getPermAcc2 _ = permAccessor
|
||||
|
||||
|
||||
addPermP2 :: (IsPerm a, MonadTx m, HasSystemDefined m) => QualifiedTable -> PermDef a -> m ()
|
||||
addPermP2 tn pd = do
|
||||
let pt = permAccToType $ getPermAcc1 pd
|
||||
@ -291,9 +279,8 @@ dropPermP1 dp@(DropPerm tn rn) = do
|
||||
tabInfo <- askTabInfo tn
|
||||
askPermInfo tabInfo rn $ getPermAcc2 dp
|
||||
|
||||
dropPermP2 :: (MonadTx m, IsPerm a) => DropPerm a -> DropPermP1Res a -> m ()
|
||||
dropPermP2 dp@(DropPerm tn rn) p1Res = do
|
||||
dropPermP2Setup dp p1Res
|
||||
dropPermP2 :: forall a m. (MonadTx m, IsPerm a) => DropPerm a -> m ()
|
||||
dropPermP2 dp@(DropPerm tn rn) = do
|
||||
liftTx $ dropPermFromCatalog tn rn pt
|
||||
where
|
||||
pa = getPermAcc2 dp
|
||||
@ -303,7 +290,7 @@ runDropPerm
|
||||
:: (IsPerm a, UserInfoM m, CacheRWM m, MonadTx m)
|
||||
=> DropPerm a -> m EncJSON
|
||||
runDropPerm defn = do
|
||||
permInfo <- buildDropPermP1Res defn
|
||||
dropPermP2 defn permInfo
|
||||
dropPermP1 defn
|
||||
dropPermP2 defn
|
||||
withNewInconsistentObjsCheck buildSchemaCache
|
||||
return successMsg
|
||||
|
@ -1,34 +0,0 @@
|
||||
module Hasura.RQL.DDL.Permission.Triggers
|
||||
( buildInsTrig
|
||||
, dropInsTrigFn
|
||||
, buildInsTrigFn
|
||||
) where
|
||||
|
||||
import Hasura.Prelude
|
||||
import Hasura.SQL.Types
|
||||
|
||||
import qualified Database.PG.Query as Q
|
||||
import qualified Hasura.SQL.DML as S
|
||||
|
||||
import qualified Data.Text.Lazy as TL
|
||||
import qualified Text.Shakespeare.Text as ST
|
||||
|
||||
buildInsTrig :: QualifiedTable -> Q.Query
|
||||
buildInsTrig qt@(QualifiedObject _ tn) =
|
||||
Q.fromBuilder $ mconcat
|
||||
[ "CREATE TRIGGER " <> toSQL tn
|
||||
, " INSTEAD OF INSERT ON " <> toSQL qt
|
||||
, " FOR EACH ROW EXECUTE PROCEDURE "
|
||||
, toSQL qt <> "();"
|
||||
]
|
||||
|
||||
dropInsTrigFn :: QualifiedTable -> Q.Query
|
||||
dropInsTrigFn fn =
|
||||
Q.fromBuilder $ "DROP FUNCTION " <> toSQL fn <> "()"
|
||||
|
||||
buildInsTrigFn :: QualifiedTable -> QualifiedTable -> S.BoolExp -> Q.Query
|
||||
buildInsTrigFn fn tn be = Q.fromText . TL.toStrict $
|
||||
let functionName = toSQLTxt fn
|
||||
tableName = toSQLTxt tn
|
||||
checkExpression = toSQLTxt be
|
||||
in $(ST.stextFile "src-rsr/insert_trigger.sql.shakespeare")
|
@ -25,7 +25,7 @@ import Hasura.SQL.Types
|
||||
|
||||
buildTablePermissions
|
||||
:: ( ArrowChoice arr, Inc.ArrowDistribute arr, Inc.ArrowCache m arr
|
||||
, ArrowWriter (Seq CollectedInfo) arr, MonadTx m, MonadReader BuildReason m )
|
||||
, ArrowWriter (Seq CollectedInfo) arr, MonadTx m )
|
||||
=> ( Inc.Dependency TableCoreCache
|
||||
, QualifiedTable
|
||||
, FieldInfoMap FieldInfo
|
||||
@ -80,8 +80,9 @@ withPermission f = proc (e, (permission, s)) -> do
|
||||
|
||||
buildPermission
|
||||
:: ( ArrowChoice arr, Inc.ArrowCache m arr
|
||||
, ArrowWriter (Seq CollectedInfo) arr, MonadTx m, MonadReader BuildReason m
|
||||
, Inc.Cacheable a, IsPerm a, FromJSON a, Inc.Cacheable (PermInfo a) )
|
||||
, ArrowWriter (Seq CollectedInfo) arr
|
||||
, MonadTx m, IsPerm a, FromJSON a
|
||||
)
|
||||
=> ( Inc.Dependency TableCoreCache
|
||||
, QualifiedTable
|
||||
, FieldInfoMap FieldInfo
|
||||
@ -98,16 +99,6 @@ buildPermission = Inc.cache proc (tableCache, tableName, tableFields, permission
|
||||
(info, dependencies) <- liftEitherA <<< Inc.bindDepend -< runExceptT $
|
||||
runTableCoreCacheRT (buildPermInfo tableName tableFields permDef) tableCache
|
||||
tellA -< Seq.fromList dependencies
|
||||
rebuildViewsIfNeeded -< (tableName, permDef, info)
|
||||
returnA -< info)
|
||||
|) permission) |)
|
||||
>-> (\info -> join info >- returnA)
|
||||
|
||||
rebuildViewsIfNeeded
|
||||
:: ( Inc.ArrowCache m arr, MonadTx m, MonadReader BuildReason m
|
||||
, Inc.Cacheable a, IsPerm a, Inc.Cacheable (PermInfo a) )
|
||||
=> (QualifiedTable, PermDef a, PermInfo a) `arr` ()
|
||||
rebuildViewsIfNeeded = Inc.cache $ arrM \(tableName, permDef, info) -> do
|
||||
buildReason <- ask
|
||||
when (buildReason == CatalogUpdate) $
|
||||
addPermP2Setup tableName permDef info
|
||||
|
@ -56,7 +56,7 @@ mkSQLCount (CountQueryP1 tn (permFltr, mWc) mDistCols) =
|
||||
, S.selExtr = extrs
|
||||
}
|
||||
Nothing -> S.mkSelect
|
||||
{ S.selExtr = [S.Extractor S.SEStar Nothing] }
|
||||
{ S.selExtr = [S.Extractor (S.SEStar Nothing) Nothing] }
|
||||
|
||||
-- SELECT count(*) FROM (SELECT DISTINCT c1, .. cn FROM .. WHERE ..) r;
|
||||
-- SELECT count(*) FROM (SELECT * FROM .. WHERE ..) r;
|
||||
|
@ -3,7 +3,6 @@ module Hasura.RQL.DML.Insert where
|
||||
import Data.Aeson.Types
|
||||
import Instances.TH.Lift ()
|
||||
|
||||
import qualified Data.Aeson.Extended as J
|
||||
import qualified Data.HashMap.Strict as HM
|
||||
import qualified Data.HashSet as HS
|
||||
import qualified Data.Sequence as DS
|
||||
@ -33,22 +32,31 @@ data ConflictClauseP1
|
||||
|
||||
data InsertQueryP1
|
||||
= InsertQueryP1
|
||||
{ iqp1Table :: !QualifiedTable
|
||||
, iqp1View :: !QualifiedTable
|
||||
, iqp1Cols :: ![PGCol]
|
||||
, iqp1Tuples :: ![[S.SQLExp]]
|
||||
, iqp1Conflict :: !(Maybe ConflictClauseP1)
|
||||
, iqp1MutFlds :: !MutFlds
|
||||
, iqp1AllCols :: ![PGColumnInfo]
|
||||
{ iqp1Table :: !QualifiedTable
|
||||
, iqp1Cols :: ![PGCol]
|
||||
, iqp1Tuples :: ![[S.SQLExp]]
|
||||
, iqp1Conflict :: !(Maybe ConflictClauseP1)
|
||||
, iqp1CheckCond :: !(Maybe AnnBoolExpSQL)
|
||||
, iqp1MutFlds :: !MutFlds
|
||||
, iqp1AllCols :: ![PGColumnInfo]
|
||||
} deriving (Show, Eq)
|
||||
|
||||
mkInsertCTE :: InsertQueryP1 -> S.CTE
|
||||
mkInsertCTE (InsertQueryP1 _ vn cols vals c _ _) =
|
||||
S.CTEInsert insert
|
||||
mkInsertCTE (InsertQueryP1 tn cols vals c checkCond _ _) =
|
||||
S.CTEInsert insert
|
||||
where
|
||||
tupVals = S.ValuesExp $ map S.TupleExp vals
|
||||
insert =
|
||||
S.SQLInsert vn cols tupVals (toSQLConflict <$> c) $ Just S.returningStar
|
||||
S.SQLInsert tn cols tupVals (toSQLConflict <$> c)
|
||||
. Just
|
||||
. S.RetExp
|
||||
$ maybe
|
||||
[S.selectStar]
|
||||
(\e ->
|
||||
[ S.selectStar
|
||||
, insertCheckExpr (toSQLBoolExp (S.QualTable tn) e)
|
||||
])
|
||||
checkCond
|
||||
|
||||
toSQLConflict :: ConflictClauseP1 -> S.SQLConflict
|
||||
toSQLConflict conflict = case conflict of
|
||||
@ -199,7 +207,6 @@ convInsertQuery objsParser sessVarBldr prepFn (InsertQuery tableName val oC mRet
|
||||
map pgiColumn $ getCols fieldInfoMap
|
||||
allCols = getCols fieldInfoMap
|
||||
insCols = HM.keys defInsVals
|
||||
insView = ipiView insPerm
|
||||
|
||||
resolvedPreSet <- mapM (convPartialSQLExp sessVarBldr) setInsVals
|
||||
|
||||
@ -208,16 +215,17 @@ convInsertQuery objsParser sessVarBldr prepFn (InsertQuery tableName val oC mRet
|
||||
let sqlExps = map snd insTuples
|
||||
inpCols = HS.toList $ HS.fromList $ concatMap fst insTuples
|
||||
|
||||
checkExpr <- convAnnBoolExpPartialSQL sessVarFromCurrentSetting (ipiCheck insPerm)
|
||||
|
||||
conflictClause <- withPathK "on_conflict" $ forM oC $ \c -> do
|
||||
roleName <- askCurRole
|
||||
unless (isTabUpdatable roleName tableInfo) $ throw400 PermissionDenied $
|
||||
"upsert is not allowed for role " <> roleName
|
||||
<<> " since update permissions are not defined"
|
||||
buildConflictClause sessVarBldr tableInfo inpCols c
|
||||
|
||||
return $ InsertQueryP1 tableName insView insCols sqlExps
|
||||
conflictClause mutFlds allCols
|
||||
|
||||
|
||||
return $ InsertQueryP1 tableName insCols sqlExps
|
||||
conflictClause (Just checkExpr) mutFlds allCols
|
||||
where
|
||||
selNecessaryMsg =
|
||||
"; \"returning\" can only be used if the role has "
|
||||
@ -241,53 +249,38 @@ convInsQ =
|
||||
|
||||
insertP2 :: Bool -> (InsertQueryP1, DS.Seq Q.PrepArg) -> Q.TxE QErr EncJSON
|
||||
insertP2 strfyNum (u, p) =
|
||||
runMutation $ Mutation (iqp1Table u) (insertCTE, p)
|
||||
runMutation
|
||||
$ Mutation (iqp1Table u) (insertCTE, p)
|
||||
(iqp1MutFlds u) (iqp1AllCols u) strfyNum
|
||||
where
|
||||
insertCTE = mkInsertCTE u
|
||||
|
||||
data ConflictCtx
|
||||
= CCUpdate !ConstraintName ![PGCol] !PreSetCols !S.BoolExp
|
||||
| CCDoNothing !(Maybe ConstraintName)
|
||||
deriving (Show, Eq)
|
||||
|
||||
nonAdminInsert :: Bool -> (InsertQueryP1, DS.Seq Q.PrepArg) -> Q.TxE QErr EncJSON
|
||||
nonAdminInsert strfyNum (insQueryP1, args) = do
|
||||
conflictCtxM <- mapM extractConflictCtx conflictClauseP1
|
||||
setConflictCtx conflictCtxM
|
||||
insertP2 strfyNum (withoutConflictClause, args)
|
||||
where
|
||||
withoutConflictClause = insQueryP1{iqp1Conflict=Nothing}
|
||||
conflictClauseP1 = iqp1Conflict insQueryP1
|
||||
|
||||
extractConflictCtx :: (MonadError QErr m) => ConflictClauseP1 -> m ConflictCtx
|
||||
extractConflictCtx cp =
|
||||
case cp of
|
||||
(CP1DoNothing mConflictTar) -> do
|
||||
mConstraintName <- mapM extractConstraintName mConflictTar
|
||||
return $ CCDoNothing mConstraintName
|
||||
(CP1Update conflictTar inpCols preSet filtr) -> do
|
||||
constraintName <- extractConstraintName conflictTar
|
||||
return $ CCUpdate constraintName inpCols preSet filtr
|
||||
where
|
||||
extractConstraintName (CTConstraint cn) = return cn
|
||||
extractConstraintName _ = throw400 NotSupported
|
||||
"\"constraint_on\" not supported for non admin insert. use \"constraint\" instead"
|
||||
|
||||
setConflictCtx :: Maybe ConflictCtx -> Q.TxE QErr ()
|
||||
setConflictCtx conflictCtxM = do
|
||||
let t = maybe "null" conflictCtxToJSON conflictCtxM
|
||||
setVal = toSQL $ S.SELit t
|
||||
setVar = "SET LOCAL hasura.conflict_clause = "
|
||||
q = Q.fromBuilder $ setVar <> setVal
|
||||
Q.unitQE defaultTxErrorHandler q () False
|
||||
where
|
||||
conflictCtxToJSON (CCDoNothing constrM) =
|
||||
J.encodeToStrictText $ InsertTxConflictCtx CAIgnore constrM Nothing
|
||||
conflictCtxToJSON (CCUpdate constr updCols preSet filtr) =
|
||||
J.encodeToStrictText $ InsertTxConflictCtx CAUpdate (Just constr) $
|
||||
Just $ toSQLTxt (S.buildUpsertSetExp updCols preSet)
|
||||
<> " " <> toSQLTxt (S.WhereFrag filtr)
|
||||
-- | Create an expression which will fail with a check constraint violation error
|
||||
-- if the condition is not met on any of the inserted rows.
|
||||
--
|
||||
-- The resulting SQL will look something like this:
|
||||
--
|
||||
-- > INSERT INTO
|
||||
-- > ...
|
||||
-- > RETURNING
|
||||
-- > *,
|
||||
-- > CASE WHEN {cond}
|
||||
-- > THEN NULL
|
||||
-- > ELSE hdb_catalog.check_violation('insert check constraint failed')
|
||||
-- > END
|
||||
insertCheckExpr
|
||||
:: S.BoolExp
|
||||
-> S.Extractor
|
||||
insertCheckExpr condExpr =
|
||||
S.Extractor
|
||||
(S.SECond condExpr S.SENull
|
||||
(S.SEFunction
|
||||
(S.FunctionExp
|
||||
(QualifiedObject (SchemaName "hdb_catalog") (FunctionName "check_violation"))
|
||||
(S.FunctionArgs [S.SELit "insert check constraint failed"] mempty)
|
||||
Nothing)
|
||||
))
|
||||
Nothing
|
||||
|
||||
runInsert
|
||||
:: (QErrM m, UserInfoM m, CacheRM m, MonadTx m, HasSQLGenCtx m)
|
||||
@ -295,6 +288,5 @@ runInsert
|
||||
-> m EncJSON
|
||||
runInsert q = do
|
||||
res <- convInsQ q
|
||||
role <- userRole <$> askUserInfo
|
||||
strfyNum <- stringifyNum <$> askSQLGenCtx
|
||||
liftTx $ bool (nonAdminInsert strfyNum res) (insertP2 strfyNum res) $ isAdmin role
|
||||
liftTx $ insertP2 strfyNum res
|
||||
|
@ -38,7 +38,7 @@ mkAdminRolePermInfo ti =
|
||||
getComputedFieldInfos fields
|
||||
|
||||
tn = _tciName ti
|
||||
i = InsPermInfo (HS.fromList pgCols) tn annBoolExpTrue M.empty []
|
||||
i = InsPermInfo (HS.fromList pgCols) annBoolExpTrue M.empty []
|
||||
s = SelPermInfo (HS.fromList pgCols) (HS.fromList scalarComputedFields) tn annBoolExpTrue
|
||||
Nothing True []
|
||||
u = UpdPermInfo (HS.fromList pgCols) tn annBoolExpTrue M.empty []
|
||||
|
@ -705,7 +705,7 @@ baseNodeToSel joinCond baseNode =
|
||||
= baseNode
|
||||
-- this is the table which is aliased as "pfx.base"
|
||||
baseSel = S.mkSelect
|
||||
{ S.selExtr = [S.Extractor S.SEStar Nothing]
|
||||
{ S.selExtr = [S.Extractor (S.SEStar Nothing) Nothing]
|
||||
, S.selFrom = Just $ S.FromExp [fromItem]
|
||||
, S.selWhere = Just $ injectJoinCond joinCond whr
|
||||
}
|
||||
|
@ -208,7 +208,6 @@ isPGColInfo _ = False
|
||||
data InsPermInfo
|
||||
= InsPermInfo
|
||||
{ ipiCols :: !(HS.HashSet PGCol)
|
||||
, ipiView :: !QualifiedTable
|
||||
, ipiCheck :: !AnnBoolExpPartialSQL
|
||||
, ipiSet :: !PreSetColsPartial
|
||||
, ipiRequiredHeaders :: ![T.Text]
|
||||
|
@ -283,7 +283,8 @@ data SQLExp
|
||||
| SELit !T.Text
|
||||
| SEUnsafe !T.Text
|
||||
| SESelect !Select
|
||||
| SEStar
|
||||
| SEStar !(Maybe Qual)
|
||||
-- ^ all fields (@*@) or all fields from relation (@iden.*@)
|
||||
| SEIden !Iden
|
||||
-- iden and row identifier are distinguished for easier rewrite rules
|
||||
| SERowIden !Iden
|
||||
@ -336,8 +337,10 @@ instance ToSQL SQLExp where
|
||||
TB.text t
|
||||
toSQL (SESelect se) =
|
||||
paren $ toSQL se
|
||||
toSQL SEStar =
|
||||
toSQL (SEStar Nothing) =
|
||||
TB.char '*'
|
||||
toSQL (SEStar (Just qual)) =
|
||||
mconcat [toSQL qual, TB.char '.', TB.char '*']
|
||||
toSQL (SEIden iden) =
|
||||
toSQL iden
|
||||
toSQL (SERowIden iden) =
|
||||
@ -725,7 +728,10 @@ newtype RetExp = RetExp [Extractor]
|
||||
deriving (Show, Eq)
|
||||
|
||||
selectStar :: Extractor
|
||||
selectStar = Extractor SEStar Nothing
|
||||
selectStar = Extractor (SEStar Nothing) Nothing
|
||||
|
||||
selectStar' :: Qual -> Extractor
|
||||
selectStar' q = Extractor (SEStar (Just q)) Nothing
|
||||
|
||||
returningStar :: RetExp
|
||||
returningStar = RetExp [selectStar]
|
||||
@ -804,15 +810,14 @@ data SQLInsert = SQLInsert
|
||||
|
||||
instance ToSQL SQLInsert where
|
||||
toSQL si =
|
||||
let insConflict = maybe "" toSQL
|
||||
in "INSERT INTO"
|
||||
<-> toSQL (siTable si)
|
||||
<-> "("
|
||||
<-> (", " <+> siCols si)
|
||||
<-> ")"
|
||||
<-> toSQL (siValues si)
|
||||
<-> insConflict (siConflict si)
|
||||
<-> toSQL (siRet si)
|
||||
"INSERT INTO"
|
||||
<-> toSQL (siTable si)
|
||||
<-> "("
|
||||
<-> (", " <+> siCols si)
|
||||
<-> ")"
|
||||
<-> toSQL (siValues si)
|
||||
<-> maybe "" toSQL (siConflict si)
|
||||
<-> toSQL (siRet si)
|
||||
|
||||
data CTE
|
||||
= CTESelect !Select
|
||||
|
@ -161,7 +161,7 @@ uSqlExp = restoringIdens . \case
|
||||
S.SELit t -> return $ S.SELit t
|
||||
S.SEUnsafe t -> return $ S.SEUnsafe t
|
||||
S.SESelect s -> S.SESelect <$> uSelect s
|
||||
S.SEStar -> return S.SEStar
|
||||
S.SEStar qual -> S.SEStar <$> traverse uQual qual
|
||||
-- this is for row expressions
|
||||
-- todo: check if this is always okay
|
||||
S.SEIden iden -> return $ S.SEIden iden
|
||||
|
@ -40,7 +40,6 @@ module Hasura.SQL.Types
|
||||
|
||||
, SchemaName(..)
|
||||
, publicSchema
|
||||
, hdbViewsSchema
|
||||
|
||||
, TableName(..)
|
||||
, FunctionName(..)
|
||||
@ -239,9 +238,6 @@ newtype SchemaName
|
||||
publicSchema :: SchemaName
|
||||
publicSchema = SchemaName "public"
|
||||
|
||||
hdbViewsSchema :: SchemaName
|
||||
hdbViewsSchema = SchemaName "hdb_views"
|
||||
|
||||
instance IsIden SchemaName where
|
||||
toIden (SchemaName t) = Iden t
|
||||
|
||||
|
@ -12,7 +12,7 @@ import Hasura.Prelude
|
||||
import qualified Data.Text as T
|
||||
|
||||
latestCatalogVersion :: Integer
|
||||
latestCatalogVersion = 29
|
||||
latestCatalogVersion = 30
|
||||
|
||||
latestCatalogVersionString :: T.Text
|
||||
latestCatalogVersionString = T.pack $ show latestCatalogVersion
|
||||
|
@ -61,10 +61,10 @@ data RQLQueryV1
|
||||
| RQCreateUpdatePermission !CreateUpdPerm
|
||||
| RQCreateDeletePermission !CreateDelPerm
|
||||
|
||||
| RQDropInsertPermission !DropInsPerm
|
||||
| RQDropSelectPermission !DropSelPerm
|
||||
| RQDropUpdatePermission !DropUpdPerm
|
||||
| RQDropDeletePermission !DropDelPerm
|
||||
| RQDropInsertPermission !(DropPerm InsPerm)
|
||||
| RQDropSelectPermission !(DropPerm SelPerm)
|
||||
| RQDropUpdatePermission !(DropPerm UpdPerm)
|
||||
| RQDropDeletePermission !(DropPerm DelPerm)
|
||||
| RQSetPermissionComment !SetPermComment
|
||||
|
||||
| RQGetInconsistentMetadata !GetInconsistentMetadata
|
||||
|
@ -656,3 +656,10 @@ CREATE VIEW hdb_catalog.hdb_computed_field_function AS
|
||||
END AS function_schema
|
||||
FROM hdb_catalog.hdb_computed_field
|
||||
);
|
||||
|
||||
CREATE OR REPLACE FUNCTION hdb_catalog.check_violation(msg text) RETURNS bool AS
|
||||
$$
|
||||
BEGIN
|
||||
RAISE check_violation USING message=msg;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
@ -1,34 +0,0 @@
|
||||
CREATE OR REPLACE FUNCTION #{functionName}() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
DECLARE r #{tableName}%ROWTYPE;
|
||||
DECLARE conflict_clause jsonb;
|
||||
DECLARE action text;
|
||||
DECLARE constraint_name text;
|
||||
DECLARE set_expression text;
|
||||
BEGIN
|
||||
conflict_clause = current_setting('hasura.conflict_clause')::jsonb;
|
||||
IF (#{checkExpression}) THEN
|
||||
CASE
|
||||
WHEN conflict_clause = 'null'::jsonb THEN INSERT INTO #{tableName} VALUES (NEW.*) RETURNING * INTO r;
|
||||
ELSE
|
||||
action = conflict_clause ->> 'action';
|
||||
constraint_name = quote_ident(conflict_clause ->> 'constraint');
|
||||
set_expression = conflict_clause ->> 'set_expression';
|
||||
IF action is NOT NULL THEN
|
||||
CASE
|
||||
WHEN action = 'ignore'::text AND constraint_name IS NULL THEN
|
||||
INSERT INTO #{tableName} VALUES (NEW.*) ON CONFLICT DO NOTHING RETURNING * INTO r;
|
||||
WHEN action = 'ignore'::text AND constraint_name is NOT NULL THEN
|
||||
EXECUTE 'INSERT INTO #{tableName} VALUES ($1.*) ON CONFLICT ON CONSTRAINT ' || constraint_name ||
|
||||
' DO NOTHING RETURNING *' INTO r USING NEW;
|
||||
ELSE
|
||||
EXECUTE 'INSERT INTO #{tableName} VALUES ($1.*) ON CONFLICT ON CONSTRAINT ' || constraint_name ||
|
||||
' DO UPDATE ' || set_expression || ' RETURNING *' INTO r USING NEW;
|
||||
END CASE;
|
||||
ELSE
|
||||
RAISE internal_error using message = 'action is not found'; RETURN NULL;
|
||||
END IF;
|
||||
END CASE;
|
||||
IF r IS NULL THEN RETURN null; ELSE RETURN r; END IF;
|
||||
ELSE RAISE check_violation using message = 'insert check constraint failed'; RETURN NULL;
|
||||
END IF;
|
||||
END $$;
|
@ -128,4 +128,4 @@ CREATE VIEW hdb_catalog.hdb_table_info_agg AS
|
||||
) foreign_key_constraints ON true
|
||||
|
||||
-- all these identify table-like things
|
||||
WHERE "table".relkind IN ('r', 't', 'v', 'm', 'f', 'p');
|
||||
WHERE "table".relkind IN ('r', 't', 'v', 'm', 'f', 'p');
|
6
server/src-rsr/migrations/29_to_30.sql
Normal file
6
server/src-rsr/migrations/29_to_30.sql
Normal file
@ -0,0 +1,6 @@
|
||||
CREATE OR REPLACE FUNCTION hdb_catalog.check_violation(msg text) RETURNS bool AS
|
||||
$$
|
||||
BEGIN
|
||||
RAISE check_violation USING message=msg;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
Loading…
Reference in New Issue
Block a user