mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 20:41:49 +03:00
ed26da59a6
These changes also add a new type, PGColumnType, between PGColInfo and PGScalarType, and they process PGRawColumnType values into PGColumnType values during schema cache generation.
125 lines
3.7 KiB
Haskell
125 lines
3.7 KiB
Haskell
module Hasura.RQL.DML.Count
|
|
( CountQueryP1(..)
|
|
, validateCountQWith
|
|
, validateCountQ
|
|
, runCount
|
|
, countQToTx
|
|
) where
|
|
|
|
import Data.Aeson
|
|
import Instances.TH.Lift ()
|
|
|
|
import qualified Data.ByteString.Builder as BB
|
|
import qualified Data.Sequence as DS
|
|
|
|
import Hasura.EncJSON
|
|
import Hasura.Prelude
|
|
import Hasura.RQL.DML.Internal
|
|
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
|
|
|
|
data CountQueryP1
|
|
= CountQueryP1
|
|
{ cqp1Table :: !QualifiedTable
|
|
, cqp1Where :: !(AnnBoolExpSQL, Maybe AnnBoolExpSQL)
|
|
, cqp1Distinct :: !(Maybe [PGCol])
|
|
} deriving (Show, Eq)
|
|
|
|
mkSQLCount
|
|
:: CountQueryP1 -> S.Select
|
|
mkSQLCount (CountQueryP1 tn (permFltr, mWc) mDistCols) =
|
|
S.mkSelect
|
|
{ S.selExtr = [S.Extractor S.countStar Nothing]
|
|
, S.selFrom = Just $ S.FromExp
|
|
[S.mkSelFromExp False innerSel $ TableName "r"]
|
|
}
|
|
where
|
|
|
|
finalWC =
|
|
toSQLBoolExp (S.QualTable tn) $
|
|
maybe permFltr (andAnnBoolExps permFltr) mWc
|
|
|
|
innerSel = partSel
|
|
{ S.selFrom = Just $ S.mkSimpleFromExp tn
|
|
, S.selWhere = S.WhereFrag <$> Just finalWC
|
|
}
|
|
|
|
partSel = case mDistCols of
|
|
Just distCols ->
|
|
let extrs = flip map distCols $ \c -> S.Extractor (S.mkSIdenExp c) Nothing
|
|
in S.mkSelect
|
|
{ S.selDistinct = Just S.DistinctSimple
|
|
, S.selExtr = extrs
|
|
}
|
|
Nothing -> S.mkSelect
|
|
{ S.selExtr = [S.Extractor S.SEStar Nothing] }
|
|
|
|
-- SELECT count(*) FROM (SELECT DISTINCT c1, .. cn FROM .. WHERE ..) r;
|
|
-- SELECT count(*) FROM (SELECT * FROM .. WHERE ..) r;
|
|
validateCountQWith
|
|
:: (UserInfoM m, QErrM m, CacheRM m)
|
|
=> SessVarBldr m
|
|
-> (PGColumnType -> Value -> m S.SQLExp)
|
|
-> CountQuery
|
|
-> m CountQueryP1
|
|
validateCountQWith sessVarBldr prepValBldr (CountQuery qt mDistCols mWhere) = do
|
|
tableInfo <- askTabInfo qt
|
|
|
|
-- Check if select is allowed
|
|
selPerm <- modifyErr (<> selNecessaryMsg) $
|
|
askSelPermInfo tableInfo
|
|
|
|
let colInfoMap = _tiFieldInfoMap tableInfo
|
|
|
|
forM_ mDistCols $ \distCols -> do
|
|
let distColAsrns = [ checkSelOnCol selPerm
|
|
, assertPGCol colInfoMap relInDistColsErr]
|
|
withPathK "distinct" $ verifyAsrns distColAsrns distCols
|
|
|
|
-- convert the where clause
|
|
annSQLBoolExp <- forM mWhere $ \be ->
|
|
withPathK "where" $
|
|
convBoolExp colInfoMap selPerm be sessVarBldr prepValBldr
|
|
|
|
resolvedSelFltr <- convAnnBoolExpPartialSQL sessVarBldr $
|
|
spiFilter selPerm
|
|
|
|
return $ CountQueryP1
|
|
qt
|
|
(resolvedSelFltr, annSQLBoolExp)
|
|
mDistCols
|
|
where
|
|
selNecessaryMsg =
|
|
"; \"count\" is only allowed if the role "
|
|
<> "has \"select\" permissions on the table"
|
|
relInDistColsErr =
|
|
"Relationships can't be used in \"distinct\"."
|
|
|
|
validateCountQ
|
|
:: (QErrM m, UserInfoM m, CacheRM m, HasSQLGenCtx m)
|
|
=> CountQuery -> m (CountQueryP1, DS.Seq Q.PrepArg)
|
|
validateCountQ =
|
|
liftDMLP1 . validateCountQWith sessVarFromCurrentSetting binRHSBuilder
|
|
|
|
countQToTx
|
|
:: (QErrM m, MonadTx m)
|
|
=> (CountQueryP1, DS.Seq Q.PrepArg) -> m EncJSON
|
|
countQToTx (u, p) = do
|
|
qRes <- liftTx $ Q.rawQE dmlTxErrorHandler
|
|
(Q.fromBuilder countSQL) (toList p) True
|
|
return $ encJFromBuilder $ encodeCount qRes
|
|
where
|
|
countSQL = toSQL $ mkSQLCount u
|
|
encodeCount (Q.SingleRow (Identity c)) =
|
|
BB.byteString "{\"count\":" <> BB.intDec c <> BB.char7 '}'
|
|
|
|
runCount
|
|
:: (QErrM m, UserInfoM m, CacheRWM m, MonadTx m, HasSQLGenCtx m)
|
|
=> CountQuery -> m EncJSON
|
|
runCount q =
|
|
validateCountQ q >>= countQToTx
|