diff --git a/CHANGELOG.md b/CHANGELOG.md index b6918b29e..5b93645ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #777, Empty body is allowed when calling a non-parameterized RPC - @koulakis - #831, Fix proc resource embedding issue with search_path - @steve-chavez - #857, Fix swagger-ui when "server-proxy-uri" is not set - @feynmanliang +- #547, Use read-only transaction for stable/immutable RPC - @begriffs ## [0.4.0.0] - 2017-01-19 diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index 73235e57c..c60335699 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -79,15 +79,29 @@ postgrest conf refDbStructure pool getTime = eClaims = jwtClaims jwtSecret (iJWT apiRequest) time authed = containsRole eClaims handleReq = runWithClaims conf eClaims (app dbStructure conf) apiRequest - txMode = transactionMode $ iAction apiRequest + txMode = transactionMode dbStructure + (iTarget apiRequest) (iAction apiRequest) response <- P.use pool $ HT.transaction HT.ReadCommitted txMode handleReq return $ either (pgError authed) identity response respond response -transactionMode :: Action -> H.Mode -transactionMode ActionRead = HT.Read -transactionMode ActionInfo = HT.Read -transactionMode _ = HT.Write +transactionMode :: DbStructure -> Target -> Action -> H.Mode +transactionMode structure target action = + case action of + ActionRead -> HT.Read + ActionInfo -> HT.Read + ActionInspect -> HT.Read + ActionInvoke -> + let proc = + case target of + (TargetProc qi) -> M.lookup (qiName qi) $ + dbProcs structure + _ -> Nothing + v = fromMaybe Volatile $ pdVolatility <$> proc in + if v == Stable || v == Immutable + then HT.Read + else HT.Write + _ -> HT.Write app :: DbStructure -> AppConfig -> ApiRequest -> H.Transaction Response app dbStructure conf apiRequest = diff --git a/src/PostgREST/DbStructure.hs b/src/PostgREST/DbStructure.hs index 1bc2142ca..faf779f62 100644 --- a/src/PostgREST/DbStructure.hs +++ b/src/PostgREST/DbStructure.hs @@ -100,10 +100,19 @@ decodeSynonyms cols = accessibleProcs :: H.Query Schema (M.HashMap Text ProcDescription) accessibleProcs = H.statement sql (HE.value HE.text) - (M.fromList . map addName <$> HD.rowsList (ProcDescription <$> HD.value HD.text - <*> (parseArgs <$> HD.value HD.text) - <*> (parseRetType <$> HD.value HD.text <*> HD.value HD.text <*> - HD.value HD.bool <*> HD.value HD.char))) True + (M.fromList . map addName <$> + HD.rowsList ( + ProcDescription <$> HD.value HD.text + <*> (parseArgs <$> HD.value HD.text) + <*> (parseRetType <$> + HD.value HD.text <*> + HD.value HD.text <*> + HD.value HD.bool <*> + HD.value HD.char) + <*> (parseVolatility <$> + HD.value HD.char) + ) + ) True where addName :: ProcDescription -> (Text, ProcDescription) addName pd = (pdName pd, pd) @@ -124,25 +133,32 @@ accessibleProcs = parseRetType schema name isSetOf typ | isSetOf = SetOf pgType | otherwise = Single pgType - where + where qi = QualifiedIdentifier schema name - pgType = case typ of + pgType = case typ of 'c' -> Composite qi 'p' -> Pseudo name _ -> Scalar qi -- 'b'ase, 'd'omain, 'e'num, 'r'ange - + + parseVolatility :: Char -> ProcVolatility + parseVolatility 'i' = Immutable + parseVolatility 's' = Stable + parseVolatility 'v' = Volatile + parseVolatility _ = Volatile -- should not happen, but be pessimistic + sql = [q| SELECT p.proname as "proc_name", pg_get_function_arguments(p.oid) as "args", tn.nspname as "rettype_schema", coalesce(comp.relname, t.typname) as "rettype_name", p.proretset as "rettype_is_setof", - t.typtype as "rettype_typ" + t.typtype as "rettype_typ", + p.provolatile FROM pg_proc p JOIN pg_namespace pn ON pn.oid = p.pronamespace JOIN pg_type t ON t.oid = p.prorettype JOIN pg_namespace tn ON tn.oid = t.typnamespace - LEFT JOIN pg_class comp ON comp.oid = t.typrelid + LEFT JOIN pg_class comp ON comp.oid = t.typrelid WHERE pn.nspname = $1|] accessibleTables :: H.Query Schema [Table] diff --git a/src/PostgREST/Types.hs b/src/PostgREST/Types.hs index 4895aab7e..8b760e849 100644 --- a/src/PostgREST/Types.hs +++ b/src/PostgREST/Types.hs @@ -41,10 +41,14 @@ data PgType = Scalar QualifiedIdentifier | Composite QualifiedIdentifier | Pseud data RetType = Single PgType | SetOf PgType deriving (Eq, Show) +data ProcVolatility = Volatile | Stable | Immutable + deriving (Eq, Show) + data ProcDescription = ProcDescription { pdName :: Text , pdArgs :: [PgArg] , pdReturnType :: RetType +, pdVolatility :: ProcVolatility } deriving (Show, Eq) type Schema = Text diff --git a/test/fixtures/schema.sql b/test/fixtures/schema.sql index 1a19b45d4..51ec8ce62 100755 --- a/test/fixtures/schema.sql +++ b/test/fixtures/schema.sql @@ -316,6 +316,7 @@ $$; CREATE FUNCTION sayhello(name text) RETURNS text LANGUAGE sql + IMMUTABLE AS $_$ SELECT 'Hello, ' || $1; $_$;