155 lines
5.4 KiB
Haskell
155 lines
5.4 KiB
Haskell
{-# LANGUAGE DeriveAnyClass #-}
|
|
{-# LANGUAGE DeriveGeneric #-}
|
|
|
|
module PostgREST.SchemaCache.Routine
|
|
( PgType(..)
|
|
, Routine(..)
|
|
, RoutineParam(..)
|
|
, FuncVolatility(..)
|
|
, FuncSettings
|
|
, RoutineMap
|
|
, RetType(..)
|
|
, funcReturnsScalar
|
|
, funcReturnsSetOfScalar
|
|
, funcReturnsSingleComposite
|
|
, funcReturnsVoid
|
|
, funcTableName
|
|
, funcReturnsCompositeAlias
|
|
, funcReturnsSingle
|
|
, MediaHandlerMap
|
|
, ResolvedHandler
|
|
, MediaHandler(..)
|
|
) where
|
|
|
|
import Data.Aeson ((.=))
|
|
import qualified Data.Aeson as JSON
|
|
import qualified Data.HashMap.Strict as HM
|
|
import qualified Hasql.Transaction.Sessions as SQL
|
|
import qualified PostgREST.MediaType as MediaType
|
|
|
|
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..),
|
|
RelIdentifier (..), Schema,
|
|
TableName)
|
|
|
|
|
|
import Protolude
|
|
|
|
data PgType
|
|
= Scalar QualifiedIdentifier
|
|
| Composite QualifiedIdentifier Bool -- True if the composite is a domain alias(used to work around a bug in pg 11 and 12, see QueryBuilder.hs)
|
|
deriving (Eq, Show, Ord, Generic, JSON.ToJSON)
|
|
|
|
data RetType
|
|
= Single PgType
|
|
| SetOf PgType
|
|
deriving (Eq, Show, Ord, Generic, JSON.ToJSON)
|
|
|
|
data FuncVolatility
|
|
= Volatile
|
|
| Stable
|
|
| Immutable
|
|
deriving (Eq, Show, Ord, Generic, JSON.ToJSON)
|
|
|
|
type FuncSettings = [(Text,Text)]
|
|
|
|
data Routine = Function
|
|
{ pdSchema :: Schema
|
|
, pdName :: Text
|
|
, pdDescription :: Maybe Text
|
|
, pdParams :: [RoutineParam]
|
|
, pdReturnType :: RetType
|
|
, pdVolatility :: FuncVolatility
|
|
, pdHasVariadic :: Bool
|
|
, pdIsoLvl :: Maybe SQL.IsolationLevel
|
|
, pdFuncSettings :: FuncSettings
|
|
}
|
|
deriving (Eq, Show, Generic)
|
|
-- need to define JSON manually bc SQL.IsolationLevel doesn't have a JSON instance(and we can't define one for that type without getting a compiler error)
|
|
instance JSON.ToJSON Routine where
|
|
toJSON (Function sch nam desc params ret vol hasVar _ sets) = JSON.object
|
|
[
|
|
"pdSchema" .= sch
|
|
, "pdName" .= nam
|
|
, "pdDescription" .= desc
|
|
, "pdParams" .= JSON.toJSON params
|
|
, "pdReturnType" .= JSON.toJSON ret
|
|
, "pdVolatility" .= JSON.toJSON vol
|
|
, "pdHasVariadic" .= JSON.toJSON hasVar
|
|
, "pdFuncSettings" .= JSON.toJSON sets
|
|
]
|
|
|
|
data RoutineParam = RoutineParam
|
|
{ ppName :: Text
|
|
, ppType :: Text
|
|
, ppTypeMaxLength :: Text
|
|
, ppReq :: Bool
|
|
, ppVar :: Bool
|
|
}
|
|
deriving (Eq, Show, Ord, Generic, JSON.ToJSON)
|
|
|
|
-- Order by least number of params in the case of overloaded functions
|
|
instance Ord Routine where
|
|
Function schema1 name1 des1 prms1 rt1 vol1 hasVar1 iso1 sets1 `compare` Function schema2 name2 des2 prms2 rt2 vol2 hasVar2 iso2 sets2
|
|
| schema1 == schema2 && name1 == name2 && length prms1 < length prms2 = LT
|
|
| schema2 == schema2 && name1 == name2 && length prms1 > length prms2 = GT
|
|
| otherwise = (schema1, name1, des1, prms1, rt1, vol1, hasVar1, iso1, sets1) `compare` (schema2, name2, des2, prms2, rt2, vol2, hasVar2, iso2, sets2)
|
|
|
|
-- | A map of all procs, all of which can be overloaded(one entry will have more than one Routine).
|
|
-- | It uses a HashMap for a faster lookup.
|
|
type RoutineMap = HM.HashMap QualifiedIdentifier [Routine]
|
|
|
|
-- | A media handler can be an aggregate over a composite type or a function over a scalar
|
|
data MediaHandler
|
|
-- non overridable builtins
|
|
= BuiltinAggSingleJson Bool
|
|
| BuiltinAggArrayJsonStrip
|
|
-- these builtins are overridable
|
|
| BuiltinOvAggJson
|
|
| BuiltinOvAggGeoJson
|
|
| BuiltinOvAggCsv
|
|
-- custom
|
|
| CustomFunc QualifiedIdentifier RelIdentifier
|
|
| NoAgg
|
|
deriving (Eq, Show, Generic, JSON.ToJSON)
|
|
|
|
funcReturnsSingle :: Routine -> Bool
|
|
funcReturnsSingle proc = case proc of
|
|
Function{pdReturnType = Single _} -> True
|
|
_ -> False
|
|
|
|
funcReturnsScalar :: Routine -> Bool
|
|
funcReturnsScalar proc = case proc of
|
|
Function{pdReturnType = Single (Scalar{})} -> True
|
|
_ -> False
|
|
|
|
funcReturnsSetOfScalar :: Routine -> Bool
|
|
funcReturnsSetOfScalar proc = case proc of
|
|
Function{pdReturnType = SetOf (Scalar{})} -> True
|
|
_ -> False
|
|
|
|
funcReturnsCompositeAlias :: Routine -> Bool
|
|
funcReturnsCompositeAlias proc = case proc of
|
|
Function{pdReturnType = Single (Composite _ True)} -> True
|
|
Function{pdReturnType = SetOf (Composite _ True)} -> True
|
|
_ -> False
|
|
|
|
funcReturnsSingleComposite :: Routine -> Bool
|
|
funcReturnsSingleComposite proc = case proc of
|
|
Function{pdReturnType = Single (Composite _ _)} -> True
|
|
_ -> False
|
|
|
|
funcReturnsVoid :: Routine -> Bool
|
|
funcReturnsVoid proc = case proc of
|
|
Function{pdReturnType = Single (Scalar (QualifiedIdentifier "pg_catalog" "void"))} -> True
|
|
_ -> False
|
|
|
|
funcTableName :: Routine -> Maybe TableName
|
|
funcTableName proc = case pdReturnType proc of
|
|
SetOf (Composite qi _) -> Just $ qiName qi
|
|
Single (Composite qi _) -> Just $ qiName qi
|
|
_ -> Nothing
|
|
|
|
-- the resolved handler also carries the media type because MTAny (*/*) is resolved to a different media type
|
|
type ResolvedHandler = (MediaHandler, MediaType.MediaType)
|
|
type MediaHandlerMap = HM.HashMap (RelIdentifier, MediaType.MediaType) ResolvedHandler
|