jarify 0.3.1__tar.gz → 0.3.2__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.
- {jarify-0.3.1 → jarify-0.3.2}/CHANGELOG.md +5 -0
- {jarify-0.3.1 → jarify-0.3.2}/PKG-INFO +1 -1
- {jarify-0.3.1 → jarify-0.3.2}/pyproject.toml +1 -1
- jarify-0.3.2/src/jarify/rules/prefer_group_by_all.py +119 -0
- jarify-0.3.2/tests/fixtures/select/group_by_all_mixed_expr.expected.sql +5 -0
- jarify-0.3.2/tests/fixtures/select/group_by_all_mixed_expr.input.sql +5 -0
- {jarify-0.3.1 → jarify-0.3.2}/uv.lock +1 -1
- jarify-0.3.1/src/jarify/rules/prefer_group_by_all.py +0 -84
- {jarify-0.3.1 → jarify-0.3.2}/.agents/skills/add-formatter-rule.md +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/.github/workflows/ci.yml +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/.github/workflows/prepare-release.yml +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/.github/workflows/publish.yml +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/.gitignore +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/.mise/tasks/release/prepare +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/.python-version +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/AGENTS.md +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/LICENSE +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/README.md +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/cliff.toml +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/docs/editors/README.md +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/docs/editors/neovim.md +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/docs/sql-style-guide.md +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/mise.toml +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/__init__.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/cli.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/config.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/formatter.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/generator.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/linter.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/parser.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/__init__.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/base.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/consistent_empty_array.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/cte_naming.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/duckdb_prefer_qualify.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/duckdb_type_style.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/keyword_case.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/no_implicit_cross_join.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/no_select_star.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/no_select_star_in_cte.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/no_unused_cte.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/prefer_using_over_on.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/rules/trailing_commas.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/src/jarify/types.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/__init__.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/conftest.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/complex/real_world.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/complex/real_world.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/create_table/basic.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/create_table/basic.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/create_table/inline_primary_key.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/create_table/inline_primary_key.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/create_table/simple_no_constraints.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/create_table/simple_no_constraints.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/cte/basic_cte.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/cte/basic_cte.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/cte/cte_inline_comment.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/cte/cte_inline_comment.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/cte/cte_leading_comment.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/cte/cte_leading_comment.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/cte/in_subquery.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/cte/in_subquery.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/agg_window_uppercase.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/agg_window_uppercase.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/create_macro.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/create_macro.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/create_macro_table.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/create_macro_table.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/distinct_in_aggregates.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/distinct_in_aggregates.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/numeric_decimal_type.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/numeric_decimal_type.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/pivot.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/pivot.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/pivot_order_by.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/pivot_order_by.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/qualify.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/qualify.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/read_parquet.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/read_parquet.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/reserved_keyword_type_cast.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/reserved_keyword_type_cast.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/rust_format_syntax.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/rust_format_syntax.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/string_agg.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/string_agg.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/struct_cast_alignment.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/struct_cast_alignment.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/type_cast.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/type_cast.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/values_cte.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/duckdb/values_cte.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/aligned_joins.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/aligned_joins.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/bare_join.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/bare_join.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/comma_join.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/comma_join.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/cross_join_unnest_alias.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/cross_join_unnest_alias.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/multi_join.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/multi_join.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/outer_join_alias_alignment.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/outer_join_alias_alignment.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/unnest_alias_no_as.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/joins/unnest_alias_no_as.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/alias_alignment_rust_fmt_placeholder.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/alias_alignment_rust_fmt_placeholder.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/alias_normalization.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/alias_normalization.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/basic.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/basic.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/between_column_comment.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/between_column_comment.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/boolean_literals.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/boolean_literals.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/case_when_and_condition.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/case_when_and_condition.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/case_when_from_first_subquery.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/case_when_from_first_subquery.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/case_when_inline.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/case_when_inline.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/case_when_multiline_threshold.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/case_when_multiline_threshold.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/coalesce_to_ifnull.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/coalesce_to_ifnull.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/from_first.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/from_first.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/from_first_star_qualified.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/from_first_star_qualified.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/from_inline_subquery.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/from_inline_subquery.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/group_by_all.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/group_by_all.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/ifnull.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/ifnull.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/inline_comment.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/inline_comment.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/json_arrow_operators.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/json_arrow_operators.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/leading_comment.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/leading_comment.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/leading_comment_multiline.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/leading_comment_multiline.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/list_contains.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/list_contains.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/paren_arithmetic.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/paren_arithmetic.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/where_clause.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/where_clause.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/where_eq_alignment.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/where_eq_alignment.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/where_eq_alignment_nested_subquery.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/where_eq_alignment_nested_subquery.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/where_or_inline.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/where_or_inline.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/templates/rust_fmt_consecutive_placeholders.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/templates/rust_fmt_consecutive_placeholders.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/templates/rust_fmt_ctas_body_placeholder.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/templates/rust_fmt_ctas_body_placeholder.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/templates/rust_fmt_indented_placeholders.expected.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/templates/rust_fmt_indented_placeholders.input.sql +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/test_config.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/test_duckdb_rules.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/test_fixtures.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/test_formatter.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/test_lint_rules.py +0 -0
- {jarify-0.3.1 → jarify-0.3.2}/tests/test_parser.py +0 -0
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.2] - 2026-04-27
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
- handle mixed agg/non-agg SELECT exprs in prefer-group-by-all ([#198](https://github.com/amfaro/jarify/pull/198)) ([#198](https://github.com/amfaro/jarify/pull/198))
|
|
7
|
+
|
|
3
8
|
## [0.3.1] - 2026-04-27
|
|
4
9
|
### Bug Fixes
|
|
5
10
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Rule: rewrite explicit GROUP BY column list to GROUP BY ALL when equivalent."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sqlglot.expressions as exp
|
|
6
|
+
|
|
7
|
+
from jarify.rules.base import FormatterRule, _node_pos
|
|
8
|
+
from jarify.types import LintViolation
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _free_col_refs(expr: exp.Expression) -> set[str]:
|
|
12
|
+
"""Return SQL strings of Column nodes in *expr* that are not inside any AggFunc.
|
|
13
|
+
|
|
14
|
+
For a purely non-aggregate expression like ``LOWER(name)`` this returns
|
|
15
|
+
``{"name"}`` (the leaf column, not the whole expression). Callers that
|
|
16
|
+
need the full expression SQL for purely non-aggregate items should call
|
|
17
|
+
``expr.sql()`` directly instead.
|
|
18
|
+
"""
|
|
19
|
+
if isinstance(expr, exp.AggFunc):
|
|
20
|
+
return set()
|
|
21
|
+
if isinstance(expr, exp.Column):
|
|
22
|
+
return {expr.sql(dialect="duckdb")}
|
|
23
|
+
result: set[str] = set()
|
|
24
|
+
for child in expr.args.values():
|
|
25
|
+
if isinstance(child, list):
|
|
26
|
+
for c in child:
|
|
27
|
+
if isinstance(c, exp.Expression):
|
|
28
|
+
result |= _free_col_refs(c)
|
|
29
|
+
elif isinstance(child, exp.Expression):
|
|
30
|
+
result |= _free_col_refs(child)
|
|
31
|
+
return result
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _non_agg_sqls(select: exp.Select) -> set[str]:
|
|
35
|
+
"""Derive the set of "non-aggregate expressions" that GROUP BY ALL would use.
|
|
36
|
+
|
|
37
|
+
Rules:
|
|
38
|
+
- If a SELECT item contains **no** AggFunc anywhere, the whole item counts
|
|
39
|
+
as a GROUP BY candidate (e.g. ``LOWER(name)``, ``col1``).
|
|
40
|
+
- If a SELECT item is a **mixed** expression (contains an AggFunc *and*
|
|
41
|
+
free column references outside that AggFunc), those free column refs are
|
|
42
|
+
the GROUP BY candidates (e.g. ``'prefix' || col1 || STRING_AGG(x)``
|
|
43
|
+
contributes ``col1``).
|
|
44
|
+
- Pure aggregate items (e.g. ``SUM(col)`` with no outer column refs)
|
|
45
|
+
contribute nothing.
|
|
46
|
+
"""
|
|
47
|
+
result: set[str] = set()
|
|
48
|
+
for item in select.expressions:
|
|
49
|
+
inner = item.this if isinstance(item, exp.Alias) else item
|
|
50
|
+
if not inner.find(exp.AggFunc):
|
|
51
|
+
# Entirely non-aggregate — use the whole expression as-is.
|
|
52
|
+
result.add(inner.sql(dialect="duckdb"))
|
|
53
|
+
else:
|
|
54
|
+
# Mixed or pure aggregate — collect free (non-agg) column refs.
|
|
55
|
+
result |= _free_col_refs(inner)
|
|
56
|
+
return result
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class PreferGroupByAllRule(FormatterRule):
|
|
60
|
+
"""Format and lint: rewrite explicit GROUP BY col list to GROUP BY ALL when equivalent."""
|
|
61
|
+
|
|
62
|
+
def __init__(self, severity: str = "warn") -> None:
|
|
63
|
+
self.severity = severity
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def name(self) -> str:
|
|
67
|
+
return "prefer-group-by-all"
|
|
68
|
+
|
|
69
|
+
def apply(self, tree: exp.Expression) -> exp.Expression:
|
|
70
|
+
"""Rewrite GROUP BY <cols> → GROUP BY ALL when all non-agg SELECT cols are covered."""
|
|
71
|
+
for select in tree.find_all(exp.Select):
|
|
72
|
+
group = select.args.get("group")
|
|
73
|
+
if not group or not group.expressions:
|
|
74
|
+
continue
|
|
75
|
+
if group.args.get("all"):
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
non_agg = _non_agg_sqls(select)
|
|
79
|
+
if not non_agg:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
group_sqls = {e.sql(dialect="duckdb") for e in group.expressions}
|
|
83
|
+
if non_agg == group_sqls:
|
|
84
|
+
group.set("all", True)
|
|
85
|
+
group.set("expressions", [])
|
|
86
|
+
|
|
87
|
+
return tree
|
|
88
|
+
|
|
89
|
+
def check(self, tree: exp.Expression) -> list[LintViolation]:
|
|
90
|
+
if self.severity == "off":
|
|
91
|
+
return []
|
|
92
|
+
violations: list[LintViolation] = []
|
|
93
|
+
|
|
94
|
+
for select in tree.find_all(exp.Select):
|
|
95
|
+
group = select.args.get("group")
|
|
96
|
+
if not group or not group.expressions:
|
|
97
|
+
continue
|
|
98
|
+
if group.args.get("all"):
|
|
99
|
+
# Already GROUP BY ALL — nothing to flag
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
non_agg = _non_agg_sqls(select)
|
|
103
|
+
if not non_agg:
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
group_sqls = {e.sql(dialect="duckdb") for e in group.expressions}
|
|
107
|
+
if non_agg == group_sqls:
|
|
108
|
+
_line, _col = _node_pos(group)
|
|
109
|
+
violations.append(
|
|
110
|
+
LintViolation(
|
|
111
|
+
rule=self.name,
|
|
112
|
+
severity=self.severity,
|
|
113
|
+
message="All non-aggregated SELECT columns are listed in GROUP BY; prefer GROUP BY ALL",
|
|
114
|
+
line=_line,
|
|
115
|
+
column=_col,
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return violations
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
"""Rule: rewrite explicit GROUP BY column list to GROUP BY ALL when equivalent."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import sqlglot.expressions as exp
|
|
6
|
-
|
|
7
|
-
from jarify.rules.base import FormatterRule, _node_pos
|
|
8
|
-
from jarify.types import LintViolation
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class PreferGroupByAllRule(FormatterRule):
|
|
12
|
-
"""Format and lint: rewrite explicit GROUP BY col list to GROUP BY ALL when equivalent."""
|
|
13
|
-
|
|
14
|
-
def __init__(self, severity: str = "warn") -> None:
|
|
15
|
-
self.severity = severity
|
|
16
|
-
|
|
17
|
-
@property
|
|
18
|
-
def name(self) -> str:
|
|
19
|
-
return "prefer-group-by-all"
|
|
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
|
-
|
|
46
|
-
def check(self, tree: exp.Expression) -> list[LintViolation]:
|
|
47
|
-
if self.severity == "off":
|
|
48
|
-
return []
|
|
49
|
-
violations: list[LintViolation] = []
|
|
50
|
-
|
|
51
|
-
for select in tree.find_all(exp.Select):
|
|
52
|
-
group = select.args.get("group")
|
|
53
|
-
if not group or not group.expressions:
|
|
54
|
-
continue
|
|
55
|
-
if group.args.get("all"):
|
|
56
|
-
# Already GROUP BY ALL — nothing to flag
|
|
57
|
-
continue
|
|
58
|
-
|
|
59
|
-
# Collect non-aggregate SELECT expressions (unwrap aliases)
|
|
60
|
-
non_agg_sqls: list[str] = []
|
|
61
|
-
for item in select.expressions:
|
|
62
|
-
inner = item.this if isinstance(item, exp.Alias) else item
|
|
63
|
-
if not inner.find(exp.AggFunc):
|
|
64
|
-
non_agg_sqls.append(inner.sql(dialect="duckdb"))
|
|
65
|
-
|
|
66
|
-
if not non_agg_sqls:
|
|
67
|
-
continue
|
|
68
|
-
|
|
69
|
-
group_sqls = {e.sql(dialect="duckdb") for e in group.expressions}
|
|
70
|
-
non_agg_set = set(non_agg_sqls)
|
|
71
|
-
|
|
72
|
-
if non_agg_set and non_agg_set == group_sqls:
|
|
73
|
-
_line, _col = _node_pos(group)
|
|
74
|
-
violations.append(
|
|
75
|
-
LintViolation(
|
|
76
|
-
rule=self.name,
|
|
77
|
-
severity=self.severity,
|
|
78
|
-
message="All non-aggregated SELECT columns are listed in GROUP BY; prefer GROUP BY ALL",
|
|
79
|
-
line=_line,
|
|
80
|
-
column=_col,
|
|
81
|
-
)
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
return violations
|
|
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
|
|
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
|
{jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/create_table/simple_no_constraints.expected.sql
RENAMED
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/alias_alignment_rust_fmt_placeholder.input.sql
RENAMED
|
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
|
{jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/case_when_from_first_subquery.expected.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/case_when_multiline_threshold.expected.sql
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/where_eq_alignment_nested_subquery.expected.sql
RENAMED
|
File without changes
|
{jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/select/where_eq_alignment_nested_subquery.input.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/templates/rust_fmt_consecutive_placeholders.input.sql
RENAMED
|
File without changes
|
{jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/templates/rust_fmt_ctas_body_placeholder.expected.sql
RENAMED
|
File without changes
|
{jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/templates/rust_fmt_ctas_body_placeholder.input.sql
RENAMED
|
File without changes
|
{jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/templates/rust_fmt_indented_placeholders.expected.sql
RENAMED
|
File without changes
|
{jarify-0.3.1 → jarify-0.3.2}/tests/fixtures/templates/rust_fmt_indented_placeholders.input.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|