🐿️ Type safe SQL in Gleam
77

Configure Feed

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

make sure decode.success with a long builder is properly formatted

Giacomo Cavalieri (Jun 22, 2025, 11:19 AM +0200) c1438883 f6f8cdd4

+189 -61
+6
CHANGELOG.md
··· 1 1 # CHANGELOG 2 2 3 + ## Unreleased 4 + 5 + - Fixed a bug where the code generated by Squirrel wouldn't be properly 6 + formatted in case of a query returned rows with many columns. 7 + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 8 + 3 9 ## v3.0.4 - 2025-05-19 4 10 5 11 - Relaxed the constraint on the `gleam_json` dependency.
+1 -1
birdie_snapshots/a_query_failing_does_not_change_the_other_query's_error.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: a query failing does not change the other query's error 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: a_query_failing_does_not_change_the_other_query_error_test
+1 -1
birdie_snapshots/a_query_failing_does_not_change_the_other_query's_error_2.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: a query failing does not change the other query's error 2 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: a_query_failing_does_not_change_the_other_query_error_2_test
+1 -1
birdie_snapshots/array_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: array decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: array_decoding_test
+1 -1
birdie_snapshots/array_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: array encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: array_encoding_test
+1 -1
birdie_snapshots/bool_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: bool decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: bool_decoding_test
+1 -1
birdie_snapshots/bool_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: bool encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: bool_encoding_test
+1 -1
birdie_snapshots/bytea_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: bytea decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: bytea_decoding_test
+1 -1
birdie_snapshots/bytea_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: bytea encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: bytea_encoding_test
+1 -1
birdie_snapshots/char_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: char decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: char_decoding_test
+1 -1
birdie_snapshots/char_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: char encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: char_encoding_test
+1 -1
birdie_snapshots/column_with_invalid_name.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: column with invalid name 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: column_with_invalid_name_test
+1 -1
birdie_snapshots/date_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: date decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: date_decoding_test
+1 -1
birdie_snapshots/date_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: date encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: date_encoding_test
+61
birdie_snapshots/decode_success_with_long_builder_is_properly_formatted_not_breaking_it_on_a_different_line.accepted
··· 1 + --- 2 + version: 1.3.0 3 + title: decode.success with long builder is properly formatted not breaking it on a different line 4 + file: ./test/squirrel_test.gleam 5 + test_name: decode_success_with_long_builder_is_properly_formatted_not_breaking_it_on_a_different_line_test 6 + --- 7 + import gleam/dynamic/decode 8 + import pog 9 + 10 + /// A row you get from running the `query` query 11 + /// defined in `query.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 QueryRow { 17 + QueryRow( 18 + first_column: Int, 19 + second_column: Int, 20 + third_column: Int, 21 + fourth_column: Int, 22 + fifth_column: Int, 23 + sixth_column: Int, 24 + ) 25 + } 26 + 27 + /// Runs the `query` query 28 + /// defined in `query.sql`. 29 + /// 30 + /// > 🐿️ This function was generated automatically using v-test of 31 + /// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). 32 + /// 33 + pub fn query(db) { 34 + let decoder = { 35 + use first_column <- decode.field(0, decode.int) 36 + use second_column <- decode.field(1, decode.int) 37 + use third_column <- decode.field(2, decode.int) 38 + use fourth_column <- decode.field(3, decode.int) 39 + use fifth_column <- decode.field(4, decode.int) 40 + use sixth_column <- decode.field(5, decode.int) 41 + decode.success(QueryRow( 42 + first_column:, 43 + second_column:, 44 + third_column:, 45 + fourth_column:, 46 + fifth_column:, 47 + sixth_column:, 48 + )) 49 + } 50 + 51 + "select 52 + 1 as first_column, 53 + 2 as second_column, 54 + 3 as third_column, 55 + 4 as fourth_column, 56 + 5 as fifth_column, 57 + 6 as sixth_column;" 58 + |> pog.query 59 + |> pog.returning(decoder) 60 + |> pog.execute(db) 61 + }
+1 -1
birdie_snapshots/enum_array_decoding.accepted
··· 1 1 --- 2 - version: 1.2.6 2 + version: 1.3.0 3 3 title: enum array decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: enum_array_decoding_test
+1 -1
birdie_snapshots/enum_decoding.accepted
··· 1 1 --- 2 - version: 1.2.6 2 + version: 1.3.0 3 3 title: enum decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: enum_decoding_test
+1 -1
birdie_snapshots/enum_encoding.accepted
··· 1 1 --- 2 - version: 1.2.6 2 + version: 1.3.0 3 3 title: enum encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: enum_encoding_test
+1 -1
birdie_snapshots/enum_with_invalid_name.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: enum with invalid name 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: enum_with_invalid_name_test
+1 -1
birdie_snapshots/enum_with_invalid_variant.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: enum with invalid variant 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: enum_with_invalid_variant_test
+1 -1
birdie_snapshots/enum_with_no_variants_is_rejected.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: enum with no variants is rejected 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: enum_with_no_variants_is_rejected_test
+1 -1
birdie_snapshots/float_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: float decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: float_decoding_test
+1 -1
birdie_snapshots/float_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: float encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: float_encoding_test
+1 -1
birdie_snapshots/full_join_columns_are_optional.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: full join columns are optional 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: full_join_columns_are_optional_test
+1 -1
birdie_snapshots/generated_type_fields_are_labelled_with_their_name_in_the_select_list.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: generated type fields are labelled with their name in the select list 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: generated_type_fields_are_labelled_with_their_name_in_the_select_list_test
+1 -1
birdie_snapshots/generated_type_has_the_same_name_as_the_function_but_in_pascal_case.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: generated type has the same name as the function but in pascal case 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: generated_type_has_the_same_name_as_the_function_but_in_pascal_case_test
+1 -1
birdie_snapshots/insert_with_no_returned_values_returns_just_nil_and_doesnt_define_a_type.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: insert with no returned values returns just nil and doesnt define a type 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: insert_with_no_returned_values_returns_just_nil_and_doesnt_define_a_type_test
+1 -1
birdie_snapshots/int_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: int decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: int_decoding_test
+1 -1
birdie_snapshots/int_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: int encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: int_encoding_test
+1 -1
birdie_snapshots/json_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: json decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: json_decoding_test
+1 -1
birdie_snapshots/json_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: json encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: json_encoding_test
+1 -1
birdie_snapshots/jsonb_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: jsonb decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: jsonb_decoding_test
+1 -1
birdie_snapshots/jsonb_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: jsonb encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: jsonb_encoding_test
+1 -1
birdie_snapshots/left_join_columns_are_optional.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: left join columns are optional 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: left_join_columns_are_optional_test
+1 -1
birdie_snapshots/left_join_nullability_inference.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: left join nullability inference 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: left_join_nullability_inference_test
+1 -1
birdie_snapshots/nullability_with_foreign_key.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: nullability with foreign key 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: nullability_with_foreign_key_condition_test
+1 -1
birdie_snapshots/optional_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: optional decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: optional_decoding_test
+1 -1
birdie_snapshots/query_returning_columns_with_same_name.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: query returning columns with same name 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: query_returning_columns_with_same_name_test
+1 -1
birdie_snapshots/query_returning_columns_with_same_names.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: query returning columns with same names 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: query_returning_columns_with_same_names_test
+1 -1
birdie_snapshots/query_starting_with_a_semicolon_produces_syntax_error_instead_of_crashing.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: query starting with a semicolon produces syntax error instead of crashing 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: query_starting_with_a_semicolon_produces_syntax_error_instead_of_crashing_test
+1 -1
birdie_snapshots/query_that_needs_more_than_a_single_helper_function.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 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
+1 -1
birdie_snapshots/query_that_needs_utils_and_enums_has_two_sections.accepted
··· 1 1 --- 2 - version: 1.2.6 2 + version: 1.3.0 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
+1 -1
birdie_snapshots/query_with_column_that_doesn't_exist.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: query with column that doesn't exist 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: query_with_column_that_doesnt_exist_test
+1 -1
birdie_snapshots/query_with_comment.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: query with comment 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: query_with_comment_test
+1 -1
birdie_snapshots/query_with_multiline_comment.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: query with multiline comment 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: query_with_multiline_comment_test
+1 -1
birdie_snapshots/query_with_syntax_error.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: query with syntax error 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: query_with_syntax_error_test
+1 -1
birdie_snapshots/query_with_table_that_doesn't_exist.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: query with table that doesn't exist 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: query_with_table_that_doesnt_exist_test
+1 -1
birdie_snapshots/recursive_common_table_query_with_semi_join.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: recursive common table query with semi join 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: recursive_common_table_query_with_semi_join_test
+1 -1
birdie_snapshots/right_join_columns_are_optional.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: right join columns are optional 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: right_join_columns_are_optional_test
+1 -1
birdie_snapshots/string_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: string decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: string_decoding_test
+1 -1
birdie_snapshots/string_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: string encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: string_encoding_test
+1 -1
birdie_snapshots/there_is_only_one_empty_line_between_code_generated_for_different_queries.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: there is only one empty line between code generated for different queries 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: there_is_only_one_empty_line_between_code_generated_for_different_queries_test
+1 -1
birdie_snapshots/timestamp_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: timestamp decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: timestamp_decoding_test
+1 -1
birdie_snapshots/timestamp_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: timestamp encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: timestamp_encoding_test
+1 -1
birdie_snapshots/using_uuids_more_than_once_results_in_a_single_uuid_decoder_helper.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: using uuids more than once results in a single uuid decoder helper 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: using_uuids_more_than_once_results_in_a_single_uuid_decoder_helper_test
+1 -1
birdie_snapshots/uuid_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: uuid decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: uuid_decoding_test
+1 -1
birdie_snapshots/uuid_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: uuid encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: uuid_encoding_test
+1 -1
birdie_snapshots/varchar_decoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: varchar decoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: varchar_decoding_test
+1 -1
birdie_snapshots/varchar_encoding.accepted
··· 1 1 --- 2 - version: 1.2.5 2 + version: 1.3.0 3 3 title: varchar encoding 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: varchar_encoding_test
+1 -1
birdie_snapshots/when_a_constraint_doesn't_exist_there_should_be_an_error_message.accepted
··· 1 1 --- 2 - version: 1.2.3 2 + version: 1.3.0 3 3 title: when a constraint doesn't exist there should be an error message 4 4 file: ./test/squirrel_test.gleam 5 5 test_name: non_existing_constraint_error_message_test
+49 -3
src/squirrel/internal/query.gleam
··· 735 735 /// 736 736 const nil_decoder = "decode.map(decode.dynamic, fn(_) { Nil })" 737 737 738 - /// A pretty printed decoder that decodes an n-item dynamic tuple using the 739 - /// `decode` package. 738 + /// A pretty printed decoder that decodes an n-item dynamic tuple with the given 739 + /// constructor wrapping the returned rows from a query. 740 + /// 741 + /// If the query returns no columns (that is `returns == []`), then we default 742 + /// to building decoder that always returns `Nil`. 740 743 /// 741 744 fn decoder_doc( 742 745 state: CodeGenState, ··· 766 769 let labelled_names = list.reverse(labelled_names) 767 770 768 771 let success_line = 769 - call_doc("decode.success", [call_doc(constructor, labelled_names)]) 772 + nested_calls_doc("decode.success", constructor, labelled_names) 773 + 770 774 let doc = block(list.append(parameters, [success_line])) 771 775 #(state, doc) 772 776 } ··· 792 796 /// 793 797 fn call_doc(function: String, args: List(Document)) -> Document { 794 798 [doc.from_string(function), comma_list("(", args, ")")] 799 + |> doc.concat 800 + |> doc.group 801 + } 802 + 803 + /// This is a special case of a call document. To accomodate for a special rule 804 + /// of the Gleam formatter: when we have a function call that has a single other 805 + /// function as its one and only argument. 806 + /// 807 + /// ```gleam 808 + /// first(second(arg_1, arg_2, arg_3, ..., arg_n)) 809 + /// ``` 810 + /// 811 + /// When this needs to be broken, the formatter will only split the arguments of 812 + /// the second call like this: 813 + /// 814 + /// ```gleam 815 + /// first(second( 816 + /// arg_1, 817 + /// ..., 818 + /// arg_n 819 + /// )) 820 + /// ``` 821 + /// 822 + /// Given the first and second function, and the arguments of the second 823 + /// function, this function builds a document that behaves like that. 824 + /// 825 + fn nested_calls_doc( 826 + first: String, 827 + second: String, 828 + arguments: List(Document), 829 + ) -> Document { 830 + [ 831 + doc.from_string(first), 832 + doc.from_string("("), 833 + // ^^ For the first call we don't add any breakable space after the `(`, so 834 + // that the only thing that can get broken on multiple lines are the 835 + // arguments of the second function 836 + call_doc(second, arguments), 837 + // ^^ And the second call is broken and behaves as usual, with its arguments 838 + // being nested 839 + doc.from_string(")"), 840 + ] 795 841 |> doc.concat 796 842 |> doc.group 797 843 }
+15
test/squirrel_test.gleam
··· 555 555 |> birdie.snap(title: "query with quoted string is properly escaped") 556 556 } 557 557 558 + // https://github.com/giacomocavalieri/squirrel/issues/77 559 + pub fn decode_success_with_long_builder_is_properly_formatted_not_breaking_it_on_a_different_line_test() { 560 + "select 561 + 1 as first_column, 562 + 2 as second_column, 563 + 3 as third_column, 564 + 4 as fourth_column, 565 + 5 as fifth_column, 566 + 6 as sixth_column;" 567 + |> should_codegen 568 + |> birdie.snap( 569 + title: "decode.success with long builder is properly formatted not breaking it on a different line", 570 + ) 571 + } 572 + 558 573 // --- ERRROR TESTS ------------------------------------------------------------ 559 574 // This is a group of tests to ensure that the errors look good when something 560 575 // goes wrong.