···11# CHANGELOG
2233+## Unreleased
44+55+- Fixed a bug where the code generated by Squirrel wouldn't be properly
66+ formatted in case of a query returned rows with many columns.
77+ ([Giacomo Cavalieri](https://github.com/giacomocavalieri))
88+39## v3.0.4 - 2025-05-19
410511- Relaxed the constraint on the `gleam_json` dependency.
···11---
22-version: 1.2.3
22+version: 1.3.0
33title: a query failing does not change the other query's error
44file: ./test/squirrel_test.gleam
55test_name: a_query_failing_does_not_change_the_other_query_error_test
···11---
22-version: 1.2.3
22+version: 1.3.0
33title: a query failing does not change the other query's error 2
44file: ./test/squirrel_test.gleam
55test_name: a_query_failing_does_not_change_the_other_query_error_2_test
···11+---
22+version: 1.3.0
33+title: decode.success with long builder is properly formatted not breaking it on a different line
44+file: ./test/squirrel_test.gleam
55+test_name: decode_success_with_long_builder_is_properly_formatted_not_breaking_it_on_a_different_line_test
66+---
77+import gleam/dynamic/decode
88+import pog
99+1010+/// A row you get from running the `query` query
1111+/// defined in `query.sql`.
1212+///
1313+/// > 🐿️ This type definition was generated automatically using v-test of the
1414+/// > [squirrel package](https://github.com/giacomocavalieri/squirrel).
1515+///
1616+pub type QueryRow {
1717+ QueryRow(
1818+ first_column: Int,
1919+ second_column: Int,
2020+ third_column: Int,
2121+ fourth_column: Int,
2222+ fifth_column: Int,
2323+ sixth_column: Int,
2424+ )
2525+}
2626+2727+/// Runs the `query` query
2828+/// defined in `query.sql`.
2929+///
3030+/// > 🐿️ This function was generated automatically using v-test of
3131+/// > the [squirrel package](https://github.com/giacomocavalieri/squirrel).
3232+///
3333+pub fn query(db) {
3434+ let decoder = {
3535+ use first_column <- decode.field(0, decode.int)
3636+ use second_column <- decode.field(1, decode.int)
3737+ use third_column <- decode.field(2, decode.int)
3838+ use fourth_column <- decode.field(3, decode.int)
3939+ use fifth_column <- decode.field(4, decode.int)
4040+ use sixth_column <- decode.field(5, decode.int)
4141+ decode.success(QueryRow(
4242+ first_column:,
4343+ second_column:,
4444+ third_column:,
4545+ fourth_column:,
4646+ fifth_column:,
4747+ sixth_column:,
4848+ ))
4949+ }
5050+5151+ "select
5252+ 1 as first_column,
5353+ 2 as second_column,
5454+ 3 as third_column,
5555+ 4 as fourth_column,
5656+ 5 as fifth_column,
5757+ 6 as sixth_column;"
5858+ |> pog.query
5959+ |> pog.returning(decoder)
6060+ |> pog.execute(db)
6161+}
···11---
22-version: 1.2.5
22+version: 1.3.0
33title: generated type fields are labelled with their name in the select list
44file: ./test/squirrel_test.gleam
55test_name: generated_type_fields_are_labelled_with_their_name_in_the_select_list_test
···11---
22-version: 1.2.5
22+version: 1.3.0
33title: generated type has the same name as the function but in pascal case
44file: ./test/squirrel_test.gleam
55test_name: generated_type_has_the_same_name_as_the_function_but_in_pascal_case_test
···11---
22-version: 1.2.5
22+version: 1.3.0
33title: insert with no returned values returns just nil and doesnt define a type
44file: ./test/squirrel_test.gleam
55test_name: insert_with_no_returned_values_returns_just_nil_and_doesnt_define_a_type_test
···11---
22-version: 1.2.5
22+version: 1.3.0
33title: query that needs more than a single helper function
44file: ./test/squirrel_test.gleam
55test_name: query_that_needs_more_than_a_single_helper_function_test
···11---
22-version: 1.2.6
22+version: 1.3.0
33title: query that needs utils and enums has two sections
44file: ./test/squirrel_test.gleam
55test_name: query_that_needs_utils_and_enums_has_two_sections_test
···11---
22-version: 1.2.5
22+version: 1.3.0
33title: there is only one empty line between code generated for different queries
44file: ./test/squirrel_test.gleam
55test_name: there_is_only_one_empty_line_between_code_generated_for_different_queries_test
···11---
22-version: 1.2.5
22+version: 1.3.0
33title: using uuids more than once results in a single uuid decoder helper
44file: ./test/squirrel_test.gleam
55test_name: using_uuids_more_than_once_results_in_a_single_uuid_decoder_helper_test
···11---
22-version: 1.2.3
22+version: 1.3.0
33title: when a constraint doesn't exist there should be an error message
44file: ./test/squirrel_test.gleam
55test_name: non_existing_constraint_error_message_test
+49-3
src/squirrel/internal/query.gleam
···735735///
736736const nil_decoder = "decode.map(decode.dynamic, fn(_) { Nil })"
737737738738-/// A pretty printed decoder that decodes an n-item dynamic tuple using the
739739-/// `decode` package.
738738+/// A pretty printed decoder that decodes an n-item dynamic tuple with the given
739739+/// constructor wrapping the returned rows from a query.
740740+///
741741+/// If the query returns no columns (that is `returns == []`), then we default
742742+/// to building decoder that always returns `Nil`.
740743///
741744fn decoder_doc(
742745 state: CodeGenState,
···766769 let labelled_names = list.reverse(labelled_names)
767770768771 let success_line =
769769- call_doc("decode.success", [call_doc(constructor, labelled_names)])
772772+ nested_calls_doc("decode.success", constructor, labelled_names)
773773+770774 let doc = block(list.append(parameters, [success_line]))
771775 #(state, doc)
772776}
···792796///
793797fn call_doc(function: String, args: List(Document)) -> Document {
794798 [doc.from_string(function), comma_list("(", args, ")")]
799799+ |> doc.concat
800800+ |> doc.group
801801+}
802802+803803+/// This is a special case of a call document. To accomodate for a special rule
804804+/// of the Gleam formatter: when we have a function call that has a single other
805805+/// function as its one and only argument.
806806+///
807807+/// ```gleam
808808+/// first(second(arg_1, arg_2, arg_3, ..., arg_n))
809809+/// ```
810810+///
811811+/// When this needs to be broken, the formatter will only split the arguments of
812812+/// the second call like this:
813813+///
814814+/// ```gleam
815815+/// first(second(
816816+/// arg_1,
817817+/// ...,
818818+/// arg_n
819819+/// ))
820820+/// ```
821821+///
822822+/// Given the first and second function, and the arguments of the second
823823+/// function, this function builds a document that behaves like that.
824824+///
825825+fn nested_calls_doc(
826826+ first: String,
827827+ second: String,
828828+ arguments: List(Document),
829829+) -> Document {
830830+ [
831831+ doc.from_string(first),
832832+ doc.from_string("("),
833833+ // ^^ For the first call we don't add any breakable space after the `(`, so
834834+ // that the only thing that can get broken on multiple lines are the
835835+ // arguments of the second function
836836+ call_doc(second, arguments),
837837+ // ^^ And the second call is broken and behaves as usual, with its arguments
838838+ // being nested
839839+ doc.from_string(")"),
840840+ ]
795841 |> doc.concat
796842 |> doc.group
797843}
+15
test/squirrel_test.gleam
···555555 |> birdie.snap(title: "query with quoted string is properly escaped")
556556}
557557558558+// https://github.com/giacomocavalieri/squirrel/issues/77
559559+pub fn decode_success_with_long_builder_is_properly_formatted_not_breaking_it_on_a_different_line_test() {
560560+ "select
561561+ 1 as first_column,
562562+ 2 as second_column,
563563+ 3 as third_column,
564564+ 4 as fourth_column,
565565+ 5 as fifth_column,
566566+ 6 as sixth_column;"
567567+ |> should_codegen
568568+ |> birdie.snap(
569569+ title: "decode.success with long builder is properly formatted not breaking it on a different line",
570570+ )
571571+}
572572+558573// --- ERRROR TESTS ------------------------------------------------------------
559574// This is a group of tests to ensure that the errors look good when something
560575// goes wrong.