{"repo":"tconbeer/sqlfmt","free":true,"listed":false,"github":"https://github.com/tconbeer/sqlfmt","clone":"git clone https://github.com/tconbeer/sqlfmt.git","description":"sqlfmt formats your dbt SQL files so you don't have to","language":"Python","stars":542,"topics":["dbt","formatter","python","sql"],"license":null,"category":"dev_tool","readme_excerpt":"# sqlfmt\n\n[![PyPI](https://img.shields.io/pypi/v/shandy-sqlfmt)](https://pypi.org/project/shandy-sqlfmt/)\n[![Downloads](https://static.pepy.tech/personalized-badge/shandy-sqlfmt?period=month&units=international_system&left_color=grey&right_color=orange&left_text=downloads/mo)](https://pepy.tech/project/shandy-sqlfmt)\n[![Test](https://github.com/tconbeer/sqlfmt/actions/workflows/test.yml/badge.svg?branch=main&event=push)](https://github.com/tconbeer/sqlfmt/actions/workflows/test.yml)\n\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/shandy-sqlfmt)\n![Runs on Linux | MacOS | Windows](https://img.shields.io/badge/runs%20on-Linux%20%7C%20MacOS%20%7C%20Windows-blue)\n\n\nsqlfmt formats your dbt SQL files so you don't have to. It is similar in nature to black, gofmt, \nand rustfmt (but for SQL). \n\n1. **sqlfmt promotes collaboration.** An auto-formatter makes it easier to collaborate with your team and solicit contributions from new people. You will never have to mention (or argue about) code style in code reviews again.\n2. **sqlfmt is fast.** Forget about formatting your code, and spend your time on business logic instead. sqlfmt processes hundreds of files per second and only operates on files that have changed since the last run.\n3. **sqlfmt works with Jinja.** It formats the code that users look at, and therefore doesn't need to know anything about what happens after the templates are rendered.\n3. **sqlfmt integrates with your workflow.** As a CLI written in Python, it's easy to install locally on any OS and run in CI. Plays well with dbt, pre-commit, SQLFluff, VSCode, and GitHub Actions. sqlfmt powers the dbt Cloud IDE's Format button.\n\nsqlfmt is not configurable, except for line length. It enforces a single style. sqlfmt maintains comments and some extra newlines, but largely ignores all indentation and line breaks in the input file.\n\nsqlfmt is not a linter. It does not parse your code into an AST; it just lexes it and tracks a small subset of tokens that impact formatting. This lets us \"do one thing and do it well:\" sqlfmt is very fast, and easier to maintain and extend than linters that need a full SQL grammar.\n\nFor now, sqlfmt only works on `select`, `delete`, `grant`, `revoke`, and `create function` statements (which is all you need if you use sqlfmt with a dbt project). It is being extended to additional DDL and DML. Visit [this tracking issue](https://github.com/tconbeer/sqlfmt/issues/262) for more information.\n\n## Documentation\n\nPlease visit [docs.sqlfmt.com](https://docs.sqlfmt.com) for more information on Getting Started, Integrations, the sqlfmt Style, and an API Reference. Or keep reading for an excerpt from the full docs.\n\n### Installation\n\n#### Try it first\nWant to test out sqlfmt on a query before you install it? Go to [sqlfmt.com](https://sqlfmt.com) to use the interactive, web-based version.\n\n#### Recommended Installation: Use uv\n\nsqlfmt is a command-line tool that is built in Python and runs on MacOS, Linux, and Windows. It is distributed\non PyPI under the name `shandy-sqlfmt`. There are many ways to install and run it, but we strongly \nrecommend using [uv](https://docs.astral.sh/uv):\n\n1. [Install uv](https://docs.astral.sh/uv/getting-started/installation/#standalone-installer). From a POSIX shell, run:\n\n    ```bash\n    curl -LsSf https://astral.sh/uv/install.sh | sh\n    ```\n\n    Or using Windows Powershell:\n\n    ```pwsh\n    powershell -ExecutionPolicy ByPass -c \"irm https://astral.sh/uv/install.ps1 | iex\"\n    ```\n\n2. Install sqlfmt as a tool using uv:\n\n    ```bash\n    uv tool install \"shandy-sqlfmt[jinjafmt]\"\n    ```\n\n    This command will install sqlfmt into an isolated environment and add it to your PATH so you can easily run the executable.\n\n    :::tip\n    Depending on your shell and OS, you may need single or double quotes around `shandy-sqlfmt[jinjafmt]`.\n    :::\n\n3. Test the installation; run sqlfmt with no arguments:\n\n    ```bash\n    sqlfmt\n    ```\n\n    You should see some ASCII art and help text.\n\n:::warning PyPI Names\nThe PyPI distribtuion is `shandy-sqlfmt`, NOT `sqlfmt`, which is a different (unrelated but not malicious) package.\nThis is unfortunate, but the author cannot do anything about it.\n:::\n\n#### Other Installation Options\n\n1. **Use pip or something pip-like:**\n\n    If you know what you’re doing, after installing Python 3.9 or above and activating your virtual environment, install `shandy-sqlfmt` using pip, pipx, poetry, or any other program that can install Python packages from PyPI:\n\n    ```bash\n    pip install \"shandy-sqlfmt[jinjafmt]\"\n    ```\n\n2. **Use Docker**\n\n    You can skip installation altogether and pull the official Docker image instead. See [the docs](./using-container) on running sqlfmt in a container.\n\n\n### Using sqlfmt\n\n:::danger Before You Begin\n**sqlfmt may not always produce the formatted output you want.** It might even break your SQL syntax. It is **highly recommended** to only run sqlfmt on files in a version control system (like git), so that it is easy for you to revert any changes made by sqlfmt. On your first run, be sure to make a commit before running sqlfmt.\n\nFor more on sqlfmt's maturity see [Maturity and Stability](https://sqlfmt.com/docs/versioning/).\n:::\n\n\nsqlfmt is a command-line tool. It works on any posix terminal and on Windows Powershell. If you have used the Python code formatter *Black*, the sqlfmt commands will look familiar. \n\n:::tip\nThe code snippets below are commands that can be typed into your terminal, after installing sqlfmt.\n:::\n\nTo list commands and options:\n\n```bash\nsqlfmt --help\n```\n\nIf you want to format all `.sql` and `.sql.jinja` files in your current working directory (and all nested directories), simply type (note the trailing `.`, denoting the current directory):\n\n```bash\nsqlfmt .\n```\n\nIf you don't want to format the files you have on disk, you can use the `--check` or `--diff` options. sqlfmt will exit with code 1 if the files on disk are not properly formatted:\n\n```bash\nsqlfmt --check .\nsqlfmt --diff .\n```\n\nsqlfmt can also format code passed through standard input (`stdin`) by passing `-` as the files argument. The formatted code will be output to `stdout` (all other output from sqlfmt is routed to `stderr`):\n```bash\necho \"select 1\" | sqlfmt -\n```\n\n\n#### Configuring sqlfmt using pyproject.toml\n\nAny command-line option for sqlfmt can also be set in a `pyproject.toml` file, under a `[tool.sqlfmt]` section header. Options passed at the command line will override the settings in the config file. [See the docs](https://docs.sqlfmt.com/getting-started/configuring-sqlfmt) for more information.\n\n#### The jinjafmt extra\n\nsqlfmt loves properly-formatted jinja, too.\n\n[See the docs](https://docs.sqlfmt.com/getting-started/formatting-jinja) for more information about using the `jinjafmt` extra or disabling jinja formatting.\n\n### Using sqlfmt with different SQL dialects\n\nsqlfmt's rules are simple, which means it does not have to parse every single token in your query. This allows nearly all SQL dialects to be formatted using sqlfmt's default \"polyglot\" dialect, which requires no configuration.\n\nThe exception to this is [ClickHouse](https://docs.sqlfmt.com/dialects/#clickhouse), which is case-sensitive where other dialects are not. To prevent the lowercasing of function names, database identifiers, and aliases, use the `--dialect clickhouse` option when running sqlfmt. For example,\n\n```bash\n$ sqlfmt . --dialect clickhouse\n```\n\nThis can also be configured using the `pyproject.toml` file:\n\n```toml\n[tool.sqlfmt]\ndialect = \"clickhouse\"\n```\n\nNote that with this option, sqlfmt will not lowercase **most** non-reserved keywords, even common ones like `sum` or `count`. See (and please join) [this discussion](https://github.com/tconbeer/sqlfmt/discussions/229) for more on this topic.\n\n### Integrations\n\nsqlfmt plays nicely with other analytics engineering tools. For more information, [see the docs](https://docs.sqlfmt.com/category/integrations).\n\n#### dbt\n\nsqlfmt was built for dbt, so only [minimal configuration](https://docs.sqlfmt.com/integrations/dbt) is required. We recommend excluding your `target` and `dbt_packages` directories from formatting. You can do this with the command-line `--exclude` option, or by setting `exclude` in your `pyproject.toml` file:\n\n```toml\n[tool.sqlfmt]\nexclude=[\"target/**/*\", \"dbt_packages/**/*\"]\n```\n\n#### Other Integrations\n\nConfig for other integrations is detailed in the docs linked below:\n\n- [pre-commit](https://docs.sqlfmt.com/integrations/pre-commit)\n- [SQLFluff](https://docs.sqlfmt.com/integrations/sqlfluff)\n- [VSCode](https://docs.sqlfmt.com/integrations/vs-code)\n\n\n## The sqlfmt style\nThe only thing you can configure with sqlfmt is the desired line length of the formatted file. You can do this with the `--line-length` or `-l` options. The default is 88.\n\nsqlfmt borrows elements from well-accepted styles from other programming languages. It places opening brackets on the same line as preceding function names (like *black* for python and *1TBS* for C). It indents closing brackets to the same depth as the opening bracket (this is extended to statements that must be closed, like `case` and `end`).\n\nThe sqlfmt style is as simple as possible, with little-to-no special-casing of formatting concerns. While at first blush, this may not create a format that is as \"nice\" or \"expressive\" as hand-crafted indentation, over time, as you grow accustomed to the style, formatting becomes transparent and the consistency will allow you to jump between files, projects, and even companies much faster.\n\n[Read More](https://docs.sqlfmt.com/style/)\n\n### Why lowercase?\nBecause SQL is code! But there are [other good reasons too](https://docs.sqlfmt.com/style/#why-lowercase).\n\n### Why trailing commas?\nUsing trailing commas follows the convention of every other written language and programming language. [But wait, there's more.](https://docs.sqlfmt.com/style/#why-trailing-commas)\n\n## Contributing\n\n[![Code style: black](h","default_branch":"main","files":237,"tree":[".coveragerc",".github/FUNDING.yml",".github/ISSUE_TEMPLATE/bug-and-bad-formatting-report.md",".github/renovate.json",".github/workflows/primer.yml",".github/workflows/publish.yml",".github/workflows/release.yml",".github/workflows/static.yml",".github/workflows/test.yml",".gitignore",".pre-commit-config.yaml",".pre-commit-hooks.yaml",".python-version","CHANGELOG.md","CODE_OF_CONDUCT.md","Dockerfile","LICENSE","Makefile","README.md","pyproject.toml","src/sqlfmt/__init__.py","src/sqlfmt/__main__.py","src/sqlfmt/actions.py","src/sqlfmt/analyzer.py","src/sqlfmt/api.py","src/sqlfmt/cache.py","src/sqlfmt/cli.py","src/sqlfmt/comment.py","src/sqlfmt/config.py","src/sqlfmt/dialect.py","src/sqlfmt/exception.py","src/sqlfmt/jinjafmt.py","src/sqlfmt/line.py","src/sqlfmt/merger.py","src/sqlfmt/mode.py","src/sqlfmt/node.py","src/sqlfmt/node_manager.py","src/sqlfmt/operator_precedence.py","src/sqlfmt/py.typed","src/sqlfmt/query.py","src/sqlfmt/query_formatter.py","src/sqlfmt/report.py","src/sqlfmt/rule.py","src/sqlfmt/rules/__init__.py","src/sqlfmt/rules/clone.py","src/sqlfmt/rules/common.py","src/sqlfmt/rules/core.py","src/sqlfmt/rules/function.py","src/sqlfmt/rules/grant.py","src/sqlfmt/rules/jinja.py","src/sqlfmt/rules/pragma.py","src/sqlfmt/rules/unsupported.py","src/sqlfmt/rules/warehouse.py","src/sqlfmt/segment.py","src/sqlfmt/splitter.py","src/sqlfmt/tokens.py","src/sqlfmt_primer/__init__.py","src/sqlfmt_primer/__main__.py","src/sqlfmt_primer/primer.py","stubs/tqdm.pyi","tests/__init__.py","tests/conftest.py","tests/data/config/dialect_config.toml","tests/data/config/dialect_name_config.toml","tests/data/config/exclude_config.toml","tests/data/config/invalid_key_config.toml","tests/data/config/invalid_toml_config.toml","tests/data/config/valid_sqlfmt_config.toml","tests/data/fast/errors/900_bad_token.sql","tests/data/fast/errors/910_unopened_multiline.sql","tests/data/fast/errors/911_unopened_bracket.sql","tests/data/fast/errors/920_unterminated_multiline.sql","tests/data/fast/preformatted/001_select_1.sql","tests/data/fast/preformatted/002_select_from_where.sql","tests/data/fast/preformatted/003_literals.sql","tests/data/fast/preformatted/004_with_select.sql","tests/data/fast/preformatted/005_fmt_off.sql","tests/data/fast/preformatted/006_has_bom.sql","tests/data/fast/unformatted/100_select_case.sql","tests/data/fast/unformatted/101_multiline.sql","tests/data/fast/unformatted/102_lots_of_comments.sql","tests/data/fast/unformatted/103_window_functions.sql","tests/data/fast/unformatted/104_joins.sql","tests/data/fast/unformatted/105_fmt_off.sql","tests/data/fast/unformatted/106_leading_commas.sql","tests/data/preformatted/001_select_1.sql","tests/data/preformatted/002_select_from_where.sql","tests/data/preformatted/003_literals.sql","tests/data/preformatted/004_with_select.sql","tests/data/preformatted/005_fmt_off.sql","tests/data/preformatted/006_fmt_off_447.sql","tests/data/preformatted/007_fmt_off_comments.sql","tests/data/preformatted/008_reserved_names.sql","tests/data/preformatted/009_empty.sql","tests/data/preformatted/010_comment_only.sql","tests/data/preformatted/011_triple_quotes.sql","tests/data/preformatted/301_multiline_jinjafmt.sql","tests/data/preformatted/302_jinjafmt_multiline_str.sql","tests/data/preformatted/303_jinjafmt_more_mutliline_str.sql","tests/data/preformatted/400_create_table.sql","tests/data/preformatted/401_create_row_access_policy.sql","tests/data/preformatted/402_alter_table.sql","tests/data/unformatted/100_select_case.sql","tests/data/unformatted/101_multiline.sql","tests/data/unformatted/102_lots_of_comments.sql","tests/data/unformatted/103_window_functions.sql","tests/data/unformatted/104_joins.sql","tests/data/unformatted/105_fmt_off.sql","tests/data/unformatted/106_leading_commas.sql","tests/data/unformatted/107_jinja_blocks.sql","tests/data/unformatted/108_test_block.sql","tests/data/unformatted/109_lateral_flatten.sql","tests/data/unformatted/110_other_identifiers.sql","tests/data/unformatted/111_chained_boolean_between.sql","tests/data/unformatted/112_semicolons.sql","tests/data/unformatted/113_utils_group_by.sql","tests/data/unformatted/114_unions.sql","tests/data/unformatted/115_select_star_except.sql","tests/data/unformatted/116_chained_booleans.sql","tests/data/unformatted/117_whitespace_in_tokens.sql","tests/data/unformatted/118_within_group.sql","tests/data/unformatted/119_psycopg_placeholders.sql","tests/data/unformatted/120_array_literals.sql","tests/data/unformatted/121_stubborn_merge_edge_cases.sql","tests/data/unformatted/122_values.sql","tests/data/unformatted/123_spark_keywords.sql","tests/data/unformatted/124_bq_compound_types.sql","tests/data/unformatted/125_numeric_literals.sql","tests/data/unformatted/126_blank_lines.sql","tests/data/unformatted/127_more_comments.sql","tests/data/unformatted/128_double_slash_comments.sql","tests/data/unformatted/129_duckdb_joins.sql","tests/data/unformatted/130_athena_data_types.sql","tests/data/unformatted/131_assignment_statement.sql","tests/data/unformatted/132_spark_number_literals.sql","tests/data/unformatted/133_for_else.sql","tests/data/unformatted/134_databricks_type_hints.sql","tests/data/unformatted/135_star_columns.sql","tests/data/unformatted/136_databricks_variant.sql","tests/data/unformatted/200_base_model.sql","tests/data/unformatted/201_basic_snapshot.sql","tests/data/unformatted/202_unpivot_macro.sql","tests/data/unformatted/203_gitlab_email_domain_type.sql","tests/data/unformatted/204_gitlab_tag_validation.sql","tests/data/unformatted/205_rittman_hubspot_deals.sql","tests/data/unformatted/206_gitlab_prep_geozone.sql","tests/data/unformatted/207_rittman_int_journals.sql","tests/data/unformatted/208_rittman_int_plan_breakout_metrics.sql","tests/data/unformatted/209_rittman_int_web_events_sessionized.sql","tests/data/unformatted/210_gitlab_gdpr_delete.sql","tests/data/unformatted/211_http_2019_cdn_17_20.sql","tests/data/unformatted/212_http_2019_cms_14_02.sql","tests/data/unformatted/213_gitlab_fct_sales_funnel_target.sql","tests/data/unformatted/214_get_unique_attributes.sql","tests/data/unformatted/215_gitlab_get_backup_table_command.sql","tests/data/unformatted/216_gitlab_zuora_revenue_revenue_contract_line_source.sql","tests/data/unformatted/217_dbt_unit_testing_csv.sql","tests/data/unformatted/218_multiple_c_comments.sql","tests/data/unformatted/219_any_all_agg.sql","tests/data/unformatted/220_clickhouse_joins.sql","tests/data/unformatted/300_jinjafmt.sql","tests/data/unformatted/400_create_fn_and_select.sql","tests/data/unformatted/401_explain_select.sql","tests/data/unformatted/402_delete_from_using.sql","tests/data/unformatted/403_grant_revoke.sql","tests/data/unformatted/404_create_function_pg_examples.sql","tests/data/unformatted/405_create_function_snowflake_examples.sql","tests/data/unformatted/406_create_function_bq_examples.sql","tests/data/unformatted/407_alter_function_pg_examples.sql","tests/data/unformatted/408_alter_function_snowflake_examples.sql","tests/data/unformatted/409_create_external_function.sql","tests/data/unformatted/410_create_warehouse.sql","tests/data/unformatted/411_create_clone.sql","tests/data/unformatted/412_pragma.sql","tests/data/unformatted/900_create_view.sql","tests/data/unformatted/998_unsupported_ddl_with_jinja.sql","tests/data/unformatted/999_unsupported_ddl.sql","tests/data/unit_tests/test_actions/test_handle_potentially_nested_tokens.sql","tests/data/unit_tests/test_analyzer/test_case_statement_parsing.sql","tests/data/unit_tests/test_analyzer/test_cte_parsing.sql","tests/data/unit_tests/test_analyzer/test_jinja_block_parsing.sql","tests/data/unit_tests/test_analyzer/test_multiline_parsing.sql","tests/data/unit_tests/test_analyzer/test_simple_query_parsing.sql","tests/data/unit_tests/test_api/test_file_discovery/a_directory/nested_directory/another_file.sql","tests/data/unit_tests/test_api/test_file_discovery/a_directory/nested_directory/j2_extension.sql.jinja","tests/data/unit_tests/test_api/test_file_discovery/a_directory/one_file.sql","tests/data/unit_tests/test_api/test_file_discovery/a_directory/symlink_source_directory/symlink_file.sql","tests/data/unit_tests/test_api/test_file_discovery/a_directory/symlink_target_directory","tests/data/unit_tests/test_api/test_file_discovery/top_level_file.sql","tests/data/unit_tests/test_api/test_file_discovery/top_level_file.two.sql","tests/data/unit_tests/test_line/test_formatting_disabled.sql","tests/data/unit_tests/test_merger/test_fix_standalone_operators.sql","tests/data/unit_tests/test_merger/test_maybe_stubbornly_merge.sql","tests/data/unit_tests/test_merger/test_merge_chained_parens.sql","tests/data/unit_tests/test_merger/test_merge_inline_comments.sql","tests/data/unit_tests/test_merger/test_merge_lines_split_by_operators.sql","tests/data/unit_tests/test_merger/test_merge_operators_before_children.sql","tests/data/unit_tests/test_merger/test_no_merge_databricks_query_hints.sql","tests/data/unit_tests/test_merger/test_no_merge_formatting_disabled.sql","tests/data/unit_tests/test_merger/test_no_merge_operator_sequences_across_commas.sql","tests/data/unit_tests/test_merger/test_no_merge_short_multiline_nodes.sql","tests/data/unit_tests/test_merger/test_segment_continues_operator_sequence.sql","tests/data/unit_tests/test_merger/test_split_into_segments.sql","tests/data/unit_tests/test_node/test_is_the_and_after_the_between_operator.sql","tests/data/unit_tests/test_node_manager/test_disabled_formatting.sql","tests/data/unit_tests/test_node_manager/test_jinja_depth.sql","tests/data/unit_tests/test_node_manager/test_union_depth.sql","tests/data/unit_tests/test_splitter/test_comment_split_impact_on_open_brackets.sql","tests/data/unit_tests/test_splitter/test_jinja_block_split.sql","tests/data/unit_tests/test_splitter/test_simple_comment_split.sql","tests/data/unit_tests/test_splitter/test_very_long_single_line.sql","tests/functional_tests/test_end_to_end.py","tests/functional_tests/test_general_formatting.py","tests/unit_tests/test_actions.py","tests/unit_tests/test_analyzer.py","tests/unit_tests/test_api.py","tests/unit_tests/test_cache.py","tests/unit_tests/test_cli.py","tests/unit_tests/test_comment.py","tests/unit_tests/test_config.py","tests/unit_tests/test_dialect.py","tests/unit_tests/test_exception.py","tests/unit_tests/test_formatter.py","tests/unit_tests/test_jinjafmt.py","tests/unit_tests/test_line.py","tests/unit_tests/test_merger.py","tests/unit_tests/test_mode.py","tests/unit_tests/test_node.py","tests/unit_tests/test_node_manager.py","tests/unit_tests/test_operator_precedence.py","tests/unit_tests/test_query.py","tests/unit_tests/test_report.py","tests/unit_tests/test_rule.py","tests/unit_tests/test_segment.py","tests/unit_tests/test_splitter.py","tests/util.py","uv.lock"],"storefront":"/r/tconbeer","claimed":false,"request_supported":{"post":"https://gitbuyer.com/r/tconbeer/sqlfmt/request-supported","requests":0},"note":"indexed from public GitHub; nothing is for sale on this page. Clone it from GitHub. Paid listings live at /search."}