Migration Guide

Use this guide to roll out sqlriver across an existing codebase with minimal churn.

Migrating from holywell 1.x

holywell was renamed to sqlriver in 2.0.0 at the SQL Style Guide author's request. Formatting behavior is unchanged; only the name is.

Swap the package:

npm uninstall holywell
npm install sqlriver

Rename your config and ignore files:

git mv .holywellrc.json .sqlriverrc.json
git mv .holywellignore .sqlriverignore

The old names still work: when the new file is absent and the old one is present, sqlriver reads it and prints a one-line deprecation notice to stderr.

Update the pre-commit hook repo and ids:

repos:
  - repo: https://github.com/vinsidious/sqlriver
    rev: v2.0.0
    hooks:
      - id: sqlriver        # was: holywell
      - id: sqlriver-check  # was: holywell-check

Update anything else that invokes the binary by name -- CI jobs (npm install -g sqlriver, sqlriver --check), npm scripts, editor format-on-save commands, and shell completions (sqlriver --completion bash). If you set HOLYWELL_FORCE_FALLBACK_GLOB, rename it to SQLRIVER_FORCE_FALLBACK_GLOB; the old name is still honored.

1) Understand the style model

sqlriver is intentionally opinionated:

  • Optional .sqlriverrc.json for operational settings (maxLineLength, maxDepth, maxInputSize, maxTokenCount, dialect, strict, recover)
  • No style toggles (indent/casing/alignment modes)
  • Deterministic output

Plan for one-time diffs when first applying formatting.

2) Known behavior changes

Before you run sqlriver on existing SQL, understand what will change:

Keywords become uppercase

All SQL keywords are uppercased. select becomes SELECT, inner join becomes INNER JOIN, etc.

ALL-CAPS identifiers become lowercase

ALL-CAPS unquoted identifiers are lowercased to avoid shouting: MYTABLE becomes mytable, USERID becomes userid. Mixed-case identifiers are preserved as-is: MyTable stays MyTable, userId stays userId.

Exception: projection aliases (column aliases in SELECT) are not lowercased, even when ALL-CAPS. SELECT col AS TOTAL keeps TOTAL unchanged.

Quoted identifiers are preserved exactly. "MyTable" stays "MyTable".

Whitespace is normalized

  • Trailing whitespace is stripped from every line
  • Original indentation is replaced with river-aligned formatting
  • Blank lines inside statements are removed
  • A trailing newline is added at the end of each statement

Warning: case-sensitive databases

Most databases (PostgreSQL, MySQL, SQL Server) treat unquoted identifiers as case-insensitive, so lowercasing ALL-CAPS identifiers has no effect on query behavior. Mixed-case identifiers are preserved, so this is rarely an issue.

However, if your database or collation is configured to treat unquoted identifiers as case-sensitive (uncommon, but possible in some configurations), the ALL-CAPS lowercasing could change which table or column is referenced. In this case:

  1. Use quoted identifiers ("MyTable") for any names that depend on specific casing
  2. Or run sqlriver --check first to preview changes before applying --write

What does NOT change

  • String literals are preserved exactly ('Hello World' stays 'Hello World')
  • Numeric literals are preserved
  • Quoted identifiers are preserved
  • Comments are preserved (though their position may shift with reformatting)
  • SQL semantics are not altered -- only whitespace and casing change

3) Start in check-only mode

Run in CI without writing changes:

npx sqlriver --check "**/*.sql"

If your repo has generated/vendor SQL, exclude it first:

npx sqlriver --check --ignore "vendor/**" --ignore "generated/**" "**/*.sql"

Or define ignores once in .sqlriverignore:

vendor/**
generated/**

4) Preview changes before writing

Use --dry-run to see what would change without modifying any files:

npx sqlriver --dry-run "**/*.sql"

This implies --check --diff, showing a unified diff for each file that would be reformatted.

5) Batch-format in one commit

Create a dedicated formatting commit:

npx sqlriver --write "**/*.sql"
git add -A
git commit -m "style: apply sqlriver"

Keeping formatting separate from feature changes makes review and rollback easier.

6) Enforce in CI

After baseline formatting, enforce check mode in CI:

npx sqlriver --check "**/*.sql"

Useful companion flag for PR logs:

npx sqlriver --check --list-different "**/*.sql"

7) Add pre-commit guard

Run only on staged SQL files:

npx sqlriver --check $(git diff --cached --name-only -- '*.sql')

Or auto-fix staged files before commit:

npx sqlriver --write $(git diff --cached --name-only -- '*.sql')
git add $(git diff --cached --name-only -- '*.sql')

8) Monorepo rollout strategy

For large repos, migrate package-by-package:

  1. Format one domain/folder.
  2. Merge.
  3. Enable CI check for that scope.
  4. Repeat until full coverage.

9) Handling unsupported syntax

Both the CLI and the formatSQL API default to recovery mode (recover: true). Statements that fail structural parsing are preserved as raw SQL where possible.

To make parse failures block CI:

npx sqlriver --strict --check "**/*.sql"

For custom tooling, pass recover: false to opt into strict mode.

For a current dialect-by-dialect coverage snapshot, see SQL Dialect Support.