Open
Description
The code at the bottom queries the persons
table to find each unique valid_from
day paired with the next unique valid_from
day. However, the generated queries use DISTINCT
incorrectly.
This could be related to #531.
Expected result
The unique days are 2025-01-01
, 2025-01-02
and 2025-01-03
. Pairing each day with the next should give:
(2025-01-01,Just 2025-02-02)
(2025-01-02,Just 2025-03-03)
(2025-01-03,Nothing)
This is equivalent to what the following query produces:
WITH commits AS (
SELECT DISTINCT valid_from FROM persons
)
SELECT valid_from, LEAD(valid_from)
OVER (ORDER BY valid_from ASC) FROM commits
Actual result with pgNubBy_ id
(2025-01-01,Just 2025-01-01)
(2025-01-02,Just 2025-01-02)
(2025-01-03,Just 2025-01-03)
Actual result with nub_
(2025-01-03,Nothing)
(2025-01-02,Just 2025-01-03)
(2025-01-01,Just 2025-01-01)
(2025-01-01,Just 2025-01-02)
(2025-01-03,Just 2025-01-03)
(2025-01-02,Just 2025-01-02)
Code
#!/usr/bin/env stack
{- stack script
--snapshot lts-23.0
--system-ghc
--package beam-core
--package beam-postgres
--package postgresql-simple
--package text
--package time
-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Coerce (coerce)
import Data.Text (Text)
import Data.Time.Calendar (Day)
import Database.Beam
import Database.Beam.Postgres
import Database.PostgreSQL.Simple (Query, execute_)
data PersonT f = Person
{ name :: C f Text
, title :: C f Text
, valid_from :: C f Day
}
deriving (Generic)
type Person = PersonT Identity
type PersonExpr s = PersonT (QExpr Postgres s)
deriving instance Show Person
deriving instance Eq Person
instance Beamable PersonT
instance Table PersonT where
data PrimaryKey PersonT f = PersonKey (C f Text)
deriving stock (Generic)
deriving anyclass (Beamable)
primaryKey Person{name} = PersonKey name
data Db f = Db
{ persons :: f (TableEntity PersonT)
}
deriving (Generic)
instance Database Postgres Db
db :: DatabaseSettings Postgres Db
db = defaultDbSettings
-- | All unique days from @persons@
commits :: Q Postgres Db s (QGenExpr QValueContext Postgres s Day)
-- commits = nub_ $ fmap valid_from $ all_ $ persons db
commits = pgNubBy_ id $ fmap valid_from $ all_ $ persons db
-- For fixing the type of `lead1_`; see <https://github.com/haskell-beam/beam/issues/745>
fixType :: QExpr Postgres s a -> QExpr Postgres s (Maybe a)
fixType = coerce
-- | The result of 'commits' each paired with the day of the next commit
commitsWithValidTo ::
Q Postgres Db s
( QGenExpr QValueContext Postgres s Day
, QGenExpr QValueContext Postgres s (Maybe Day)
)
commitsWithValidTo =
fmap fixType <$>
withWindow_
( \valid_from ->
frame_
noPartition_
(orderPartitionBy_ (asc_ valid_from))
noBounds_
)
( \valid_from w ->
(valid_from, lead1_ valid_from `over_` w)
)
commits
main = do
conn <- connect defaultConnectInfo {connectDatabase = "test"}
execute_ conn
$ "create table if not exists persons"
<> "(name varchar, title varchar, valid_from date)"
execute_ conn
$ "delete from persons"
execute_ conn
$ "insert into persons (name, title, valid_from) values"
-- Commit 1
<> "('Janne', 'Mechanic', '2025-01-01'),"
<> "('Jenny', 'Consultant', '2025-01-01'),"
-- Commit 2
<> "('Janne', 'Cleaner', '2025-01-02'),"
<> "('Jenny', 'Assistant', '2025-01-02'),"
-- Commit 3
<> "('Janne', 'CEO', '2025-01-03'),"
<> "('Jenny', 'CFO', '2025-01-03')"
allCommits <- runBeamPostgresDebug putStrLn conn $
runSelectReturningList $ select commits
allCommitsWithValidTo <- runBeamPostgresDebug putStrLn conn $
runSelectReturningList $ select commitsWithValidTo
putStrLn "----"
mapM_ print allCommits
putStrLn "----"
mapM_ print allCommitsWithValidTo