🐿️ Type safe SQL in Gleam
72

Configure Feed

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

generate functions in a reproducible order

Giacomo Cavalieri (Jul 28, 2025, 11:46 PM +0200) 41d4fd2d b80ec3dc

+83
+4
CHANGELOG.md
··· 9 9 with a `do` block. 10 10 ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 11 11 12 + - Fixed a bug where queries would not be generated in a reproducible order, 13 + causing `squirrel check` to fail unexpectedly. 14 + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 15 + 12 16 ## 4.0.1 - 2025-07-14 13 17 14 18 - Fixed a bug where using numeric types in a query would result in runtime
+64
birdie_snapshots/queries_are_sorted_alphabetically.accepted
··· 1 + --- 2 + version: 1.3.2 3 + title: queries are sorted alphabetically 4 + file: ./test/squirrel_test.gleam 5 + test_name: queries_are_sorted_alphabetically_test 6 + --- 7 + import gleam/dynamic/decode 8 + import pog 9 + 10 + /// A row you get from running the `first` query 11 + /// defined in `first.sql`. 12 + /// 13 + /// > 🐿️ This type definition was generated automatically using v-test of the 14 + /// > [squirrel package](https://github.com/giacomocavalieri/squirrel). 15 + /// 16 + pub type FirstRow { 17 + FirstRow(wibble: Int) 18 + } 19 + 20 + /// Runs the `first` query 21 + /// defined in `first.sql`. 22 + /// 23 + /// > 🐿️ This function was generated automatically using v-test of 24 + /// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). 25 + /// 26 + pub fn first(db) { 27 + let decoder = { 28 + use wibble <- decode.field(0, decode.int) 29 + decode.success(FirstRow(wibble:)) 30 + } 31 + 32 + "select 1 as wibble" 33 + |> pog.query 34 + |> pog.returning(decoder) 35 + |> pog.execute(db) 36 + } 37 + 38 + /// A row you get from running the `last` query 39 + /// defined in `last.sql`. 40 + /// 41 + /// > 🐿️ This type definition was generated automatically using v-test of the 42 + /// > [squirrel package](https://github.com/giacomocavalieri/squirrel). 43 + /// 44 + pub type LastRow { 45 + LastRow(wibble: Int) 46 + } 47 + 48 + /// Runs the `last` query 49 + /// defined in `last.sql`. 50 + /// 51 + /// > 🐿️ This function was generated automatically using v-test of 52 + /// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). 53 + /// 54 + pub fn last(db) { 55 + let decoder = { 56 + use wibble <- decode.field(0, decode.int) 57 + decode.success(LastRow(wibble:)) 58 + } 59 + 60 + "select 1 as wibble" 61 + |> pog.query 62 + |> pog.returning(decoder) 63 + |> pog.execute(db) 64 + }
+7
src/squirrel/internal/query.gleam
··· 357 357 /// Generates the code for a single file containing a bunch of typed queries. 358 358 /// 359 359 pub fn generate_code(queries: List(TypedQuery), version: String) -> String { 360 + // We need to sort the queries before generating any code, otherwise the order 361 + // with which they will appear in the generated file won't be reproducible! 362 + // That could cause CI checks like `gleam run -m squirrel check` to fail. 363 + // 364 + let queries = 365 + list.sort(queries, fn(one, other) { string.compare(one.file, other.file) }) 366 + 360 367 let #(state, queries_docs) = { 361 368 let state = default_codegen_state() 362 369 use #(state, docs), query <- list.fold(over: queries, from: #(state, []))
+8
test/squirrel_test.gleam
··· 623 623 ) 624 624 } 625 625 626 + pub fn queries_are_sorted_alphabetically_test() { 627 + should_codegen_queries([ 628 + #("last", "select 1 as wibble"), 629 + #("first", "select 1 as wibble"), 630 + ]) 631 + |> birdie.snap(title: "queries are sorted alphabetically") 632 + } 633 + 626 634 // --- ERRROR TESTS ------------------------------------------------------------ 627 635 // This is a group of tests to ensure that the errors look good when something 628 636 // goes wrong.