Pass custom settings to the DB's SESSION (#1063)

- allows queries to refer to current_setting('app.settings.foo') to retrieve variables
- useful for 12-factor apps (app data can be in environment)
- provides workaround for AWS Relational Database Service (RDS) not
  allowing `ALTER DATABASE SET 'app.[KEY]' TO '[VALUE]'` on database.
This commit is contained in:
Duane Johnson
2018-02-19 12:41:22 -06:00
committed by Joe Nelson
parent 70ce1b9329
commit a46b6f5020
8 changed files with 61 additions and 11 deletions
+12 -3
View File
@@ -76,6 +76,7 @@ data AppConfig = AppConfig {
, configMaxRows :: Maybe Integer
, configReqCheck :: Maybe Text
, configQuiet :: Bool
, configSettings :: [(Text, Text)]
}
defaultCorsPolicy :: CorsResourcePolicy
@@ -136,6 +137,7 @@ readOptions = do
<*> (join . fmap coerceInt <$> C.key "max-rows")
<*> (mfilter (/= "") <$> C.key "pre-request")
<*> pure False
<*> (fmap parsedPairToTextPair <$> C.subassocs "app.settings")
case mAppConf of
Nothing -> do
@@ -145,6 +147,13 @@ readOptions = do
return appConf
where
parsedPairToTextPair :: (Name, Value) -> (Text, Text)
parsedPairToTextPair (k, v) = (k, newValue)
where
newValue = case v of
String textVal -> textVal
_ -> show v
parseJwtAudience :: Name -> C.ConfigParserM (Maybe StringOrURI)
parseJwtAudience k =
C.key k >>= \case
@@ -159,9 +168,9 @@ readOptions = do
coerceInt (String x) = readMaybe $ toS x
coerceInt _ = Nothing
coerceBool :: Value -> Maybe Bool
coerceBool :: Value -> Maybe Bool
coerceBool (Bool b) = Just b
coerceBool (String x) = readMaybe $ toS x
coerceBool (String b) = readMaybe $ toS b
coerceBool _ = Nothing
opts = info (helper <*> pathParser) $
@@ -218,7 +227,7 @@ pathParser =
-- | Tells the minimum PostgreSQL version required by this version of PostgREST
minimumPgVersion :: PgVersion
minimumPgVersion = PgVersion 90300 "9.3"
minimumPgVersion = PgVersion 90400 "9.4"
pgVersion96 :: PgVersion
pgVersion96 = PgVersion 90600 "9.6"
+21 -1
View File
@@ -9,6 +9,7 @@ module PostgREST.DbStructure (
, accessibleProcs
, schemaDescription
, getPgVersion
, fillSessionWithSettings
) where
import qualified Hasql.Decoders as HD
@@ -18,9 +19,11 @@ import qualified Hasql.Query as H
import Control.Applicative
import qualified Data.HashMap.Strict as M
import Data.List (elemIndex)
import qualified Data.List as List
import Data.Maybe (fromJust)
import Data.Text (split, strip,
breakOn, dropAround, splitOn)
breakOn, dropAround,
splitOn)
import qualified Data.Text as T
import qualified Hasql.Session as H
import PostgREST.Types
@@ -30,6 +33,9 @@ import GHC.Exts (groupWith)
import Protolude
import Unsafe (unsafeHead)
import Data.Functor.Contravariant (contramap)
import Contravariant.Extras (contrazip2)
getDbStructure :: Schema -> PgVersion -> H.Session DbStructure
getDbStructure schema pgVer = do
tabs <- H.query () allTables
@@ -747,3 +753,17 @@ getPgVersion = H.query () $ H.statement sql HE.unit versionRow False
where
sql = "SELECT current_setting('server_version_num')::integer, current_setting('server_version')"
versionRow = HD.singleRow $ PgVersion <$> HD.value HD.int4 <*> HD.value HD.text
fillSessionWithSettings :: [(Text, Text)] -> H.Session ()
fillSessionWithSettings settings =
-- Send all of the config settings to the set_config function, using pgsql's `unnest` to transform arrays of values
H.query settings $ H.statement "SELECT set_config(k, v, false) FROM unnest($1, $2) AS f1(k, v)" encoder HD.unit False
where
-- Take a list of (key, value) pairs and encode each as an array to later bind to the query
-- see Insert Many section at https://hackage.haskell.org/package/hasql-1.1.1/docs/Hasql-Encoders.html
encoder = contramap List.unzip $ contrazip2 (vector HE.text) (vector HE.text)
where
vector value =
HE.value $ HE.array $ HE.arrayDimension foldl' $ HE.arrayValue value