{"id":25437664,"url":"https://github.com/pablormira/sql_formatter","last_synced_at":"2025-10-06T22:10:23.215Z","repository":{"id":40588456,"uuid":"312074378","full_name":"PabloRMira/sql_formatter","owner":"PabloRMira","description":"A Python based SQL formatter","archived":false,"fork":false,"pushed_at":"2022-04-30T22:03:12.000Z","size":1558,"stargazers_count":46,"open_issues_count":12,"forks_count":10,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-30T05:58:21.463Z","etag":null,"topics":["formatter","pre-commit","python","sql","sql-formatter"],"latest_commit_sha":null,"homepage":"https://pablormira.github.io/sql_formatter/","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PabloRMira.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-11-11T19:51:29.000Z","updated_at":"2025-05-16T08:01:51.000Z","dependencies_parsed_at":"2022-08-09T23:50:20.111Z","dependency_job_id":null,"html_url":"https://github.com/PabloRMira/sql_formatter","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/PabloRMira/sql_formatter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PabloRMira%2Fsql_formatter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PabloRMira%2Fsql_formatter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PabloRMira%2Fsql_formatter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PabloRMira%2Fsql_formatter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PabloRMira","download_url":"https://codeload.github.com/PabloRMira/sql_formatter/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PabloRMira%2Fsql_formatter/sbom","scorecard":{"id":107206,"data":{"date":"2025-08-11","repo":{"name":"github.com/PabloRMira/sql_formatter","commit":"d5b11c177103e95b01c26ed02594d5b4563d7ada"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.5,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 1/27 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/main.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:7: update your workflow using https://app.stepsecurity.io/secureworkflow/PabloRMira/sql_formatter/main.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:8: update your workflow using https://app.stepsecurity.io/secureworkflow/PabloRMira/sql_formatter/main.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/main.yml:14","Warn: pipCommand not pinned by hash: .github/workflows/main.yml:15","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"33 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-cr5q-6q9f-rq6q","Warn: Project is vulnerable to: GHSA-j6gc-792m-qgm2","Warn: Project is vulnerable to: GHSA-pj73-v5mw-pm9j","Warn: Project is vulnerable to: GHSA-48wp-p9qv-4j64","Warn: Project is vulnerable to: GHSA-4qw4-jpp4-8gvp","Warn: Project is vulnerable to: GHSA-636f-xm5j-pj9m","Warn: Project is vulnerable to: GHSA-7vh7-fw88-wj87","Warn: Project is vulnerable to: GHSA-fmx4-26r3-wxpf","Warn: Project is vulnerable to: GHSA-2qc6-mcvw-92cw","Warn: Project is vulnerable to: GHSA-2rr5-8q37-2w7h","Warn: Project is vulnerable to: GHSA-353f-x4gh-cqq8","Warn: Project is vulnerable to: GHSA-59gp-qqm7-cw4j","Warn: Project is vulnerable to: GHSA-5w6v-399v-w3cc","Warn: Project is vulnerable to: GHSA-cgx6-hpwq-fhv5","Warn: Project is vulnerable to: GHSA-crjr-9rc5-ghw8","Warn: Project is vulnerable to: GHSA-fq42-c5rg-92c2","Warn: Project is vulnerable to: GHSA-gx8x-g87m-h5q6","Warn: Project is vulnerable to: GHSA-jc36-42cf-vqwj","Warn: Project is vulnerable to: GHSA-mrxw-mxhj-p664","Warn: Project is vulnerable to: GHSA-pxvg-2qj5-37jq","Warn: Project is vulnerable to: GHSA-r95h-9x8f-r3f7","Warn: Project is vulnerable to: GHSA-v6gp-9mmm-c6p5","Warn: Project is vulnerable to: GHSA-vvfq-8hwr-qm4m","Warn: Project is vulnerable to: GHSA-xc9x-jj77-9p9j","Warn: Project is vulnerable to: GHSA-xh29-r2w5-wx8m","Warn: Project is vulnerable to: GHSA-xxx9-3xcr-gjj3","Warn: Project is vulnerable to: GHSA-2rxp-v6pw-ch6m","Warn: Project is vulnerable to: GHSA-4xqq-m2hx-25v8","Warn: Project is vulnerable to: GHSA-5866-49gr-22v4","Warn: Project is vulnerable to: GHSA-r55c-59qm-vjw6","Warn: Project is vulnerable to: GHSA-vg3r-rm7w-2xgh","Warn: Project is vulnerable to: GHSA-vmwr-mc7x-5vc3","Warn: Project is vulnerable to: GHSA-5cm2-9h8c-rvfx"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 16 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-15T11:19:14.425Z","repository_id":40588456,"created_at":"2025-08-15T11:19:14.425Z","updated_at":"2025-08-15T11:19:14.425Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274729430,"owners_count":25338693,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-11T02:00:13.660Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["formatter","pre-commit","python","sql","sql-formatter"],"created_at":"2025-02-17T09:19:07.982Z","updated_at":"2025-10-06T22:10:18.185Z","avatar_url":"https://github.com/PabloRMira.png","language":"Jupyter Notebook","readme":"# sql_formatter\n\u003e A Python based SQL formatter\n\n\n![CI](https://github.com/PabloRMira/sql_formatter/workflows/CI/badge.svg) [![PyPI](https://img.shields.io/pypi/v/sql-formatter?color=yellow\u0026label=pypi%20version)](https://pypi.org/project/sql-formatter/#description)\n[![Anaconda-Server Badge](https://anaconda.org/pablormira/sql_formatter/badges/version.svg)](https://anaconda.org/pablormira/sql_formatter)\n\n## How to install\n\nVia pip\n\n`pip install sql-formatter`\n\nor via conda\n\n`conda install -c pablormira sql_formatter`\n\n## How to use\n\nFormat your SQL files via the command line\n\n`sql-formatter sql_file.sql sql_file2.sql`\n\nYou can also format all your SQL-files via\n\n`sql-formatter *.sql`\n\nTo format all your SQL files recursively use\n\n`sql-formatter -r \"*.sql\"`\n\n### Controlling maximum length line via truncation\n\nThe `sql_formatter` will try to truncate too long lines in the `SELECT` clause for either\n\n* Function with many arguments\n* `in` with many elements\n\nThe default maximum line length is 82 after line stripping.\n\nYou can control the maximum length line using e.g.\n\n`sql-formatter sql_file.sql --max-line-length=50`\n\n### Usage with `pre-commit`\n\n[pre-commit](https://pre-commit.com) is a nice development tool to automatize the binding of pre-commit hooks. After installation and configuration `pre-commit` will run your hooks before you commit any change. \n\nTo add `sql-formatter` as a hook to your `pre-commit` configuration to format your SQL files before commit, just add the following lines to your `.pre-commit-config.yaml`:\n\n```yaml\nrepos:\n  - repo: https://github.com/PabloRMira/sql_formatter\n    rev: master\n    hooks:\n    - id: sql_formatter\n```\n\nIf you want to install `sql-formatter` locally and use that instead of using `pre-commit`'s default environment, set `repo: local` in your `.pre-commit-config.yaml` file:\n\n```yaml\nrepos:\n  - repo: local\n    hooks:\n    - id: sql_formatter\n      name: SQL formatter\n      language: system\n      entry: sql-formatter\n      files: \\.sql$\n```\n\nor\n\n```yaml\nrepos:\n  - repo: local\n    hooks:\n    - id: sql_formatter\n      name: SQL formatter\n      language: system\n      entry: sql-formatter --max-line-length=50\n      files: \\.sql$\n```\n\nfor a custom maximum line length truncation of e.g. 50\n\n### Usage in Python\n\nTo exemplify the formatting let's say you have a SQL query like this\n\n```\nexample_sql = \"\"\"\ncreate or replace table mytable as -- mytable example\nseLecT a.asdf, b.qwer, -- some comment here\nc.asdf, -- some comment there\nb.asdf2 frOm table1 as a leFt join \ntable2 as b -- and here a comment\n    on a.asdf = b.asdf  -- join this way\n    inner join table3 as c\non a.asdf=c.asdf\nwhEre a.asdf= 1 -- comment this\nanD b.qwer =2 and a.asdf\u003c=1 --comment that\nor b.qwer\u003e=5\ngroUp by a.asdf\n\"\"\"\n```\n\nThen you can use this package to format it so that it is better readable\n\n```\nfrom sql_formatter.core import format_sql\nprint(format_sql(example_sql))\n```\n\n    CREATE OR REPLACE TABLE mytable AS -- mytable example\n    SELECT a.asdf,\n           b.qwer, -- some comment here\n           c.asdf, -- some comment there\n           b.asdf2\n    FROM   table1 as a\n        LEFT JOIN table2 as b -- and here a comment\n            ON a.asdf = b.asdf -- join this way\n        INNER JOIN table3 as c\n            ON a.asdf = c.asdf\n    WHERE  a.asdf = 1 -- comment this\n       and b.qwer = 2\n       and a.asdf \u003c= 1 --comment that\n        or b.qwer \u003e= 5\n    GROUP BY a.asdf\n\n\nIt can even deal with subqueries and it will correct my favourite simple careless mistake (comma at the end of SELECT statement before of FROM) for you on the flow :-)\n\n```\nprint(format_sql(\"\"\"\nselect asdf, cast(qwer as numeric), -- some comment\nqwer1\nfrom \n(select asdf, qwer, from table1 where asdf = 1) as a\nleft \njoin (select asdf, qwer2 from table2 where qwer2 = 1) as b\non a.asdf = b.asdf\nwhere qwer1 \u003e= 0\n\"\"\"))\n```\n\n    SELECT asdf,\n           cast(qwer as numeric), -- some comment\n           qwer1\n    FROM   (SELECT asdf,\n                   qwer\n            FROM   table1\n            WHERE  asdf = 1) as a\n        LEFT JOIN (SELECT asdf,\n                          qwer2\n                   FROM   table2\n                   WHERE  qwer2 = 1) as b\n            ON a.asdf = b.asdf\n    WHERE  qwer1 \u003e= 0\n\n\nThe formatter is also robust against nested subqueries\n\n```\nprint(format_sql(\"\"\"\nselect field1, field2 from (select field1, \nfield2 from (select field1, field2, \nfield3 from table1 where a=1 and b\u003e=100))\n\"\"\"))\n```\n\n    SELECT field1,\n           field2\n    FROM   (SELECT field1,\n                   field2\n            FROM   (SELECT field1,\n                           field2,\n                           field3\n                    FROM   table1\n                    WHERE  a = 1\n                       and b \u003e= 100))\n\n\nIf you do not want to get some query formatted in your SQL file then you can use the marker `/*skip-formatter*/` in your query to disable formatting for just the corresponding query\n\n```\nfrom sql_formatter.format_file import format_sql_commands\nprint(format_sql_commands(\n\"\"\"\nuse database my_database;\n\n-- My first view --\ncreate or repLace view my_view as\nselect asdf, qwer from table1\nwhere asdf \u003c= 10;\n\n\n/*skip-formatter*/\ncreate oR rePlace tabLe my_table as\nselect asdf\nFrom my_view;\n\"\"\"\n))\n```\n\n    use database my_database;\n    \n    \n    -- My first view --\n    CREATE OR REPLACE VIEW my_view AS\n    SELECT asdf,\n           qwer\n    FROM   table1\n    WHERE  asdf \u003c= 10;\n    \n    \n    /*skip-formatter*/\n    create oR rePlace tabLe my_table as\n    select asdf\n    From my_view;\n    \n\n\n### A note of caution\n\nFor the SQL-formatter to work properly you should meticulously end each of your SQL statements with semicolon (;)\n\nHowever, we have equiped the `sql-formatter` with some basic validations:\n\n* *Forgotten semicolon validation*: The validator will check if the `CREATE` keyword appears more than twice, indicating the user that he / she may have forgotten a semicolon\n* *Unbalanced parenthesis*: The validator will check if there are unbalanced parenthesis in the query\n* *Unbalanced `case when ... end`*: The validator will check if there are `case when` statements without `end` or vice versa\n\n### What `sql_formatter` does not do\n\nThis package is just a SQL formatter and therefore\n\n* cannot parse your SQL queries into e.g. dictionaries\n* cannot validate your SQL queries to be valid for the corresponding database system / provider\n\nUp to now it only formats queries of the form\n\n* `CREATE TABLE / VIEW ...`\n* `SELECT ...`\n\nEvery other SQL commands will remain unformatted, e.g. `INSERT INTO` ...\n\n## Formatting Logic\n\nThe main goal of the `sql_formatter` is to enhance readability and quick understanding of SQL queries via proper formatting. We use **indentation** and **lowercasing / uppercasing** as means to arrange statements / clauses and parameters into context. By **programmatically standardizing** the way to write SQL queries we help the user understand its queries faster.\n\nAs a by-product of using the `sql_formatter`, developer teams can focus on the query logic itself and save time by not incurring into styling decisions, this then begin accomplished by the `sql_formatter`. This is similar to the goal accomplished by the [black package](https://github.com/psf/black) for the Python language, which was also an inspiration for the development of this package for SQL. \n\nWe can summarize the main steps of the formatter as follows:\n\n1. Each query is separated from above by two newlines.\n2. Everything but **main statements\\* / clauses** is lowercased\n\n\\* Main statements:\n\n* CREATE ... TABLE / VIEW table_name AS\n* SELECT (DISTINCT)\n* FROM\n* (LEFT / INNER / RIGHT / OUTER) JOIN\n* UNION\n* ON\n* WHERE\n* GROUP BY\n* ORDER BY\n* OVER\n* PARTITION BY\n\n3. Indentation is used to put parameters into context. Here an easy example:\n\n```sql\nSELECT field1,\n       case when field2 \u003e 1 and\n                 field2 \u003c= 10 and\n                 field1 = 'a' then 1\n            else 0 end as case_field,\n       ...\nFROM   table1\nWHERE  field1 = 1\n   and field2 \u003c= 2\n    or field3 = 5\nORDER BY field1;\n```\n\n\u003e This is a very nice, easy example but things can become more complicated if comments come into play\n\n4. Subqueries are also properly indented, e.g.\n\n```sql\nSELECT a.field1,\n       a.field2,\n       b.field3\nFROM   (SELECT field1,\n               field2\n        FROM   table1\n        WHERE  field1 = 1) as a\n    LEFT JOIN (SELECT field1,\n                      field3\n               FROM   table2) as b\n        ON a.field1 = b.field1;\n```\n\n5. Everything not being a query of the form `CREATE ... TABLE / VIEW` or `SELECT ...` is left unchanged\n\n## Versioning\n\nWe version our package via [semantic versioning](https://semver.org), i.e., \n\n* We use three digits separated by points x1.x2.x3, e.g. 0.5.1\n* We increase x1 (the major version) if we introduce breaking changes\n  * Exception: Versions with 0 at the beginning (e.g. 0.5.1) mean that the package is not stable yet and therefore every new feature could be a breaking change\n* We increase x2 (the minor version) if we introduce a new feature\n* We increase x3 (the patch version) if we fix a bug\n\nNew documentation, refactoring / maintenance of code and admin tasks do not change the versions.\n\nYou can follow the changes introduced by each version in our [CHANGELOG](https://github.com/PabloRMira/sql_formatter/blob/master/CHANGELOG.md)\n\n## How to contribute\n\nSee [CONTRIBUTING](https://github.com/PabloRMira/sql_formatter/blob/master/CONTRIBUTING.md)\n\n## Acknowledgements\n\nThank you very much to Jeremy Howard and all the [nbdev](https://github.com/fastai/nbdev) team for enabling the *fast* and delightful development of this library via the `nbdev` framework.\n\nFor more details on `nbdev`, see its official [tutorial](https://nbdev.fast.ai/tutorial.html)\n\nThank you very much for the developers of the [black](https://github.com/psf/black) package, which was also an inspiration for the development of this package\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpablormira%2Fsql_formatter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpablormira%2Fsql_formatter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpablormira%2Fsql_formatter/lists"}