packages feed

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 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 $