Skip to content

Commit faa06b2

Browse files
authored
Support for the migration framework in beam-duckdb (#812)
1 parent beae307 commit faa06b2

11 files changed

Lines changed: 1579 additions & 19 deletions

File tree

beam-duckdb/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## 0.3.1.0 -- unreleased
44

5+
* Added support for `beam-migrate`: the new `Database.Beam.DuckDB.Migrate`
6+
module exposes a `migrationBackend :: BeamMigrationBackend DuckDB DuckDBM`
7+
value, along with other utilities. This brings DuckDB on par with `beam-postgres`
8+
and `beam-sqlite` for schema verification and migration script generation.
59
* Added file-mode `COPY ... TO 'file'` / `COPY ... FROM 'file'` support
610
via the new `MonadBeamCopyTo` / `MonadBeamCopyFrom` instances on
711
`DuckDBM`. Smart constructors `copyToCSV` / `copyToParquet` /

beam-duckdb/beam-duckdb.cabal

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ common common
3636
-Wredundant-constraints
3737
-fhide-source-paths
3838
-Wpartial-fields
39+
-Wunused-packages
3940
library
4041
import: common
4142
exposed-modules: Database.Beam.DuckDB
43+
Database.Beam.DuckDB.Migrate
4244
other-modules: Database.Beam.DuckDB.Backend
4345
Database.Beam.DuckDB.Connection
4446
Database.Beam.DuckDB.Syntax
@@ -47,7 +49,8 @@ library
4749
Database.Beam.DuckDB.Syntax.Extensions.Copy
4850
Database.Beam.DuckDB.Syntax.Extensions.DataSource
4951
Database.Beam.DuckDB.Syntax.Extensions.InsertOnConflict
50-
build-depends: base >=4.11 && <5
52+
build-depends: aeson >=1.0 && <2.3
53+
, base >=4.11 && <5
5154
, beam-core >=0.11.1 && <0.12
5255
, beam-migrate ^>=0.6
5356
, bytestring >=0.10 && <0.13
@@ -56,6 +59,7 @@ library
5659
, duckdb-simple (>=0.1.2 && <0.1.5) || (>=0.1.5.1 && <0.1.6)
5760
, dlist >=0.8 && <1.1
5861
, free >=4.12 && <5.3
62+
, hashable >=1.1 && <1.6
5963
, scientific ^>=0.3
6064
, text >=1.0 && <2.2
6165
, time >=1.6 && <1.17
@@ -70,13 +74,16 @@ test-suite beam-duckdb-test
7074
Database.Beam.DuckDB.Test.Extensions.DataSource
7175
Database.Beam.DuckDB.Test.Extensions.InsertOnConflict
7276
Database.Beam.DuckDB.Test.Extensions.Returning
77+
Database.Beam.DuckDB.Test.Migrate
7378
Database.Beam.DuckDB.Test.Query
7479
type: exitcode-stdio-1.0
7580
hs-source-dirs: tests
7681
main-is: Main.hs
7782
build-depends: base >=4.11 && <5
7883
, beam-core
7984
, beam-duckdb
85+
, beam-migrate
86+
, bytestring
8087
, duckdb-simple
8188
, hedgehog
8289
, tasty
@@ -85,3 +92,4 @@ test-suite beam-duckdb-test
8592
, temporary
8693
, text
8794
, time
95+
, uuid-types

beam-duckdb/examples/ExamScores.hs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,39 @@
22
{- cabal:
33
build-depends: base >= 4, beam-core, beam-duckdb, duckdb-simple, time, text
44
-}
5-
65
{-# LANGUAGE DeriveAnyClass #-}
76
{-# LANGUAGE DeriveGeneric #-}
87
{-# LANGUAGE ExplicitForAll #-}
98
{-# LANGUAGE FlexibleInstances #-}
9+
{-# LANGUAGE MultiParamTypeClasses #-}
1010
{-# LANGUAGE OverloadedStrings #-}
1111
{-# LANGUAGE StandaloneDeriving #-}
12-
{-# LANGUAGE TypeFamilies #-}
1312
{-# LANGUAGE TypeApplications #-}
14-
{-# LANGUAGE MultiParamTypeClasses #-}
13+
{-# LANGUAGE TypeFamilies #-}
1514

1615
module Main where
1716

17+
import Data.Function
1818
import Data.Int
1919
import qualified Data.List.NonEmpty as NonEmpty
2020
import Data.Text (Text)
2121
import Data.Time
2222
import Database.Beam
2323
import Database.Beam.DuckDB
2424
import Database.DuckDB.Simple
25-
import Data.Function
2625

2726
data ExamT f = Exam
2827
{ _examId :: Columnar f Int32,
2928
_examName :: Columnar f Text,
3029
_examScore :: Columnar f Double,
3130
_examDate :: Columnar f Day
32-
} deriving (Generic, Beamable)
31+
}
32+
deriving (Generic, Beamable)
3333

3434
type Exam = ExamT Identity
3535

3636
deriving instance Show (ExamT Identity)
37+
3738
deriving instance Eq (ExamT Identity)
3839

3940
instance Table ExamT where
@@ -42,7 +43,7 @@ instance Table ExamT where
4243
primaryKey = ExamKey <$> _examId
4344

4445
data ScoresDB f = ScoresDB
45-
{ _scores :: f (DataSourceEntity ExamT) }
46+
{_scores :: f (DataSourceEntity ExamT)}
4647
deriving (Generic)
4748

4849
deriving instance Database DuckDB ScoresDB
@@ -63,11 +64,12 @@ scoresDb =
6364
}
6465

6566
main = do
66-
Just maxScore <- withConnection ":memory:"
67-
$ \conn -> runBeamDuckDB conn
68-
$ runSelectReturningOne
69-
$ select
70-
$ aggregate_
67+
Just maxScore <- withConnection ":memory:" $
68+
\conn ->
69+
runBeamDuckDB conn $
70+
runSelectReturningOne $
71+
select $
72+
aggregate_
7173
(max_ . _examScore)
7274
(allFromDataSource_ (_scores scoresDb))
7375

beam-duckdb/src/Database/Beam/DuckDB/Backend.hs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Data.Functor (($>), (<&>))
1717
import Data.Int (Int16, Int32, Int64, Int8)
1818
import Data.Scientific (Scientific, scientific)
1919
import Data.Text (Text)
20+
import qualified Data.Text as Text
2021
import Data.Time (Day, LocalTime, TimeOfDay, UTCTime)
2122
import Data.UUID.Types (UUID)
2223
import Data.Word (Word16, Word32, Word64, Word8)
@@ -42,6 +43,7 @@ import Database.Beam.DuckDB.Syntax.Builder
4243
parens,
4344
)
4445
import Database.Beam.DuckDB.Syntax.Extensions.Copy (DuckDBCopyFromSyntax, DuckDBCopyToSyntax)
46+
import Database.Beam.Migrate.SQL (BeamMigrateOnlySqlBackend)
4547
import Database.Beam.Query.SQL92 (buildSql92Query')
4648
import Database.Beam.Query.Types (HasQBuilder (..))
4749
import Database.DuckDB.Simple (Null)
@@ -53,6 +55,8 @@ type instance BeamSqlBackendSyntax DuckDB = DuckDBCommandSyntax
5355

5456
instance BeamSqlBackend DuckDB
5557

58+
instance BeamMigrateOnlySqlBackend DuckDB
59+
5660
type instance BeamSqlBackendCopyToSyntax DuckDB = DuckDBCopyToSyntax
5761

5862
type instance BeamSqlBackendCopyFromSyntax DuckDB = DuckDBCopyFromSyntax
@@ -109,6 +113,13 @@ instance FromBackendRow DuckDB Word64
109113

110114
instance FromBackendRow DuckDB Text
111115

116+
instance FromBackendRow DuckDB Char where
117+
fromBackendRow = do
118+
t <- fromBackendRow @DuckDB @Text
119+
case Text.uncons t of
120+
Just (c, _) -> pure c
121+
Nothing -> fail "Need string of size one to parse Char"
122+
112123
instance FromBackendRow DuckDB ByteString
113124

114125
instance FromBackendRow DuckDB UUID

beam-duckdb/src/Database/Beam/DuckDB/Connection.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module Database.Beam.DuckDB.Connection
1616
runBeamDuckDB,
1717
runBeamDuckDBDebug,
1818
runBeamDuckDBDebugString,
19+
liftIOWithHandle,
1920
BeamDuckDBParams (..),
2021
BeamDuckDBRow (..),
2122
)
@@ -88,6 +89,12 @@ runBeamDuckDBDebug debug conn action =
8889
runBeamDuckDBDebugString :: (String -> IO ()) -> Connection -> DuckDBM a -> IO a
8990
runBeamDuckDBDebugString debug = runBeamDuckDBDebug (debug . Text.unpack)
9091

92+
-- | Run an IO action with access to the underlying DuckDB 'Connection'.
93+
-- This is mostly useful for migrate-backend implementors who need to issue
94+
-- raw queries against the connection.
95+
liftIOWithHandle :: (Connection -> IO a) -> DuckDBM a
96+
liftIOWithHandle f = DuckDBM $ ReaderT $ \(_, conn) -> f conn
97+
9198
newtype BeamDuckDBParams = BeamDuckDBParams [SomeField]
9299

93100
instance ToRow BeamDuckDBParams where

0 commit comments

Comments
 (0)