jarify 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. {jarify-0.2.0 → jarify-0.3.0}/CHANGELOG.md +5 -0
  2. {jarify-0.2.0 → jarify-0.3.0}/PKG-INFO +1 -1
  3. {jarify-0.2.0 → jarify-0.3.0}/docs/sql-style-guide.md +1 -1
  4. {jarify-0.2.0 → jarify-0.3.0}/pyproject.toml +1 -1
  5. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/prefer_group_by_all.py +29 -4
  6. jarify-0.3.0/tests/fixtures/select/group_by_all.expected.sql +7 -0
  7. jarify-0.3.0/tests/fixtures/select/group_by_all.input.sql +8 -0
  8. {jarify-0.2.0 → jarify-0.3.0}/tests/test_formatter.py +6 -1
  9. {jarify-0.2.0 → jarify-0.3.0}/uv.lock +1 -1
  10. {jarify-0.2.0 → jarify-0.3.0}/.agents/skills/add-formatter-rule.md +0 -0
  11. {jarify-0.2.0 → jarify-0.3.0}/.github/workflows/ci.yml +0 -0
  12. {jarify-0.2.0 → jarify-0.3.0}/.github/workflows/prepare-release.yml +0 -0
  13. {jarify-0.2.0 → jarify-0.3.0}/.github/workflows/publish.yml +0 -0
  14. {jarify-0.2.0 → jarify-0.3.0}/.gitignore +0 -0
  15. {jarify-0.2.0 → jarify-0.3.0}/.mise/tasks/release/prepare +0 -0
  16. {jarify-0.2.0 → jarify-0.3.0}/.python-version +0 -0
  17. {jarify-0.2.0 → jarify-0.3.0}/AGENTS.md +0 -0
  18. {jarify-0.2.0 → jarify-0.3.0}/LICENSE +0 -0
  19. {jarify-0.2.0 → jarify-0.3.0}/README.md +0 -0
  20. {jarify-0.2.0 → jarify-0.3.0}/cliff.toml +0 -0
  21. {jarify-0.2.0 → jarify-0.3.0}/docs/editors/README.md +0 -0
  22. {jarify-0.2.0 → jarify-0.3.0}/docs/editors/neovim.md +0 -0
  23. {jarify-0.2.0 → jarify-0.3.0}/mise.toml +0 -0
  24. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/__init__.py +0 -0
  25. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/cli.py +0 -0
  26. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/config.py +0 -0
  27. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/formatter.py +0 -0
  28. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/generator.py +0 -0
  29. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/linter.py +0 -0
  30. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/parser.py +0 -0
  31. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/__init__.py +0 -0
  32. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/base.py +0 -0
  33. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/consistent_empty_array.py +0 -0
  34. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/cte_naming.py +0 -0
  35. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/duckdb_prefer_qualify.py +0 -0
  36. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/duckdb_type_style.py +0 -0
  37. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/keyword_case.py +0 -0
  38. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/no_implicit_cross_join.py +0 -0
  39. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/no_select_star.py +0 -0
  40. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/no_select_star_in_cte.py +0 -0
  41. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/no_unused_cte.py +0 -0
  42. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/prefer_using_over_on.py +0 -0
  43. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/rules/trailing_commas.py +0 -0
  44. {jarify-0.2.0 → jarify-0.3.0}/src/jarify/types.py +0 -0
  45. {jarify-0.2.0 → jarify-0.3.0}/tests/__init__.py +0 -0
  46. {jarify-0.2.0 → jarify-0.3.0}/tests/conftest.py +0 -0
  47. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/complex/real_world.expected.sql +0 -0
  48. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/complex/real_world.input.sql +0 -0
  49. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/create_table/basic.expected.sql +0 -0
  50. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/create_table/basic.input.sql +0 -0
  51. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/create_table/inline_primary_key.expected.sql +0 -0
  52. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/create_table/inline_primary_key.input.sql +0 -0
  53. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/create_table/simple_no_constraints.expected.sql +0 -0
  54. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/create_table/simple_no_constraints.input.sql +0 -0
  55. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/cte/basic_cte.expected.sql +0 -0
  56. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/cte/basic_cte.input.sql +0 -0
  57. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/cte/cte_inline_comment.expected.sql +0 -0
  58. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/cte/cte_inline_comment.input.sql +0 -0
  59. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/cte/cte_leading_comment.expected.sql +0 -0
  60. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/cte/cte_leading_comment.input.sql +0 -0
  61. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/cte/in_subquery.expected.sql +0 -0
  62. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/cte/in_subquery.input.sql +0 -0
  63. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/agg_window_uppercase.expected.sql +0 -0
  64. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/agg_window_uppercase.input.sql +0 -0
  65. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/create_macro.expected.sql +0 -0
  66. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/create_macro.input.sql +0 -0
  67. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/create_macro_table.expected.sql +0 -0
  68. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/create_macro_table.input.sql +0 -0
  69. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/distinct_in_aggregates.expected.sql +0 -0
  70. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/distinct_in_aggregates.input.sql +0 -0
  71. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/pivot.expected.sql +0 -0
  72. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/pivot.input.sql +0 -0
  73. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/pivot_order_by.expected.sql +0 -0
  74. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/pivot_order_by.input.sql +0 -0
  75. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/qualify.expected.sql +0 -0
  76. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/qualify.input.sql +0 -0
  77. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/read_parquet.expected.sql +0 -0
  78. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/read_parquet.input.sql +0 -0
  79. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/reserved_keyword_type_cast.expected.sql +0 -0
  80. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/reserved_keyword_type_cast.input.sql +0 -0
  81. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/rust_format_syntax.expected.sql +0 -0
  82. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/rust_format_syntax.input.sql +0 -0
  83. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/string_agg.expected.sql +0 -0
  84. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/string_agg.input.sql +0 -0
  85. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/struct_cast_alignment.expected.sql +0 -0
  86. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/struct_cast_alignment.input.sql +0 -0
  87. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/type_cast.expected.sql +0 -0
  88. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/type_cast.input.sql +0 -0
  89. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/values_cte.expected.sql +0 -0
  90. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/duckdb/values_cte.input.sql +0 -0
  91. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/aligned_joins.expected.sql +0 -0
  92. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/aligned_joins.input.sql +0 -0
  93. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/bare_join.expected.sql +0 -0
  94. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/bare_join.input.sql +0 -0
  95. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/comma_join.expected.sql +0 -0
  96. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/comma_join.input.sql +0 -0
  97. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/cross_join_unnest_alias.expected.sql +0 -0
  98. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/cross_join_unnest_alias.input.sql +0 -0
  99. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/multi_join.expected.sql +0 -0
  100. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/multi_join.input.sql +0 -0
  101. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/outer_join_alias_alignment.expected.sql +0 -0
  102. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/outer_join_alias_alignment.input.sql +0 -0
  103. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/unnest_alias_no_as.expected.sql +0 -0
  104. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/joins/unnest_alias_no_as.input.sql +0 -0
  105. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/alias_alignment_rust_fmt_placeholder.expected.sql +0 -0
  106. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/alias_alignment_rust_fmt_placeholder.input.sql +0 -0
  107. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/alias_normalization.expected.sql +0 -0
  108. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/alias_normalization.input.sql +0 -0
  109. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/basic.expected.sql +0 -0
  110. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/basic.input.sql +0 -0
  111. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/between_column_comment.expected.sql +0 -0
  112. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/between_column_comment.input.sql +0 -0
  113. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/boolean_literals.expected.sql +0 -0
  114. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/boolean_literals.input.sql +0 -0
  115. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/case_when_and_condition.expected.sql +0 -0
  116. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/case_when_and_condition.input.sql +0 -0
  117. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/case_when_from_first_subquery.expected.sql +0 -0
  118. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/case_when_from_first_subquery.input.sql +0 -0
  119. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/case_when_inline.expected.sql +0 -0
  120. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/case_when_inline.input.sql +0 -0
  121. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/case_when_multiline_threshold.expected.sql +0 -0
  122. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/case_when_multiline_threshold.input.sql +0 -0
  123. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/coalesce_to_ifnull.expected.sql +0 -0
  124. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/coalesce_to_ifnull.input.sql +0 -0
  125. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/from_first.expected.sql +0 -0
  126. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/from_first.input.sql +0 -0
  127. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/from_first_star_qualified.expected.sql +0 -0
  128. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/from_first_star_qualified.input.sql +0 -0
  129. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/from_inline_subquery.expected.sql +0 -0
  130. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/from_inline_subquery.input.sql +0 -0
  131. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/ifnull.expected.sql +0 -0
  132. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/ifnull.input.sql +0 -0
  133. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/inline_comment.expected.sql +0 -0
  134. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/inline_comment.input.sql +0 -0
  135. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/json_arrow_operators.expected.sql +0 -0
  136. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/json_arrow_operators.input.sql +0 -0
  137. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/leading_comment.expected.sql +0 -0
  138. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/leading_comment.input.sql +0 -0
  139. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/leading_comment_multiline.expected.sql +0 -0
  140. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/leading_comment_multiline.input.sql +0 -0
  141. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/list_contains.expected.sql +0 -0
  142. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/list_contains.input.sql +0 -0
  143. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/paren_arithmetic.expected.sql +0 -0
  144. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/paren_arithmetic.input.sql +0 -0
  145. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/where_clause.expected.sql +0 -0
  146. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/where_clause.input.sql +0 -0
  147. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/where_eq_alignment.expected.sql +0 -0
  148. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/where_eq_alignment.input.sql +0 -0
  149. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/where_eq_alignment_nested_subquery.expected.sql +0 -0
  150. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/where_eq_alignment_nested_subquery.input.sql +0 -0
  151. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/where_or_inline.expected.sql +0 -0
  152. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/select/where_or_inline.input.sql +0 -0
  153. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/templates/rust_fmt_consecutive_placeholders.expected.sql +0 -0
  154. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/templates/rust_fmt_consecutive_placeholders.input.sql +0 -0
  155. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/templates/rust_fmt_ctas_body_placeholder.expected.sql +0 -0
  156. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/templates/rust_fmt_ctas_body_placeholder.input.sql +0 -0
  157. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/templates/rust_fmt_indented_placeholders.expected.sql +0 -0
  158. {jarify-0.2.0 → jarify-0.3.0}/tests/fixtures/templates/rust_fmt_indented_placeholders.input.sql +0 -0
  159. {jarify-0.2.0 → jarify-0.3.0}/tests/test_config.py +0 -0
  160. {jarify-0.2.0 → jarify-0.3.0}/tests/test_duckdb_rules.py +0 -0
  161. {jarify-0.2.0 → jarify-0.3.0}/tests/test_fixtures.py +0 -0
  162. {jarify-0.2.0 → jarify-0.3.0}/tests/test_lint_rules.py +0 -0
  163. {jarify-0.2.0 → jarify-0.3.0}/tests/test_parser.py +0 -0
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0] - 2026-04-27
4
+ ### Features
5
+
6
+ - promote prefer-group-by-all to auto-fix formatter rule ([#192](https://github.com/amfaro/jarify/pull/192)) ([#192](https://github.com/amfaro/jarify/pull/192))
7
+
3
8
  ## [0.2.0] - 2026-04-27
4
9
  ### Features
5
10
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarify
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Bespoke SQL linter and formatter for DuckDB, powered by sqlglot
5
5
  Project-URL: Homepage, https://github.com/amfaro/jarify
6
6
  Project-URL: Repository, https://github.com/amfaro/jarify
@@ -903,7 +903,7 @@ QUALIFY
903
903
 
904
904
  ### `prefer-group-by-all`
905
905
 
906
- Flag explicit `GROUP BY col1, col2, ...` when all non-aggregated `SELECT` columns are listed. Use `GROUP BY ALL` instead.
906
+ **Auto-fixed by `fmt`.** Rewrites explicit `GROUP BY col1, col2, ...` to `GROUP BY ALL` when all non-aggregated `SELECT` columns are listed.
907
907
 
908
908
  **Bad**
909
909
  ```sql
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jarify"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  description = "Bespoke SQL linter and formatter for DuckDB, powered by sqlglot"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1,15 +1,15 @@
1
- """Rule: suggest GROUP BY ALL when all non-aggregated SELECT columns are listed."""
1
+ """Rule: rewrite explicit GROUP BY column list to GROUP BY ALL when equivalent."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
5
  import sqlglot.expressions as exp
6
6
 
7
- from jarify.rules.base import LintOnlyRule, _node_pos
7
+ from jarify.rules.base import FormatterRule, _node_pos
8
8
  from jarify.types import LintViolation
9
9
 
10
10
 
11
- class PreferGroupByAllRule(LintOnlyRule):
12
- """Lint: flag explicit GROUP BY col list when GROUP BY ALL would be equivalent."""
11
+ class PreferGroupByAllRule(FormatterRule):
12
+ """Format and lint: rewrite explicit GROUP BY col list to GROUP BY ALL when equivalent."""
13
13
 
14
14
  def __init__(self, severity: str = "warn") -> None:
15
15
  self.severity = severity
@@ -18,6 +18,31 @@ class PreferGroupByAllRule(LintOnlyRule):
18
18
  def name(self) -> str:
19
19
  return "prefer-group-by-all"
20
20
 
21
+ def apply(self, tree: exp.Expression) -> exp.Expression:
22
+ """Rewrite GROUP BY <cols> → GROUP BY ALL when all non-agg SELECT cols are covered."""
23
+ for select in tree.find_all(exp.Select):
24
+ group = select.args.get("group")
25
+ if not group or not group.expressions:
26
+ continue
27
+ if group.args.get("all"):
28
+ continue
29
+
30
+ non_agg_sqls: list[str] = []
31
+ for item in select.expressions:
32
+ inner = item.this if isinstance(item, exp.Alias) else item
33
+ if not inner.find(exp.AggFunc):
34
+ non_agg_sqls.append(inner.sql(dialect="duckdb"))
35
+
36
+ if not non_agg_sqls:
37
+ continue
38
+
39
+ group_sqls = {e.sql(dialect="duckdb") for e in group.expressions}
40
+ if set(non_agg_sqls) == group_sqls:
41
+ group.set("all", True)
42
+ group.set("expressions", [])
43
+
44
+ return tree
45
+
21
46
  def check(self, tree: exp.Expression) -> list[LintViolation]:
22
47
  if self.severity == "off":
23
48
  return []
@@ -0,0 +1,7 @@
1
+ SELECT
2
+ a
3
+ ,b
4
+ ,SUM(c) AS total
5
+ FROM t
6
+ GROUP BY ALL
7
+ ;
@@ -0,0 +1,8 @@
1
+ SELECT
2
+ a
3
+ , b
4
+ , SUM(c) AS total
5
+ FROM t
6
+ GROUP BY
7
+ a
8
+ , b
@@ -205,10 +205,15 @@ class TestJoinFormatting:
205
205
 
206
206
  class TestGroupByPerLine:
207
207
  def test_group_by_multi_column_one_per_line(self):
208
+ # When all non-agg columns are in GROUP BY, the formatter rewrites to GROUP BY ALL.
208
209
  out, _ = format_sql("SELECT a, b, count(*) FROM t GROUP BY a, b")
210
+ assert "GROUP BY ALL" in out
211
+
212
+ def test_group_by_explicit_when_partial(self):
213
+ # When only a subset of non-agg columns is listed, keep explicit GROUP BY.
214
+ out, _ = format_sql("SELECT a, b, c, count(*) FROM t GROUP BY a, b")
209
215
  lines = out.splitlines()
210
216
  group_by_idx = next(i for i, line in enumerate(lines) if "GROUP BY" in line)
211
- # The line with GROUP BY should not contain column names on the same line
212
217
  group_by_line = lines[group_by_idx]
213
218
  assert group_by_line.strip() == "GROUP BY", f"Expected 'GROUP BY' on its own line, got: {group_by_line!r}"
214
219
 
@@ -34,7 +34,7 @@ wheels = [
34
34
 
35
35
  [[package]]
36
36
  name = "jarify"
37
- version = "0.2.0"
37
+ version = "0.3.0"
38
38
  source = { editable = "." }
39
39
  dependencies = [
40
40
  { name = "click" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes