jarify 0.3.2__tar.gz → 0.4.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.
- {jarify-0.3.2 → jarify-0.4.0}/CHANGELOG.md +10 -0
- {jarify-0.3.2 → jarify-0.4.0}/PKG-INFO +1 -1
- {jarify-0.3.2 → jarify-0.4.0}/docs/sql-style-guide.md +24 -0
- {jarify-0.3.2 → jarify-0.4.0}/pyproject.toml +1 -1
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/config.py +1 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/formatter.py +10 -1
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/generator.py +6 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/__init__.py +3 -0
- jarify-0.4.0/src/jarify/rules/prefer_neq_operator.py +53 -0
- jarify-0.4.0/tests/fixtures/operators/prefer_neq_operator.expected.sql +7 -0
- jarify-0.4.0/tests/fixtures/operators/prefer_neq_operator.input.sql +6 -0
- jarify-0.4.0/tests/fixtures/templates/rust_fmt_trailing_placeholder.expected.sql +7 -0
- jarify-0.4.0/tests/fixtures/templates/rust_fmt_trailing_placeholder.input.sql +6 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/test_formatter.py +24 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/test_lint_rules.py +17 -0
- {jarify-0.3.2 → jarify-0.4.0}/uv.lock +1 -1
- {jarify-0.3.2 → jarify-0.4.0}/.agents/skills/add-formatter-rule.md +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/.github/workflows/ci.yml +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/.github/workflows/prepare-release.yml +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/.github/workflows/publish.yml +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/.gitignore +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/.mise/tasks/release/prepare +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/.python-version +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/AGENTS.md +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/LICENSE +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/README.md +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/cliff.toml +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/docs/editors/README.md +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/docs/editors/neovim.md +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/mise.toml +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/__init__.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/cli.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/linter.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/parser.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/base.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/consistent_empty_array.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/cte_naming.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/duckdb_prefer_qualify.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/duckdb_type_style.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/keyword_case.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/no_implicit_cross_join.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/no_select_star.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/no_select_star_in_cte.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/no_unused_cte.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/prefer_group_by_all.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/prefer_using_over_on.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/rules/trailing_commas.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/src/jarify/types.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/__init__.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/conftest.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/complex/real_world.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/complex/real_world.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/create_table/basic.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/create_table/basic.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/create_table/inline_primary_key.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/create_table/inline_primary_key.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/create_table/simple_no_constraints.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/create_table/simple_no_constraints.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/cte/basic_cte.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/cte/basic_cte.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/cte/cte_inline_comment.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/cte/cte_inline_comment.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/cte/cte_leading_comment.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/cte/cte_leading_comment.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/cte/in_subquery.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/cte/in_subquery.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/agg_window_uppercase.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/agg_window_uppercase.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/create_macro.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/create_macro.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/create_macro_table.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/create_macro_table.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/distinct_in_aggregates.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/distinct_in_aggregates.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/numeric_decimal_type.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/numeric_decimal_type.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/pivot.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/pivot.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/pivot_order_by.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/pivot_order_by.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/qualify.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/qualify.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/read_parquet.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/read_parquet.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/reserved_keyword_type_cast.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/reserved_keyword_type_cast.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/rust_format_syntax.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/rust_format_syntax.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/string_agg.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/string_agg.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/struct_cast_alignment.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/struct_cast_alignment.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/type_cast.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/type_cast.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/values_cte.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/duckdb/values_cte.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/aligned_joins.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/aligned_joins.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/bare_join.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/bare_join.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/comma_join.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/comma_join.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/cross_join_unnest_alias.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/cross_join_unnest_alias.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/multi_join.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/multi_join.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/outer_join_alias_alignment.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/outer_join_alias_alignment.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/unnest_alias_no_as.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/joins/unnest_alias_no_as.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/alias_alignment_rust_fmt_placeholder.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/alias_alignment_rust_fmt_placeholder.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/alias_normalization.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/alias_normalization.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/basic.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/basic.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/between_column_comment.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/between_column_comment.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/boolean_literals.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/boolean_literals.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/case_when_and_condition.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/case_when_and_condition.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/case_when_from_first_subquery.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/case_when_from_first_subquery.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/case_when_inline.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/case_when_inline.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/case_when_multiline_threshold.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/case_when_multiline_threshold.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/coalesce_to_ifnull.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/coalesce_to_ifnull.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/from_first.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/from_first.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/from_first_star_qualified.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/from_first_star_qualified.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/from_inline_subquery.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/from_inline_subquery.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/group_by_all.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/group_by_all.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/group_by_all_mixed_expr.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/group_by_all_mixed_expr.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/ifnull.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/ifnull.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/inline_comment.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/inline_comment.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/json_arrow_operators.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/json_arrow_operators.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/leading_comment.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/leading_comment.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/leading_comment_multiline.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/leading_comment_multiline.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/list_contains.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/list_contains.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/paren_arithmetic.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/paren_arithmetic.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/where_clause.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/where_clause.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/where_eq_alignment.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/where_eq_alignment.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/where_eq_alignment_nested_subquery.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/where_eq_alignment_nested_subquery.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/where_or_inline.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/where_or_inline.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/templates/rust_fmt_consecutive_placeholders.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/templates/rust_fmt_consecutive_placeholders.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/templates/rust_fmt_ctas_body_placeholder.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/templates/rust_fmt_ctas_body_placeholder.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/templates/rust_fmt_indented_placeholders.expected.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/templates/rust_fmt_indented_placeholders.input.sql +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/test_config.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/test_duckdb_rules.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/test_fixtures.py +0 -0
- {jarify-0.3.2 → jarify-0.4.0}/tests/test_parser.py +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0] - 2026-04-27
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
- add prefer-neq-operator rule — rewrite <> to != ([#206](https://github.com/amfaro/jarify/pull/206)) ([#206](https://github.com/amfaro/jarify/pull/206))
|
|
7
|
+
|
|
8
|
+
## [0.3.3] - 2026-04-27
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- place trailing rust fmt placeholder before statement terminator ([#201](https://github.com/amfaro/jarify/pull/201)) ([#201](https://github.com/amfaro/jarify/pull/201))
|
|
12
|
+
|
|
3
13
|
## [0.3.2] - 2026-04-27
|
|
4
14
|
### Bug Fixes
|
|
5
15
|
|
|
@@ -986,3 +986,27 @@ SELECT
|
|
|
986
986
|
FROM _base
|
|
987
987
|
;
|
|
988
988
|
```
|
|
989
|
+
|
|
990
|
+
---
|
|
991
|
+
|
|
992
|
+
### `prefer-neq-operator`
|
|
993
|
+
|
|
994
|
+
Always use `!=` for inequality comparisons instead of the SQL-92 `<>` operator.
|
|
995
|
+
Both are valid in DuckDB, but `!=` is the modern standard.
|
|
996
|
+
The formatter rewrites `<>` to `!=` automatically.
|
|
997
|
+
|
|
998
|
+
**Bad**
|
|
999
|
+
```sql
|
|
1000
|
+
SELECT a
|
|
1001
|
+
FROM t
|
|
1002
|
+
WHERE x <> y
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
**Good**
|
|
1006
|
+
```sql
|
|
1007
|
+
SELECT
|
|
1008
|
+
a
|
|
1009
|
+
FROM t
|
|
1010
|
+
WHERE x != y
|
|
1011
|
+
;
|
|
1012
|
+
```
|
|
@@ -45,6 +45,7 @@ class JarifyConfig:
|
|
|
45
45
|
prefer_using_over_on: str = "warn" # suggest USING (col) over ON a.col = b.col
|
|
46
46
|
consistent_empty_array: str = "warn" # prefer [] over '[]'::type[] empty array
|
|
47
47
|
no_select_star_in_cte: str = "warn" # flag SELECT * inside CTE bodies
|
|
48
|
+
prefer_neq_operator: str = "warn" # rewrite <> to != inequality operator
|
|
48
49
|
|
|
49
50
|
# --- per-rule overrides (populated from [rules.*] in toml) ---
|
|
50
51
|
rules: dict[str, Any] = field(default_factory=dict)
|
|
@@ -88,11 +88,20 @@ def format_sql(
|
|
|
88
88
|
tree = _apply_rules(tree, rules)
|
|
89
89
|
formatted_parts.append(generator.generate(tree))
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
# Join statements but hold off on the trailing ;\n -- it must come *after*
|
|
92
|
+
# no-anchor line placeholders are re-inserted, otherwise a trailing
|
|
93
|
+
# {placeholder} ends up after the semicolon instead of before it, which
|
|
94
|
+
# breaks SQL templates that use the placeholder as an optional WHERE clause.
|
|
95
|
+
# A trailing \n is appended to ensure the last line ends with a newline so
|
|
96
|
+
# that no-anchor placeholder re-insertion does not concatenate directly onto
|
|
97
|
+
# the last SQL token without a line break.
|
|
98
|
+
formatted = "\n;\n\n".join(formatted_parts) + ("\n" if formatted_parts else "")
|
|
92
99
|
formatted = _unmask_rust_fmt_placeholders(formatted, inline_mask)
|
|
93
100
|
formatted = _unmask_ifnull(formatted)
|
|
94
101
|
formatted = _unmask_numeric(formatted)
|
|
95
102
|
result = _reinsert_line_rust_fmt_placeholders(formatted, line_insertions)
|
|
103
|
+
if formatted_parts:
|
|
104
|
+
result = result.rstrip() + "\n;\n"
|
|
96
105
|
return _restore_ctas_body_placeholders(result, ctas_body_map), warnings
|
|
97
106
|
|
|
98
107
|
|
|
@@ -862,6 +862,12 @@ class JarifyGenerator(DuckDB.Generator):
|
|
|
862
862
|
return super().star_sql(expression)
|
|
863
863
|
|
|
864
864
|
# ------------------------------------------------------------------
|
|
865
|
+
# Inequality operator: always emit != (never <>)
|
|
866
|
+
# ------------------------------------------------------------------
|
|
867
|
+
|
|
868
|
+
def neq_sql(self, expression: exp.NEQ) -> str:
|
|
869
|
+
return self.binary(expression, "!=")
|
|
870
|
+
|
|
865
871
|
# IS NOT NULL: preserve original form instead of NOT x IS NULL
|
|
866
872
|
# ------------------------------------------------------------------
|
|
867
873
|
|
|
@@ -15,6 +15,7 @@ from jarify.rules.no_select_star import NoSelectStarRule
|
|
|
15
15
|
from jarify.rules.no_select_star_in_cte import NoSelectStarInCteRule
|
|
16
16
|
from jarify.rules.no_unused_cte import NoUnusedCteRule
|
|
17
17
|
from jarify.rules.prefer_group_by_all import PreferGroupByAllRule
|
|
18
|
+
from jarify.rules.prefer_neq_operator import PreferNeqOperatorRule
|
|
18
19
|
from jarify.rules.prefer_using_over_on import PreferUsingOverOnRule
|
|
19
20
|
from jarify.rules.trailing_commas import TrailingCommasRule
|
|
20
21
|
|
|
@@ -58,5 +59,7 @@ def get_default_rules(config: JarifyConfig) -> list[FormatterRule]:
|
|
|
58
59
|
rules.append(ConsistentEmptyArrayRule(severity=config.consistent_empty_array))
|
|
59
60
|
if config.no_select_star_in_cte != "off":
|
|
60
61
|
rules.append(NoSelectStarInCteRule(severity=config.no_select_star_in_cte))
|
|
62
|
+
if config.prefer_neq_operator != "off":
|
|
63
|
+
rules.append(PreferNeqOperatorRule(severity=config.prefer_neq_operator))
|
|
61
64
|
|
|
62
65
|
return rules
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Rule: rewrite <> to != in inequality expressions."""
|
|
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 PreferNeqOperatorRule(FormatterRule):
|
|
12
|
+
"""Prefer != over <> for inequality comparisons.
|
|
13
|
+
|
|
14
|
+
Both operators are equivalent in DuckDB, but != is the modern standard.
|
|
15
|
+
|
|
16
|
+
**Format**: The JarifyGenerator's ``neq_sql`` override always emits ``!=``,
|
|
17
|
+
so ``apply()`` is a no-op here — the rewrite happens at generation time.
|
|
18
|
+
|
|
19
|
+
**Lint**: Because sqlglot normalises both ``!=`` and ``<>`` to the same
|
|
20
|
+
``exp.NEQ`` AST node, the lint check cannot distinguish the two from the
|
|
21
|
+
AST alone. The check flags every ``NEQ`` node so that users running
|
|
22
|
+
``jarify lint`` without first formatting are told to prefer ``!=``.
|
|
23
|
+
False positives (SQL already using ``!=``) are harmless: running
|
|
24
|
+
``jarify fmt`` confirms the canonical form and silences the flag.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, severity: str = "warn") -> None:
|
|
28
|
+
self.severity = severity
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def name(self) -> str:
|
|
32
|
+
return "prefer-neq-operator"
|
|
33
|
+
|
|
34
|
+
def apply(self, tree: exp.Expression) -> exp.Expression:
|
|
35
|
+
# The JarifyGenerator.neq_sql override handles the rewrite to `!=`.
|
|
36
|
+
return tree
|
|
37
|
+
|
|
38
|
+
def check(self, tree: exp.Expression) -> list[LintViolation]:
|
|
39
|
+
if self.severity == "off":
|
|
40
|
+
return []
|
|
41
|
+
violations: list[LintViolation] = []
|
|
42
|
+
for node in tree.find_all(exp.NEQ):
|
|
43
|
+
_line, _col = _node_pos(node)
|
|
44
|
+
violations.append(
|
|
45
|
+
LintViolation(
|
|
46
|
+
rule=self.name,
|
|
47
|
+
severity=self.severity,
|
|
48
|
+
message="Prefer != over <> for inequality comparisons",
|
|
49
|
+
line=_line,
|
|
50
|
+
column=_col,
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
return violations
|
|
@@ -225,3 +225,27 @@ class TestGroupByPerLine:
|
|
|
225
225
|
out, _ = format_sql("SELECT a, count(*) FROM t GROUP BY a")
|
|
226
226
|
assert "GROUP BY" in out
|
|
227
227
|
assert "a" in out
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class TestTrailingRustFmtPlaceholder:
|
|
231
|
+
"""Trailing whole-line Rust format placeholders must land *before* the
|
|
232
|
+
statement-terminating semicolon, not after it.
|
|
233
|
+
|
|
234
|
+
Regression for: formatter inserts ';' between the last SQL clause and a
|
|
235
|
+
trailing {placeholder}, producing invalid SQL when Rust substitutes a
|
|
236
|
+
non-empty value (e.g. 'WHERE seller_key = ...').
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
def test_placeholder_before_semicolon(self):
|
|
240
|
+
sql = "SELECT a, b\nFROM t\n{filter}"
|
|
241
|
+
out, _ = format_sql(sql)
|
|
242
|
+
lines = out.splitlines()
|
|
243
|
+
filter_idx = next(i for i, line in enumerate(lines) if "{filter}" in line)
|
|
244
|
+
semi_idx = next(i for i, line in enumerate(lines) if line.strip() == ";")
|
|
245
|
+
assert filter_idx < semi_idx, f"{{filter}} (line {filter_idx}) must appear before ';' (line {semi_idx}):\n{out}"
|
|
246
|
+
|
|
247
|
+
def test_idempotent(self):
|
|
248
|
+
sql = "SELECT a, b\nFROM t\n{filter}"
|
|
249
|
+
out, _ = format_sql(sql)
|
|
250
|
+
out2, _ = format_sql(out)
|
|
251
|
+
assert out == out2, f"Formatting is not idempotent:\nFirst pass:\n{out}\nSecond pass:\n{out2}"
|
|
@@ -308,6 +308,23 @@ class TestViolationPositions:
|
|
|
308
308
|
assert v.line is not None, "line should not be None"
|
|
309
309
|
|
|
310
310
|
|
|
311
|
+
class TestPreferNeqOperator:
|
|
312
|
+
def test_warns_on_diamond_operator(self):
|
|
313
|
+
sql = "SELECT a FROM t WHERE x <> y"
|
|
314
|
+
rules = _lint(sql)
|
|
315
|
+
assert "prefer-neq-operator" in rules
|
|
316
|
+
|
|
317
|
+
def test_warns_on_multiple_neq_expressions(self):
|
|
318
|
+
sql = "SELECT a FROM t WHERE x <> y AND p <> q"
|
|
319
|
+
rules = _lint(sql)
|
|
320
|
+
assert rules.count("prefer-neq-operator") == 2
|
|
321
|
+
|
|
322
|
+
def test_off_disables_rule(self):
|
|
323
|
+
sql = "SELECT a FROM t WHERE x <> y"
|
|
324
|
+
rules = _lint(sql, prefer_neq_operator="off")
|
|
325
|
+
assert "prefer-neq-operator" not in rules
|
|
326
|
+
|
|
327
|
+
|
|
311
328
|
class TestRustFormatSyntax:
|
|
312
329
|
"""SQL files used as Rust format-string templates must not emit parse errors."""
|
|
313
330
|
|
|
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.2 → jarify-0.4.0}/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.2 → jarify-0.4.0}/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.2 → jarify-0.4.0}/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.2 → jarify-0.4.0}/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
|
|
File without changes
|
|
File without changes
|
{jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/select/where_eq_alignment_nested_subquery.expected.sql
RENAMED
|
File without changes
|
{jarify-0.3.2 → jarify-0.4.0}/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.2 → jarify-0.4.0}/tests/fixtures/templates/rust_fmt_consecutive_placeholders.input.sql
RENAMED
|
File without changes
|
{jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/templates/rust_fmt_ctas_body_placeholder.expected.sql
RENAMED
|
File without changes
|
{jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/templates/rust_fmt_ctas_body_placeholder.input.sql
RENAMED
|
File without changes
|
{jarify-0.3.2 → jarify-0.4.0}/tests/fixtures/templates/rust_fmt_indented_placeholders.expected.sql
RENAMED
|
File without changes
|
{jarify-0.3.2 → jarify-0.4.0}/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
|