diff --git a/CHANGELOG.md b/CHANGELOG.md index 5036a633875..8ee73d84e36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Next release (Add entries here in the order of: server, console, cli, docs, others) +- server: `use_prepared_statements` option (default: False) in `add_pg_source` metadata API - server: add a comment field for actions (#231) - console: add a comment field for actions (#231) diff --git a/server/src-exec/Main.hs b/server/src-exec/Main.hs index 3bd9f16f4c4..a2864423a03 100644 --- a/server/src-exec/Main.hs +++ b/server/src-exec/Main.hs @@ -135,6 +135,7 @@ runApp env (HGEOptionsG rci metadataDbUrl hgeCmd) = do let defaultSourceConfig = maybeDefaultPgConnInfo <&> \(dbUrlConf, _) -> let pgSourceConnInfo = PostgresSourceConnInfo dbUrlConf (Just setPostgresPoolSettings{_ppsRetries = maybeRetries <|> Just 1}) + False in PostgresConnConfiguration pgSourceConnInfo Nothing res <- runTxWithMinimalPool _gcMetadataDbConnInfo $ downgradeCatalog defaultSourceConfig opts initTime either (printErrJExit DowngradeProcessError) (liftIO . print) res diff --git a/server/src-lib/Hasura/App.hs b/server/src-lib/Hasura/App.hs index 26a841eb23a..a6d0a198114 100644 --- a/server/src-lib/Hasura/App.hs +++ b/server/src-lib/Hasura/App.hs @@ -278,7 +278,7 @@ initialiseServeCtx env GlobalCtx{..} so@ServeOptions{..} = do , _ppsIdleTimeout = Just $ Q.cpIdleTime soConnParams , _ppsRetries = snd _gcDefaultPostgresConnInfo <|> Just 1 } - sourceConnInfo = PostgresSourceConnInfo dbUrlConf (Just connSettings) + sourceConnInfo = PostgresSourceConnInfo dbUrlConf (Just connSettings) $ Q.cpAllowPrepare soConnParams in PostgresConnConfiguration sourceConnInfo Nothing sqlGenCtx = SQLGenCtx soStringifyNum diff --git a/server/src-lib/Hasura/Backends/Postgres/Connection.hs b/server/src-lib/Hasura/Backends/Postgres/Connection.hs index b55d435433b..f6956c0a2ad 100644 --- a/server/src-lib/Hasura/Backends/Postgres/Connection.hs +++ b/server/src-lib/Hasura/Backends/Postgres/Connection.hs @@ -349,8 +349,9 @@ getDefaultPGPoolSettingIfNotExists connSettings defaultPgPoolSettings = data PostgresSourceConnInfo = PostgresSourceConnInfo - { _psciDatabaseUrl :: !UrlConf - , _psciPoolSettings :: !(Maybe PostgresPoolSettings) + { _psciDatabaseUrl :: !UrlConf + , _psciPoolSettings :: !(Maybe PostgresPoolSettings) + , _psciUsePreparedStatements :: !Bool } deriving (Show, Eq, Generic) instance Cacheable PostgresSourceConnInfo instance Hashable PostgresSourceConnInfo @@ -362,6 +363,7 @@ instance FromJSON PostgresSourceConnInfo where PostgresSourceConnInfo <$> o .: "database_url" <*> o .:? "pool_settings" + <*> o .:? "use_prepared_statements" .!= False -- By default preparing statements is OFF for postgres source instance Arbitrary PostgresSourceConnInfo where arbitrary = genericArbitrary diff --git a/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Dependencies.hs b/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Dependencies.hs index 2f9cedd2ab4..6c04eccbe8a 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Dependencies.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Dependencies.hs @@ -81,7 +81,7 @@ pruneDanglingDependents pruneDanglingDependents cache = fmap (M.filter (not . null)) . traverse do partitionEithers . map \(metadataObject, dependency) -> case resolveDependency dependency of Right () -> Right (metadataObject, dependency) - Left errorMessage -> Left (InconsistentObject errorMessage metadataObject) + Left errorMessage -> Left (InconsistentObject errorMessage Nothing metadataObject) where resolveDependency :: SchemaDependency -> Either Text () resolveDependency (SchemaDependency objectId _) = case objectId of diff --git a/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Fields.hs b/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Fields.hs index a2c2e11fb06..e3eb03d837e 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Fields.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema/Cache/Fields.hs @@ -122,7 +122,7 @@ addNonColumnFields = proc ( source This columnInfo -> returnA -< FIColumn columnInfo That (fieldInfo, _) -> returnA -< fieldInfo These columnInfo (_, fieldMetadata) -> do - recordInconsistency -< (fieldMetadata, "field definition conflicts with postgres column") + recordInconsistency -< ((Nothing, fieldMetadata), "field definition conflicts with postgres column") returnA -< FIColumn columnInfo mkRelationshipMetadataObject diff --git a/server/src-lib/Hasura/RQL/DDL/Schema/Source.hs b/server/src-lib/Hasura/RQL/DDL/Schema/Source.hs index 341e3127482..fd9534c41d2 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema/Source.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema/Source.hs @@ -23,13 +23,14 @@ import Hasura.RQL.Types mkPgSourceResolver :: Q.PGLogger -> SourceResolver mkPgSourceResolver pgLogger _ config = runExceptT do env <- lift Env.getEnvironment - let PostgresSourceConnInfo urlConf connSettings = _pccConnectionInfo config + let PostgresSourceConnInfo urlConf connSettings allowPrepare = _pccConnectionInfo config -- If the user does not provide values for the pool settings, then use the default values let (maxConns, idleTimeout, retries) = getDefaultPGPoolSettingIfNotExists connSettings defaultPostgresPoolSettings urlText <- resolveUrlConf env urlConf let connInfo = Q.ConnInfo retries $ Q.CDDatabaseURI $ txtToBs urlText connParams = Q.defaultConnParams{ Q.cpIdleTime = idleTimeout , Q.cpConns = maxConns + , Q.cpAllowPrepare = allowPrepare } pgPool <- liftIO $ Q.initPGPool connInfo connParams pgLogger let pgExecCtx = mkPGExecCtx Q.ReadCommitted pgPool diff --git a/server/src-lib/Hasura/RQL/Types/Metadata/Object.hs b/server/src-lib/Hasura/RQL/Types/Metadata/Object.hs index ad02dcf4721..704bb88b0e1 100644 --- a/server/src-lib/Hasura/RQL/Types/Metadata/Object.hs +++ b/server/src-lib/Hasura/RQL/Types/Metadata/Object.hs @@ -154,7 +154,7 @@ data MetadataObject $(makeLenses ''MetadataObject) data InconsistentMetadata - = InconsistentObject !Text !MetadataObject + = InconsistentObject !Text !(Maybe Value) !MetadataObject | ConflictingObjects !Text ![MetadataObject] | DuplicateObjects !MetadataObjId ![Value] | DuplicateRestVariables !Text !MetadataObject @@ -165,20 +165,20 @@ $(makePrisms ''InconsistentMetadata) getInconsistentRemoteSchemas :: [InconsistentMetadata] -> [RemoteSchemaName] getInconsistentRemoteSchemas = - toListOf (traverse._InconsistentObject._2.moId._MORemoteSchema) + toListOf (traverse._InconsistentObject._3.moId._MORemoteSchema) imObjectIds :: InconsistentMetadata -> [MetadataObjId] imObjectIds = \case - InconsistentObject _ metadata -> [_moId metadata] - ConflictingObjects _ metadatas -> map _moId metadatas - DuplicateObjects objectId _ -> [objectId] - DuplicateRestVariables _ md -> [_moId md] - InvalidRestSegments _ md -> [_moId md] - AmbiguousRestEndpoints _ mds -> take 1 $ map _moId mds -- TODO: Take 1 is a workaround to ensure that conflicts are not reported multiple times per endpoint. + InconsistentObject _ _ metadata -> [_moId metadata] + ConflictingObjects _ metadatas -> map _moId metadatas + DuplicateObjects objectId _ -> [objectId] + DuplicateRestVariables _ md -> [_moId md] + InvalidRestSegments _ md -> [_moId md] + AmbiguousRestEndpoints _ mds -> take 1 $ map _moId mds -- TODO: Take 1 is a workaround to ensure that conflicts are not reported multiple times per endpoint. imReason :: InconsistentMetadata -> Text imReason = \case - InconsistentObject reason _ -> reason + InconsistentObject reason _ _ -> reason ConflictingObjects reason _ -> reason DuplicateObjects objectId _ -> "multiple definitions for " <> moiName objectId DuplicateRestVariables reason _ -> reason @@ -197,17 +197,18 @@ instance ToJSON InconsistentMetadata where toJSON inconsistentMetadata = object (("reason" .= imReason inconsistentMetadata) : extraFields) where extraFields = case inconsistentMetadata of - InconsistentObject _ metadata -> metadataObjectFields metadata + InconsistentObject _ internal metadata -> metadataObjectFields internal metadata ConflictingObjects _ metadatas -> - [ "objects" .= map (object . metadataObjectFields) metadatas ] + [ "objects" .= map (object . metadataObjectFields Nothing) metadatas ] DuplicateObjects objectId definitions -> [ "type" .= String (moiTypeName objectId) , "definitions" .= definitions ] - DuplicateRestVariables _ md -> metadataObjectFields md - InvalidRestSegments _ md -> metadataObjectFields md + DuplicateRestVariables _ md -> metadataObjectFields Nothing md + InvalidRestSegments _ md -> metadataObjectFields Nothing md AmbiguousRestEndpoints _ mds -> [ "conflicts" .= map _moDefinition mds ] - metadataObjectFields (MetadataObject objectId definition) = + metadataObjectFields (maybeInternal :: Maybe Value) (MetadataObject objectId definition) = [ "type" .= String (moiTypeName objectId) , "definition" .= definition ] + <> maybe [] (\internal -> ["internal" .= internal]) maybeInternal diff --git a/server/src-lib/Hasura/RQL/Types/SchemaCache/Build.hs b/server/src-lib/Hasura/RQL/Types/SchemaCache/Build.hs index f6e0ce85525..46e1ff6420c 100644 --- a/server/src-lib/Hasura/RQL/Types/SchemaCache/Build.hs +++ b/server/src-lib/Hasura/RQL/Types/SchemaCache/Build.hs @@ -35,7 +35,7 @@ import Control.Lens import Control.Monad.Morph import Control.Monad.Trans.Control (MonadBaseControl) import Control.Monad.Unique -import Data.Aeson (toJSON) +import Data.Aeson (Value, toJSON) import Data.Aeson.TH import Data.List (nub) import Data.Text.Extended @@ -83,13 +83,17 @@ partitionCollectedInfo = in (inconsistencies, dependency:dependencies) recordInconsistency - :: (ArrowWriter (Seq w) arr, AsInconsistentMetadata w) => (MetadataObject, Text) `arr` () -recordInconsistency = first (arr (:[])) >>> recordInconsistencies + :: (ArrowWriter (Seq w) arr, AsInconsistentMetadata w) => ((Maybe Value, MetadataObject), Text) `arr` () +recordInconsistency = first (arr (:[])) >>> recordInconsistencies' recordInconsistencies :: (ArrowWriter (Seq w) arr, AsInconsistentMetadata w) => ([MetadataObject], Text) `arr` () -recordInconsistencies = proc (metadataObjects, reason) -> - tellA -< Seq.fromList $ map (review _InconsistentMetadata . InconsistentObject reason) metadataObjects +recordInconsistencies = first (arr (map (Nothing,))) >>> recordInconsistencies' + +recordInconsistencies' + :: (ArrowWriter (Seq w) arr, AsInconsistentMetadata w) => ([(Maybe Value, MetadataObject)], Text) `arr` () +recordInconsistencies' = proc (metadataObjects, reason) -> + tellA -< Seq.fromList $ map (review _InconsistentMetadata . uncurry (InconsistentObject reason)) metadataObjects recordDependencies :: (ArrowWriter (Seq CollectedInfo) arr) @@ -105,7 +109,7 @@ withRecordInconsistency f = proc (e, (metadataObject, s)) -> do result <- runErrorA f -< (e, s) case result of Left err -> do - recordInconsistency -< (metadataObject, qeError err) + recordInconsistency -< ((qeInternal err, metadataObject), qeError err) returnA -< Nothing Right v -> returnA -< Just v {-# INLINABLE withRecordInconsistency #-}