git.iko.soy/rowtype-yoga/purescript-yoga-postgres
on main help sign in
Add extended type support and integration tests for Jsonb, DateTime, BigInt, Array
commit2a4221352a7b85a9f72b42cb80acd96d29561078 authorMark <mark.eibes@gmail.com> dateFri 13 Feb 2026, 19:45 (8 months ago) parent10c5d60
3 files changed+71−6
src/Yoga/Postgres/Schema.purs+8−2 test/Integration.purs+2 test/Schema.purs+61−4
src/Yoga/Postgres/Schema.purs+8−2
@@ -5,6 +5,7 @@ import Prelude
55import Data.Array as Array
66import Data.Array (intercalate, mapWithIndex, foldl)
77import Data.DateTime (DateTime)
8+import Data.JSDate as JSDate
89import Data.Maybe (Maybe(..))
910import Data.Newtype (class Newtype)
1011import Data.Nullable (toNullable)
@@ -70,6 +71,9 @@ newtype Jsonb = Jsonb Foreign
7071
7172derive instance Newtype Jsonb _
7273
74+instance ReadForeign Jsonb where
75+ readImpl = pure <<< Jsonb
76+
7377-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
7478-- Nullability: inferred from Maybe
7579-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -884,8 +888,8 @@ else instance FlushWhereWord "IN" currentType cols paramsIn (Array currentType)
884888else instance FlushWhereWord "TRUE" currentType cols paramsIn currentType paramsIn
885889else instance FlushWhereWord "FALSE" currentType cols paramsIn currentType paramsIn
886890else instance FlushWhereWord "BETWEEN" currentType cols paramsIn currentType paramsIn
887−else instance FlushWhereWord "ANY" currentType cols paramsIn currentType paramsIn
888−else instance FlushWhereWord "ALL" currentType cols paramsIn currentType paramsIn
891+else instance FlushWhereWord "ANY" currentType cols paramsIn (Array currentType) paramsIn
892+else instance FlushWhereWord "ALL" currentType cols paramsIn (Array currentType) paramsIn
889893else instance FlushWhereWord "CAST" currentType cols paramsIn currentType paramsIn
890894else instance FlushWhereWord "AS" currentType cols paramsIn currentType paramsIn
891895else instance FlushWhereWord "EXISTS" currentType cols paramsIn currentType paramsIn
@@ -1068,6 +1072,8 @@ class FieldToPGValue a where
10681072
10691073instance FieldToPGValue a => FieldToPGValue (Maybe a) where
10701074 fieldToPGValue = toNullable >>> unsafeCoerce
1075+else instance FieldToPGValue DateTime where
1076+ fieldToPGValue = JSDate.fromDateTime >>> unsafeCoerce
10711077else instance FieldToPGValue a where
10721078 fieldToPGValue = unsafeCoerce
10731079
test/Integration.purs+2
@@ -21,7 +21,9 @@ main = launchAff_ do
2121 , password: PG.PostgresPassword "postgres"
2222 }
2323 _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS users") conn
24+ _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS events") conn
2425 _ <- PG.executeSimple (PG.SQL (createTableDDL @Schema.UsersTable)) conn
26+ _ <- PG.executeSimple (PG.SQL (createTableDDL @Schema.EventsTable)) conn
2527 _ <- PG.execute (PG.SQL "INSERT INTO users (name, email, age) VALUES ($1, $2, $3)")
2628 [ PG.toPGValue "Alice", PG.toPGValue "alice@example.com", PG.toPGValue 22 ]
2729 conn
test/Schema.purs+61−4
@@ -3,14 +3,19 @@ module Test.Postgres.Schema where
33import Prelude
44
55import Data.Array as Array
6−import Data.DateTime (DateTime)
7−import Data.Maybe (Maybe(..))
6+import Data.Date (canonicalDate)
7+import Data.DateTime (DateTime(..))
8+import Data.Enum (toEnum)
9+import Data.Maybe (Maybe(..), fromJust)
10+import Data.Time (Time(..))
11+import Partial.Unsafe (unsafePartial)
812import Data.String (contains, Pattern(..))
913import Data.Tuple.Nested (type (/\))
1014import Effect.Aff (Aff)
1115import Effect.Class (liftEffect)
12−import Foreign (Foreign)
16+import Foreign (Foreign, unsafeToForeign)
1317import JS.BigInt (BigInt)
18+import JS.BigInt as BigInt
1419import Prim.Boolean (True)
1520import Test.Spec (Spec, around, describe, it)
1621import Test.Spec.Assertions (shouldEqual, shouldSatisfy)
@@ -103,7 +108,18 @@ typedTitleLike = from eventsTable # selectAll # where_ @"title LIKE $title"
103108
104109typedArrayWhere
105110 :: Q "events" _ _ (id :: Array Int) _
106−typedArrayWhere = from eventsTable # selectAll # where_ @"id IN $id"
111+typedArrayWhere = from eventsTable # selectAll # where_ @"id = ANY($id)"
112+
113+testDateTime :: DateTime
114+testDateTime = unsafePartial do
115+ let year = fromJust (toEnum 2025)
116+ let month = fromJust (toEnum 1)
117+ let day = fromJust (toEnum 15)
118+ let hour = fromJust (toEnum 12)
119+ let minute = fromJust (toEnum 0)
120+ let second = fromJust (toEnum 0)
121+ let ms = fromJust (toEnum 0)
122+ DateTime (canonicalDate year month day) (Time hour minute second ms)
107123
108124-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
109125-- Phase 6: Type-safe UPDATE
@@ -447,6 +463,47 @@ integrationSpec conn = do
447463 # runQueryOne conn { name: "Nobody" }
448464 result `shouldEqual` Nothing
449465
466+ describe "Extended types execution" do
467+ it "inserts and selects Jsonb, Array, DateTime, BigInt" do
468+ _ <- from eventsTable
469+ # insert
470+ { title: "Launch"
471+ , metadata: Jsonb (unsafeToForeign { key: "value", nested: { a: 1 } })
472+ , tags: [ "release", "v1" ]
473+ , created_at: testDateTime
474+ , view_count: BigInt.fromInt 42
475+ }
476+ # runExecute conn {}
477+ rows <- from eventsTable
478+ # select @"title, tags, view_count"
479+ # where_ @"title = $title"
480+ # runQuery conn { title: "Launch" }
481+ Array.length rows `shouldEqual` 1
482+ (map _.title rows) `shouldEqual` [ "Launch" ]
483+
484+ it "inserts with returningAll" do
485+ rows <- from eventsTable
486+ # insert { title: "Minimal", metadata: Jsonb (unsafeToForeign {}), tags: ([] :: Array String), created_at: testDateTime, view_count: BigInt.fromInt 0 }
487+ # returning @"id, title"
488+ # runQuery conn {}
489+ Array.length rows `shouldEqual` 1
490+ (map _.title rows) `shouldEqual` [ "Minimal" ]
491+
492+ it "WHERE with JSONB @> operator" do
493+ rows <- from eventsTable
494+ # select @"title"
495+ # where_ @"metadata @> $metadata"
496+ # runQuery conn { metadata: Jsonb (unsafeToForeign { key: "value" }) }
497+ Array.length rows `shouldEqual` 1
498+ (map _.title rows) `shouldEqual` [ "Launch" ]
499+
500+ it "WHERE with = ANY() array param" do
501+ rows <- from usersTable
502+ # select @"name"
503+ # where_ @"id = ANY($id)"
504+ # runQuery conn { id: [ 1, 2 ] }
505+ Array.length rows `shouldSatisfy` (_ >= 1)
506+
450507 it "complex where with multiple params" do
451508 rows <- from usersTable
452509 # selectAll