🐿️ Type safe SQL in Gleam
77

Configure Feed

Select the types of activity you want to include in your feed.

update to pog@4

Giacomo Cavalieri (Jul 7, 2025, 3:49 PM +0200) ac3483a5 df648bca

+84 -55
+9
CHANGELOG.md
··· 1 1 # CHANGELOG 2 2 3 + ## Unreleased 4 + 5 + - Squirrel now uses the `gleam_time` time types for `time`, `date` and 6 + `timestamp`s. 7 + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 8 + 9 + - Updated the `pog` dependency to `>= 4.0.0 and < 5.0.0`. 10 + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 11 + 3 12 ## 3.1.0 - 2025-07-04 4 13 5 14 - You can now use the `PGCONNECT_TIMEOUT` variable to set the maximum time in
+13 -13
README.md
··· 191 191 192 192 The types that are currently supported are: 193 193 194 - | postgres type | encoded as | decoded as | 195 - | ------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | 196 - | `bool` | `Bool` | `Bool` | 197 - | `text`, `char`, `bpchar`, `varchar` | `String` | `String` | 198 - | `float4`, `float8`, `numeric` | `Float` | `Float` | 199 - | `int2`, `int4`, `int8` | `Int` | `Int` | 200 - | `json`, `jsonb` | [`Json`](https://hexdocs.pm/gleam_json/gleam/json.html#Json) | `String` | 201 - | `uuid` | [`Uuid`](https://hexdocs.pm/youid/youid/uuid.html#Uuid) | [`Uuid`](https://hexdocs.pm/youid/youid/uuid.html#Uuid) | 202 - | `bytea` | `BitArray` | `BitArray` | 203 - | `date` | `#(Int, Int, Int)` with `#(year, month, day)` | `#(Int, Int, Int)` with `#(year, month, day)` | 204 - | `timestamp` | `#(#(Int, Int, Int), (#(Int, Int, Int))` with `#(#(year, month, day), #(hour, minute, second))` | `#(#(Int, Int, Int), (#(Int, Int, Int))` with `#(#(year, month, day), #(hour, minute, second))` | 205 - | `<type>[]` (where `<type>` is any supported type) | `List(<type>)` | `List(<type>)` | 206 - | user-defined enum | [Gleam custom type](https://tour.gleam.run/data-types/custom-types/) | [Gleam custom type](https://tour.gleam.run/data-types/custom-types/) | 194 + | postgres type | encoded as | decoded as | 195 + | ------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | 196 + | `bool` | `Bool` | `Bool` | 197 + | `text`, `char`, `bpchar`, `varchar` | `String` | `String` | 198 + | `float4`, `float8`, `numeric` | `Float` | `Float` | 199 + | `int2`, `int4`, `int8` | `Int` | `Int` | 200 + | `json`, `jsonb` | [`json.Json`](https://hexdocs.pm/gleam_json/gleam/json.html#Json) | `String` | 201 + | `uuid` | [`uuid.Uuid`](https://hexdocs.pm/youid/youid/uuid.html#Uuid) | [`uuid.Uuid`](https://hexdocs.pm/youid/youid/uuid.html#Uuid) | 202 + | `bytea` | `BitArray` | `BitArray` | 203 + | `date` | [`calendar.Date`](https://hexdocs.pm/gleam_time/gleam/time/calendar.html#Date) | [`calendar.Date`](https://hexdocs.pm/gleam_time/gleam/time/calendar.html#Date) | 204 + | `timestamp` | [`timestamp.Timestamp`](https://hexdocs.pm/gleam_time/gleam/time/timestamp.html#Timestamp) | [`timestamp.Timestamp`](https://hexdocs.pm/gleam_time/gleam/time/timestamp.html#Timestamp) | 205 + | `<type>[]` (where `<type>` is any supported type) | `List(<type>)` | `List(<type>)` | 206 + | user-defined enum | [Gleam custom type](https://tour.gleam.run/data-types/custom-types/) | [Gleam custom type](https://tour.gleam.run/data-types/custom-types/) | 207 207 208 208 ### Enums 209 209
+4 -3
birdie_snapshots/date_decoding.accepted
··· 1 1 --- 2 - version: 1.3.0 2 + version: 1.3.1 3 3 title: date decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: date_decoding_test 6 6 --- 7 7 import gleam/dynamic/decode 8 + import gleam/time/calendar.{type Date} 8 9 import pog 9 10 10 11 /// A row you get from running the `query` query ··· 14 15 /// > [squirrel package](https://github.com/giacomocavalieri/squirrel). 15 16 /// 16 17 pub type QueryRow { 17 - QueryRow(res: pog.Date) 18 + QueryRow(res: Date) 18 19 } 19 20 20 21 /// Runs the `query` query ··· 25 26 /// 26 27 pub fn query(db) { 27 28 let decoder = { 28 - use res <- decode.field(0, pog.date_decoder()) 29 + use res <- decode.field(0, pog.calendar_date_decoder()) 29 30 decode.success(QueryRow(res:)) 30 31 } 31 32
+2 -2
birdie_snapshots/date_encoding.accepted
··· 1 1 --- 2 - version: 1.3.0 2 + version: 1.3.1 3 3 title: date encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: date_encoding_test ··· 31 31 32 32 "select true as res where $1 = 'Jan-2-1970'::date" 33 33 |> pog.query 34 - |> pog.parameter(pog.date(arg_1)) 34 + |> pog.parameter(pog.calendar_date(arg_1)) 35 35 |> pog.returning(decoder) 36 36 |> pog.execute(db) 37 37 }
+4 -3
birdie_snapshots/query_that_needs_more_than_a_single_helper_function.accepted
··· 1 1 --- 2 - version: 1.3.0 2 + version: 1.3.1 3 3 title: query that needs more than a single helper function 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: query_that_needs_more_than_a_single_helper_function_test 6 6 --- 7 7 import gleam/dynamic/decode 8 + import gleam/time/calendar.{type Date} 8 9 import pog 9 10 import youid/uuid.{type Uuid} 10 11 ··· 15 16 /// > [squirrel package](https://github.com/giacomocavalieri/squirrel). 16 17 /// 17 18 pub type QueryRow { 18 - QueryRow(gen_random_uuid: Uuid, date: pog.Date) 19 + QueryRow(gen_random_uuid: Uuid, date: Date) 19 20 } 20 21 21 22 /// Runs the `query` query ··· 27 28 pub fn query(db) { 28 29 let decoder = { 29 30 use gen_random_uuid <- decode.field(0, uuid_decoder()) 30 - use date <- decode.field(1, pog.date_decoder()) 31 + use date <- decode.field(1, pog.calendar_date_decoder()) 31 32 decode.success(QueryRow(gen_random_uuid:, date:)) 32 33 } 33 34
+4 -3
birdie_snapshots/query_that_needs_utils_and_enums_has_two_sections.accepted
··· 1 1 --- 2 - version: 1.3.0 2 + version: 1.3.1 3 3 title: query that needs utils and enums has two sections 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: query_that_needs_utils_and_enums_has_two_sections_test 6 6 --- 7 7 import gleam/dynamic/decode 8 + import gleam/time/calendar.{type Date} 8 9 import pog 9 10 10 11 /// A row you get from running the `query` query ··· 14 15 /// > [squirrel package](https://github.com/giacomocavalieri/squirrel). 15 16 /// 16 17 pub type QueryRow { 17 - QueryRow(squirrel_colour: SquirrelColour, date: pog.Date) 18 + QueryRow(squirrel_colour: SquirrelColour, date: Date) 18 19 } 19 20 20 21 /// Runs the `query` query ··· 26 27 pub fn query(db) { 27 28 let decoder = { 28 29 use squirrel_colour <- decode.field(0, squirrel_colour_decoder()) 29 - use date <- decode.field(1, pog.date_decoder()) 30 + use date <- decode.field(1, pog.calendar_date_decoder()) 30 31 decode.success(QueryRow(squirrel_colour:, date:)) 31 32 } 32 33
+3 -2
birdie_snapshots/timestamp_decoding.accepted
··· 1 1 --- 2 - version: 1.3.0 2 + version: 1.3.1 3 3 title: timestamp decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: timestamp_decoding_test 6 6 --- 7 7 import gleam/dynamic/decode 8 + import gleam/time/timestamp.{type Timestamp} 8 9 import pog 9 10 10 11 /// A row you get from running the `query` query ··· 14 15 /// > [squirrel package](https://github.com/giacomocavalieri/squirrel). 15 16 /// 16 17 pub type QueryRow { 17 - QueryRow(res: pog.Timestamp) 18 + QueryRow(res: Timestamp) 18 19 } 19 20 20 21 /// Runs the `query` query
+2 -1
gleam.toml
··· 18 18 gleam_stdlib = ">= 0.51.0 and < 2.0.0" 19 19 justin = ">= 1.0.1 and < 2.0.0" 20 20 non_empty_list = ">= 2.1.0 and < 3.0.0" 21 - pog = ">= 3.1.0 and < 4.0.0" 22 21 simplifile = ">= 2.0.1 and < 3.0.0" 23 22 term_size = ">= 1.0.1 and < 2.0.0" 24 23 tom = ">= 2.0.0 and < 3.0.0" ··· 26 25 youid = ">= 1.2.0 and < 2.0.0" 27 26 glexer = ">= 2.1.0 and < 3.0.0" 28 27 mug = ">= 3.0.0 and < 4.0.0" 28 + pog = ">= 4.0.0 and < 5.0.0" 29 + gleam_time = ">= 1.2.0 and < 2.0.0" 29 30 30 31 [dev-dependencies] 31 32 birdie = ">= 1.1.8 and < 2.0.0"
+7 -5
manifest.toml
··· 8 8 { name = "edit_distance", version = "2.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "edit_distance", source = "hex", outer_checksum = "A1E485C69A70210223E46E63985FA1008B8B2DDA9848B7897469171B29020C05" }, 9 9 { name = "envoy", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "95FD059345AA982E89A0B6E2A3BF1CF43E17A7048DCD85B5B65D3B9E4E39D359" }, 10 10 { name = "eval", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "eval", source = "hex", outer_checksum = "264DAF4B49DF807F303CA4A4E4EBC012070429E40BE384C58FE094C4958F9BDA" }, 11 - { name = "exception", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "F5580D584F16A20B7FCDCABF9E9BE9A2C1F6AC4F9176FA6DD0B63E3B20D450AA" }, 11 + { name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" }, 12 12 { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" }, 13 13 { name = "glam", version = "2.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glam", source = "hex", outer_checksum = "4932A2D139AB0389E149396407F89654928D7B815E212BB02F13C66F53B1BBA1" }, 14 14 { name = "glance", version = "5.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glexer"], otp_app = "glance", source = "hex", outer_checksum = "FAA3DAC74AF71D47C67D88EB32CE629075169F878D148BB1FF225439BE30070A" }, ··· 17 17 { name = "gleam_crypto", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "50774BAFFF1144E7872814C566C5D653D83A3EBF23ACC3156B757A1B6819086E" }, 18 18 { name = "gleam_erlang", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "F91CE62A2D011FA13341F3723DB7DB118541AAA5FE7311BD2716D018F01EF9E3" }, 19 19 { name = "gleam_json", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "874FA3C3BB6E22DD2BB111966BD40B3759E9094E05257899A7C08F5DE77EC049" }, 20 + { name = "gleam_otp", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "7020E652D18F9ABAC9C877270B14160519FA0856EE80126231C505D719AD68DA" }, 20 21 { name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" }, 21 22 { name = "gleam_stdlib", version = "0.61.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "3DC407D6EDA98FCE089150C11F3AD892B6F4C3CA77C87A97BAE8D5AB5E41F331" }, 22 23 { name = "gleam_time", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "D71F1AFF7FEB534FF55E5DC58E534E9201BA75A444619788A2E4DEA4EBD87D16" }, ··· 26 27 { name = "mug", version = "3.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "mug", source = "hex", outer_checksum = "49AD2B71690C6A615453D272300951C4BDE19FBF55B167D9C951F5CD89FEC820" }, 27 28 { name = "non_empty_list", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "non_empty_list", source = "hex", outer_checksum = "3BF1496B475F3F2CE2EA18157587329812493369B2A7C5B9DCFEA5C007670A3B" }, 28 29 { name = "opentelemetry_api", version = "1.4.0", build_tools = ["rebar3", "mix"], requirements = [], otp_app = "opentelemetry_api", source = "hex", outer_checksum = "3DFBBFAA2C2ED3121C5C483162836C4F9027DEF469C41578AF5EF32589FCFC58" }, 29 - { name = "pg_types", version = "0.4.0", build_tools = ["rebar3"], requirements = [], otp_app = "pg_types", source = "hex", outer_checksum = "B02EFA785CAECECF9702C681C80A9CA12A39F9161A846CE17B01FB20AEEED7EB" }, 30 - { name = "pgo", version = "0.14.0", build_tools = ["rebar3"], requirements = ["backoff", "opentelemetry_api", "pg_types"], otp_app = "pgo", source = "hex", outer_checksum = "71016C22599936E042DC0012EE4589D24C71427D266292F775EBF201D97DF9C9" }, 31 - { name = "pog", version = "3.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "pgo"], otp_app = "pog", source = "hex", outer_checksum = "35CBBE8F844DE9E5676CF306CDE2BD98AA6A25920D2EBE771FD5A293EB454161" }, 30 + { name = "pg_types", version = "0.5.0", build_tools = ["rebar3"], requirements = [], otp_app = "pg_types", source = "hex", outer_checksum = "A3023B464AA960BC1628635081E30CCA4F676F2D4C23CCD6179C1C11C9B4A642" }, 31 + { name = "pgo", version = "0.15.0", build_tools = ["rebar3"], requirements = ["backoff", "opentelemetry_api", "pg_types"], otp_app = "pgo", source = "hex", outer_checksum = "4B883D751B8D4247F4D8A6FBAE58EDB6FA9DBC183371299BF84975EE36406388" }, 32 + { name = "pog", version = "4.0.0", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_otp", "gleam_stdlib", "gleam_time", "pgo"], otp_app = "pog", source = "hex", outer_checksum = "E495AAD2C4FD0BB3A92C505682705FA2460053B2D3ABC6F9024C3FED6868417D" }, 32 33 { name = "rank", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "rank", source = "hex", outer_checksum = "5660E361F0E49CBB714CC57CC4C89C63415D8986F05B2DA0C719D5642FAD91C9" }, 33 34 { name = "shellout", version = "1.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "shellout", source = "hex", outer_checksum = "1BDC03438FEB97A6AF3E396F4ABEB32BECF20DF2452EC9A8C0ACEB7BDDF70B14" }, 34 35 { name = "simplifile", version = "2.3.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0A868DAC6063D9E983477981839810DC2E553285AB4588B87E3E9C96A7FB4CB4" }, ··· 53 54 gleam_json = { version = ">= 1.0.0 and < 4.0.0" } 54 55 gleam_regexp = { version = ">= 1.0.0 and < 2.0.0" } 55 56 gleam_stdlib = { version = ">= 0.51.0 and < 2.0.0" } 57 + gleam_time = { version = ">= 1.2.0 and < 2.0.0" } 56 58 gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 57 59 glexer = { version = ">= 2.1.0 and < 3.0.0" } 58 60 justin = { version = ">= 1.0.1 and < 2.0.0" } 59 61 mug = { version = ">= 3.0.0 and < 4.0.0" } 60 62 non_empty_list = { version = ">= 2.1.0 and < 3.0.0" } 61 - pog = { version = ">= 3.1.0 and < 4.0.0" } 63 + pog = { version = ">= 4.0.0 and < 5.0.0" } 62 64 shellout = { version = ">= 1.6.0 and < 2.0.0" } 63 65 simplifile = { version = ">= 2.0.1 and < 3.0.0" } 64 66 temporary = { version = ">= 1.0.0 and < 2.0.0" }
+11 -4
src/squirrel/internal/query.gleam
··· 234 234 let #(state, inner_decoder) = gleam_type_to_decoder(state, type_) 235 235 #(state, call_doc("decode.optional", [inner_decoder])) 236 236 } 237 - gleam.Date -> #(state, doc.from_string("pog.date_decoder()")) 237 + gleam.Date -> #(state, doc.from_string("pog.calendar_date_decoder()")) 238 238 gleam.Timestamp -> #(state, doc.from_string("pog.timestamp_decoder()")) 239 239 gleam.Int -> #(state, doc.from_string("decode.int")) 240 240 gleam.Float -> #(state, doc.from_string("decode.float")) ··· 284 284 let doc = call_doc("pog.text", [call_doc("json.to_string", [name])]) 285 285 #(state, doc) 286 286 } 287 - gleam.Date -> #(state, call_doc("pog.date", [name])) 287 + gleam.Date -> #(state, call_doc("pog.calendar_date", [name])) 288 288 gleam.Timestamp -> #(state, call_doc("pog.timestamp", [name])) 289 289 gleam.Int -> #(state, call_doc("pog.int", [name])) 290 290 gleam.Float -> #(state, call_doc("pog.float", [name])) ··· 322 322 state |> import_qualified("youid/uuid", "type Uuid"), 323 323 doc.from_string("Uuid"), 324 324 ) 325 - gleam.Date -> #(state, doc.from_string("pog.Date")) 326 - gleam.Timestamp -> #(state, doc.from_string("pog.Timestamp")) 325 + gleam.Date -> { 326 + let state = state |> import_qualified("gleam/time/calendar", "type Date") 327 + #(state, doc.from_string("Date")) 328 + } 329 + gleam.Timestamp -> { 330 + let state = 331 + state |> import_qualified("gleam/time/timestamp", "type Timestamp") 332 + #(state, doc.from_string("Timestamp")) 333 + } 327 334 gleam.Int -> #(state, doc.from_string("Int")) 328 335 gleam.Float -> #(state, doc.from_string("Float")) 329 336 gleam.Bool -> #(state, doc.from_string("Bool"))
+18 -16
test/integration_test.gleam
··· 67 67 // Bytea 68 68 TestType("bytea", [TestValue("<<1, 2, 3>>")]), 69 69 // Date 70 - TestType("date", [TestValue("pog.Date(1998, 10, 11)")]), 70 + TestType("date", [TestValue("calendar.Date(1998, calendar.October, 11)")]), 71 71 // Timestamp 72 - TestType( 73 - "timestamp", 74 - [TestValue("pog.Timestamp(pog.Date(1998, 10, 11), pog.Time(22, 10, 0, 0))")], 75 - ), 72 + TestType("timestamp", [TestValue("timestamp.from_unix_seconds(1000)")]), 76 73 // Array 77 74 TestType("int[]", [TestValue("[1, 2, 3]")]), 78 75 TestType("squirrel_colour[]", [TestValue("[sql.Red, sql.Grey]")]), ··· 274 271 /// 275 272 fn write_main(assertions: String, to dir: String) -> Nil { 276 273 let main = " 274 + import gleam/erlang/process 277 275 import gleam/io 276 + import gleam/json 278 277 import gleam/string 278 + import gleam/time/calendar 279 + import gleam/time/timestamp 279 280 import pog 280 - import gleam/json 281 + import sql 281 282 import youid/uuid 282 - import sql 283 283 284 284 pub fn main() { 285 - let config = 286 - pog.Config( 287 - ..pog.default_config(), 288 - port: 5432, 289 - user: \"squirrel_test\", 290 - host: \"localhost\", 291 - database: \"squirrel_test\", 292 - ) 293 - let db = pog.connect(config) 285 + let name = process.new_name(\"test\") 286 + let config = 287 + pog.Config( 288 + ..pog.default_config(name), 289 + port: 5432, 290 + user: \"squirrel_test\", 291 + host: \"localhost\", 292 + database: \"squirrel_test\", 293 + ) 294 + let assert Ok(actor) = pog.start(config) 295 + let db = actor.data 294 296 295 297 " <> assertions <> " 296 298 }"
+7 -3
test/squirrel_test.gleam
··· 1 1 import birdie 2 2 import filepath 3 3 import glam/doc 4 + import gleam/erlang/process 4 5 import gleam/list 5 6 import gleam/string 6 7 import gleeunit ··· 28 29 const port = 5432 29 30 30 31 pub fn test_config() -> pog.Config { 31 - pog.default_config() 32 + let name = process.new_name("test") 33 + 34 + pog.default_config(name) 32 35 |> pog.port(port) 33 36 |> pog.user(user) 34 37 |> pog.host(host) ··· 36 39 } 37 40 38 41 fn setup_database() { 39 - let db = pog.connect(test_config()) 42 + let assert Ok(actor) = pog.start(test_config()) 43 + let db = actor.data 40 44 41 45 let assert Ok(_) = 42 46 " ··· 176 180 |> pog.query 177 181 |> pog.execute(db) 178 182 179 - pog.disconnect(db) 183 + Nil 180 184 } 181 185 182 186 // --- ASSERTION HELPERS -------------------------------------------------------