git.iko.soy/rowtype-yoga/purescript-yoga-postgres
on main help sign in
Intermediate changes
commit83fd46d4ab0f5ccf6d719387a26bc490780ac0f7 authorMark <mark.eibes@gmail.com> dateMon 16 Feb 2026, 00:42 (8 months ago) parentbbfdc0a
7 files changed+112−11
bun.lock+3 docker-compose.test.yml+14 package.json+2−1 src/Yoga/Postgres/Schema.purs+4−4 test/Integration.purs+11−3 test/Main.purs+2−2 test/Schema.purs+76−1
bun.lock+3
@@ -6,6 +6,7 @@
66 "dependencies": {
77 "pg": "^8.11.0",
88 "postgres": "^3.4.8",
9+ "uuid": "^13.0.0",
910 },
1011 },
1112 },
@@ -38,6 +39,8 @@
3839
3940 "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
4041
42+ "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
43+
4144 "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
4245 }
4346}
docker-compose.test.ymlnew file+14
@@ -0,0 +1,14 @@
1+services:
2+ postgres:
3+ image: postgres:17
4+ environment:
5+ POSTGRES_USER: postgres
6+ POSTGRES_PASSWORD: postgres
7+ POSTGRES_DB: test_playground
8+ ports:
9+ - "5433:5432"
10+ healthcheck:
11+ test: ["CMD-SHELL", "pg_isready -U postgres"]
12+ interval: 2s
13+ timeout: 5s
14+ retries: 10
package.json+2−1
@@ -25,6 +25,7 @@
2525 },
2626 "dependencies": {
2727 "pg": "^8.11.0",
28− "postgres": "^3.4.8"
28+ "postgres": "^3.4.8",
29+ "uuid": "^13.0.0"
2930 }
3031}
src/Yoga/Postgres/Schema.purs+4−4
@@ -1766,10 +1766,10 @@ else instance FlushWhereWordT "tsvector" currentType tables paramsIn currentType
17661766else instance FlushWhereWordT "TSVECTOR" currentType tables paramsIn currentType paramsIn
17671767else instance FlushWhereWordT "tsquery" currentType tables paramsIn currentType paramsIn
17681768else instance FlushWhereWordT "TSQUERY" currentType tables paramsIn currentType paramsIn
1769−else instance FlushWhereWordT "to_tsvector" currentType tables paramsIn currentType paramsIn
1770−else instance FlushWhereWordT "TO_TSVECTOR" currentType tables paramsIn currentType paramsIn
1771−else instance FlushWhereWordT "to_tsquery" currentType tables paramsIn currentType paramsIn
1772−else instance FlushWhereWordT "TO_TSQUERY" currentType tables paramsIn currentType paramsIn
1769+else instance FlushWhereWordT "to_tsvector" currentType tables paramsIn String paramsIn
1770+else instance FlushWhereWordT "TO_TSVECTOR" currentType tables paramsIn String paramsIn
1771+else instance FlushWhereWordT "to_tsquery" currentType tables paramsIn String paramsIn
1772+else instance FlushWhereWordT "TO_TSQUERY" currentType tables paramsIn String paramsIn
17731773else instance
17741774 ( ResolveColumn word tables entry
17751775 , ExtractType entry typ
test/Integration.purs+11−3
@@ -20,12 +20,20 @@ main = launchAff_ do
2020 , username: PG.PostgresUsername "postgres"
2121 , password: PG.PostgresPassword "postgres"
2222 }
23− _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS posts") conn
24− _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS users") conn
25− _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS events") conn
23+ _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS sessions CASCADE") conn
24+ _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS articles CASCADE") conn
25+ _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS locations CASCADE") conn
26+ _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS schedule CASCADE") conn
27+ _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS posts CASCADE") conn
28+ _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS users CASCADE") conn
29+ _ <- PG.executeSimple (PG.SQL "DROP TABLE IF EXISTS events CASCADE") conn
2630 _ <- PG.executeSimple (PG.SQL (createTableDDL @Schema.UsersTable)) conn
2731 _ <- PG.executeSimple (PG.SQL (createTableDDL @Schema.EventsTable)) conn
2832 _ <- PG.executeSimple (PG.SQL (createTableDDL @Schema.PostsTable)) conn
33+ _ <- PG.executeSimple (PG.SQL (createTableDDL @Schema.SessionsTable)) conn
34+ _ <- PG.executeSimple (PG.SQL (createTableDDL @Schema.ScheduleTable)) conn
35+ _ <- PG.executeSimple (PG.SQL (createTableDDL @Schema.LocationsTable)) conn
36+ _ <- PG.executeSimple (PG.SQL (createTableDDL @Schema.ArticlesTable)) conn
2937 _ <- PG.execute (PG.SQL "INSERT INTO users (name, email, age) VALUES ($1, $2, $3)")
3038 [ PG.toPGValue "Alice", PG.toPGValue "alice@example.com", PG.toPGValue 22 ]
3139 conn
test/Main.purs+2−2
@@ -433,11 +433,11 @@ main = launchAff_ do
433433 bracket
434434 ( do
435435 liftEffect $ log "Starting Postgres and waiting for it to be ready..."
436− Docker.startService "packages/yoga-postgres/docker-compose.test.yml" 30
436+ Docker.startService "docker-compose.test.yml" 30
437437 liftEffect $ log "Postgres is ready!\n"
438438 )
439439 ( \_ -> do
440− Docker.stopService "packages/yoga-postgres/docker-compose.test.yml"
440+ Docker.stopService "docker-compose.test.yml"
441441 liftEffect $ log "Cleanup complete\n"
442442 )
443443 (\_ -> runSpec [ consoleReporter ] spec)
test/Schema.purs+76−1
@@ -2,11 +2,15 @@ module Test.Postgres.Schema where
22
33import Prelude
44
5+import Data.Array as Array
56import Data.Date (canonicalDate)
67import Data.DateTime (DateTime(..))
78import Data.Enum (toEnum)
89import Data.Maybe (Maybe(..), fromJust)
10+import Data.Newtype (un)
11+import Data.String as String
912import Data.Time (Time(..))
13+import Data.UUID as UUID
1014import Partial.Unsafe (unsafePartial)
1115
1216import Type.Function (type (#))
@@ -735,7 +739,7 @@ articlesDDL :: String
735739articlesDDL = createTableDDL @ArticlesTable
736740
737741typedFTSWhere
738− :: Q _ _ _ _
742+ :: Q _ _ (q :: String) _
739743typedFTSWhere = from articlesTable # selectAll # where_ @"body_vector @@ to_tsquery($q)"
740744
741745-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -1343,3 +1347,74 @@ integrationSpec conn = do
13431347 # orderBy @"name"
13441348 # runQuery conn { age: 10, minCount: 0 }
13451349 rows `shouldEqual` [ { name: "Alice" }, { name: "Bob" } ]
1350+
1351+ describe "UUID + DefaultExpr execution" do
1352+ it "creates sessions table from DDL" do
1353+ _ <- PG.executeSimple (PG.SQL "DELETE FROM sessions") conn
1354+ pure unit
1355+
1356+ it "inserts with server-generated UUID and timestamp" do
1357+ _ <- PG.executeSimple (PG.SQL "DELETE FROM sessions") conn
1358+ count <- from sessionsTable
1359+ # insert { user_id: 1, token: "sess-abc" }
1360+ # runExecute conn {}
1361+ count `shouldEqual` 1
1362+
1363+ it "selects back UUID column" do
1364+ rows <- from sessionsTable
1365+ # select @"id, token"
1366+ # where_ @"token = $token"
1367+ # runQuery conn { token: "sess-abc" }
1368+ let len = Array.length rows
1369+ len `shouldEqual` 1
1370+ do
1371+ let row = unsafePartial fromJust (Array.head rows)
1372+ let uuidStr = UUID.toString (un PGUUID row.id)
1373+ String.length uuidStr `shouldEqual` 36
1374+ row.token `shouldEqual` "sess-abc"
1375+
1376+ describe "PGDate + PGTime execution" do
1377+ it "inserts and round-trips date and time" do
1378+ _ <- PG.executeSimple (PG.SQL "DELETE FROM schedule") conn
1379+ _ <- PG.executeSimple (PG.SQL "ALTER SEQUENCE schedule_id_seq RESTART WITH 1") conn
1380+ _ <- PG.execute
1381+ (PG.SQL "INSERT INTO schedule (event_date, start_time, title) VALUES ($1::date, $2::time, $3)")
1382+ [ PG.toPGValue "2025-06-15", PG.toPGValue "14:30:00", PG.toPGValue "Meeting" ]
1383+ conn
1384+ rows <- from scheduleTable
1385+ # select @"title"
1386+ # where_ @"title = $title"
1387+ # runQuery conn { title: "Meeting" }
1388+ rows `shouldEqual` [ { title: "Meeting" } ]
1389+
1390+ describe "Point execution" do
1391+ it "inserts and selects point values" do
1392+ _ <- PG.executeSimple (PG.SQL "DELETE FROM locations") conn
1393+ _ <- PG.executeSimple (PG.SQL "ALTER SEQUENCE locations_id_seq RESTART WITH 1") conn
1394+ _ <- PG.execute
1395+ (PG.SQL "INSERT INTO locations (name, coords) VALUES ($1, point($2, $3))")
1396+ [ PG.toPGValue "HQ", PG.toPGValue 40.7, PG.toPGValue (-74.0) ]
1397+ conn
1398+ rows <- from locationsTable
1399+ # select @"name"
1400+ # where_ @"name = $name"
1401+ # runQuery conn { name: "HQ" }
1402+ rows `shouldEqual` [ { name: "HQ" } ]
1403+
1404+ describe "TSVector + TSQuery execution" do
1405+ it "inserts and queries with full-text search" do
1406+ _ <- PG.executeSimple (PG.SQL "DELETE FROM articles") conn
1407+ _ <- PG.executeSimple (PG.SQL "ALTER SEQUENCE articles_id_seq RESTART WITH 1") conn
1408+ _ <- PG.execute
1409+ (PG.SQL "INSERT INTO articles (title, body_vector) VALUES ($1, to_tsvector($2))")
1410+ [ PG.toPGValue "PureScript Rocks", PG.toPGValue "purescript is a strongly typed functional language" ]
1411+ conn
1412+ _ <- PG.execute
1413+ (PG.SQL "INSERT INTO articles (title, body_vector) VALUES ($1, to_tsvector($2))")
1414+ [ PG.toPGValue "Haskell Guide", PG.toPGValue "haskell is a lazy functional language" ]
1415+ conn
1416+ rows <- from articlesTable
1417+ # select @"title"
1418+ # where_ @"body_vector @@ to_tsquery($q)"
1419+ # runQuery conn { q: "purescript" }
1420+ rows `shouldEqual` [ { title: "PureScript Rocks" } ]