beam-postgres 0.5.6.0 → 0.5.6.1
raw patch · 4 files changed
+66/−5 lines, 4 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Database.Beam.Postgres.Migrate: instance Database.Beam.Migrate.SQL.Tables.FieldReturnType 'GHC.Types.True 'GHC.Types.False Database.Beam.Postgres.Types.Postgres resTy a => Database.Beam.Migrate.SQL.Tables.FieldReturnType 'GHC.Types.False 'GHC.Types.False Database.Beam.Postgres.Types.Postgres resTy (Database.Beam.Postgres.Migrate.PgHasDefault -> a)
- Database.Beam.Postgres.Syntax: instance (TypeError ...) => Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Types.Int
- Database.Beam.Postgres.Syntax: instance (TypeError ...) => Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Types.Word
- Database.Beam.Postgres.Syntax: instance Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Num.Integer.Integer
- Database.Beam.Postgres.Syntax: instance Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Types.Bool
- Database.Beam.Postgres.Syntax: instance Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Types.Double
- Database.Beam.Postgres.Syntax: instance Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Types.Float
- Database.Beam.Postgres.Syntax: instance Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax [GHC.Types.Char]
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgColumnConstraintDefinitionSyntax
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgColumnSchemaSyntax
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgDataTypeDescr
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgDataTypeSyntax
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgEscapeType
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgExpressionSyntax
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgHasEnum
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgIndexOptions
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgNullOrdering
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgSelectLockingOptions
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgSelectLockingStrength
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq Database.Beam.Postgres.Syntax.PgSyntax
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Eq f => GHC.Classes.Eq (Database.Beam.Postgres.Syntax.PgSyntaxF f)
- Database.Beam.Postgres.Syntax: instance GHC.Classes.Ord Database.Beam.Postgres.Syntax.PgEscapeType
+ Database.Beam.Postgres.Migrate: instance Database.Beam.Migrate.SQL.Tables.FieldReturnType 'GHC.Internal.Types.True 'GHC.Internal.Types.False Database.Beam.Postgres.Types.Postgres resTy a => Database.Beam.Migrate.SQL.Tables.FieldReturnType 'GHC.Internal.Types.False 'GHC.Internal.Types.False Database.Beam.Postgres.Types.Postgres resTy (Database.Beam.Postgres.Migrate.PgHasDefault -> a)
+ Database.Beam.Postgres.Syntax: instance (TypeError ...) => Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Internal.Types.Int
+ Database.Beam.Postgres.Syntax: instance (TypeError ...) => Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Internal.Types.Word
+ Database.Beam.Postgres.Syntax: instance Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Internal.Bignum.Integer.Integer
+ Database.Beam.Postgres.Syntax: instance Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Internal.Types.Bool
+ Database.Beam.Postgres.Syntax: instance Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Internal.Types.Double
+ Database.Beam.Postgres.Syntax: instance Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax GHC.Internal.Types.Float
+ Database.Beam.Postgres.Syntax: instance Database.Beam.Backend.SQL.SQL92.HasSqlValueSyntax Database.Beam.Postgres.Syntax.PgValueSyntax [GHC.Internal.Types.Char]
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgColumnConstraintDefinitionSyntax
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgColumnSchemaSyntax
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgDataTypeDescr
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgDataTypeSyntax
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgEscapeType
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgExpressionSyntax
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgHasEnum
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgIndexOptions
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgNullOrdering
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgSelectLockingOptions
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgSelectLockingStrength
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq Database.Beam.Postgres.Syntax.PgSyntax
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Eq f => GHC.Internal.Classes.Eq (Database.Beam.Postgres.Syntax.PgSyntaxF f)
+ Database.Beam.Postgres.Syntax: instance GHC.Internal.Classes.Ord Database.Beam.Postgres.Syntax.PgEscapeType
- Database.Beam.Postgres: PgBox :: {-# UNPACK #-} !PgPoint -> {-# UNPACK #-} !PgPoint -> PgBox
+ Database.Beam.Postgres: PgBox :: PgPoint -> PgPoint -> PgBox
- Database.Beam.Postgres: PgCircle :: {-# UNPACK #-} !PgPoint -> {-# UNPACK #-} !Double -> PgCircle
+ Database.Beam.Postgres: PgCircle :: PgPoint -> Double -> PgCircle
- Database.Beam.Postgres: PgLine :: {-# UNPACK #-} !Double -> {-# UNPACK #-} !Double -> {-# UNPACK #-} !Double -> PgLine
+ Database.Beam.Postgres: PgLine :: Double -> Double -> Double -> PgLine
- Database.Beam.Postgres: PgLineSegment :: {-# UNPACK #-} !PgPoint -> {-# UNPACK #-} !PgPoint -> PgLineSegment
+ Database.Beam.Postgres: PgLineSegment :: PgPoint -> PgPoint -> PgLineSegment
- Database.Beam.Postgres: PgPoint :: {-# UNPACK #-} !Double -> {-# UNPACK #-} !Double -> PgPoint
+ Database.Beam.Postgres: PgPoint :: Double -> Double -> PgPoint
Files
- ChangeLog.md +8/−0
- Database/Beam/Postgres/Connection.hs +7/−4
- beam-postgres.cabal +1/−1
- test/Database/Beam/Postgres/Test/Select.hs +50/−0
ChangeLog.md view
@@ -1,3 +1,11 @@+# 0.5.6.1++## Bug fixes++* Fixed a critical bug introduced by the performance improvements of 0.5.6.0, which would+ result in unexpected type errors when executing database queries.+ While the bug has been fixed, performance remains as good as the 0.5.6.0 release (#803)+ # 0.5.6.0 ## Added features
Database/Beam/Postgres/Connection.hs view
@@ -197,16 +197,16 @@ -- Default batch size is 256 rows, set by 'postgresql-simple' -- 'defaultFoldOptions' (FetchQuantity = Automatic, which resolves to -- 256 in 'Database.PostgreSQL.Simple').- fieldsCache <- newIORef (Nothing :: Maybe (Vector Pg.Field))+ fieldsCache <- newIORef (Nothing :: Maybe (Pg.Result, Vector Pg.Field)) let cachedGetFields :: Pg.Result -> IO (Vector Pg.Field) cachedGetFields res = do cached <- readIORef fieldsCache case cached of- Just fs -> pure fs+ Just (cachedRes, fs) | cachedRes == res -> pure fs _ -> do fs <- getFields res- writeIORef fieldsCache (Just fs)+ writeIORef fieldsCache (Just (res, fs)) pure fs finish x = pure (Right x)@@ -258,7 +258,10 @@ -- Hoist per-query metadata out of the per-row loop: the -- same 'Pg.Result' is used for every row, so 'fields' -- and 'rowCount' are loop-invariant.- fields <- cachedGetFields res+ -- Use getFields directly: unsafeFreeResult below lets libpq+ -- reuse the pointer address, which would cause a false hit in+ -- cachedGetFields on the next query.+ fields <- getFields res Pg.Row rowCount <- Pg.ntuples res let Pg process = mkProcess (Pg (liftF (PgFetchNext id))) runF process (\x _ -> Pg.unsafeFreeResult res >> next x)
beam-postgres.cabal view
@@ -1,5 +1,5 @@ name: beam-postgres-version: 0.5.6.0+version: 0.5.6.1 synopsis: Connection layer between beam and postgres description: Beam driver for <https://www.postgresql.org/ PostgreSQL>, an advanced open-source RDBMS homepage: https://haskell-beam.github.io/beam/user-guide/backends/beam-postgres
test/Database/Beam/Postgres/Test/Select.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE StandaloneDeriving #-} module Database.Beam.Postgres.Test.Select (tests) where @@ -12,6 +13,7 @@ import Test.Tasty.HUnit import Data.UUID (UUID, nil) import qualified Data.UUID.V5 as V5+import Database.PostgreSQL.Simple (execute_) import Database.Beam import Database.Beam.Backend.SQL.SQL92@@ -58,6 +60,7 @@ , testInSelect getConn , testReturningMany getConn , testPgUnnest getConn+ , testFieldsCacheStale getConn ] testPgArrayToJSON :: IO ByteString -> TestTree@@ -205,6 +208,53 @@ SqlSelect Postgres x -> (Pg (Maybe x) -> Pg a) -> Pg a runSelectReturningMany (SqlSelect s) = runReturningMany (selectCmd s)++-- Tables for fieldsCache regression test (PR #797).+-- AlphaT has a Text column; BetaT has an Int32 column.+-- Querying both within a single runBeamPostgres call exposes the+-- stale-cache bug: the second query gets field OID metadata from the+-- first (text OID 25 instead of int4 OID 23), causing a type mismatch.++data AlphaT f = Alpha { alphaName :: C f T.Text } deriving (Generic, Beamable)++instance Table AlphaT where+ data PrimaryKey AlphaT f = AlphaPk (C f T.Text) deriving (Generic, Beamable)+ primaryKey = AlphaPk . alphaName++data BetaT f = Beta { betaValue :: C f Int32 } deriving (Generic, Beamable)++instance Table BetaT where+ data PrimaryKey BetaT f = BetaPk (C f Int32) deriving (Generic, Beamable)+ primaryKey = BetaPk . betaValue++data FieldsCacheDb e = FieldsCacheDb+ { dbAlpha :: e (TableEntity AlphaT)+ , dbBeta :: e (TableEntity BetaT)+ } deriving (Generic, Database Postgres)++fieldsCacheDb :: DatabaseSettings Postgres FieldsCacheDb+fieldsCacheDb = defaultDbSettings++-- | Regression test for the fieldsCache bug introduced in PR #797.+-- withPgDebug keeps a per-invocation IORef (Maybe (Vector Field)).+-- The cache hit arm (Just fs -> pure fs) never validates against the+-- current Pg.Result, so the second SELECT in one runBeamPostgres call+-- inherits stale OID metadata from the first, producing a type-mismatch+-- error when the two queries return different column types.+testFieldsCacheStale :: IO ByteString -> TestTree+testFieldsCacheStale getConn =+ testCase "fieldsCache is not reused across queries with different schemas" $+ withTestPostgres "fields_cache_stale" getConn $ \conn -> do+ execute_ conn "CREATE TABLE alpha (name TEXT NOT NULL)"+ execute_ conn "CREATE TABLE beta (value INT4 NOT NULL)"+ execute_ conn "INSERT INTO alpha VALUES ('hello')"+ execute_ conn "INSERT INTO beta VALUES (42)"+ (names, values) <- runBeamPostgres conn $ do+ ns <- runSelectReturningList $ select $ all_ (dbAlpha fieldsCacheDb)+ vs <- runSelectReturningList $ select $ all_ (dbBeta fieldsCacheDb)+ pure (ns, vs)+ assertEqual "alpha rows" ["hello" :: T.Text] (alphaName <$> names)+ assertEqual "beta rows" [42 :: Int32] (betaValue <$> values) testFunction :: IO ByteString -> String -> (Connection -> Assertion) -> TestTree testFunction getConn name mkAssertion = testCase name $