From sqlc: one command#
scythe migrate sqlc.yamlThis reads your sqlc config (v1 or v2 format), converts query annotations, and generates a scythe.toml. If your database engine lacks scythe backend support for your language, the migration tool stops and reports both sides rather than generating an incomplete config.
What changes in the config format
sqlc.yaml (before):
version: "2"
sql:
- schema: "sql/schema.sql"
queries: "sql/queries.sql"
engine: "postgresql"
gen:
go:
out: "db"
package: "db"scythe.toml (after):
[scythe]
version = "1"
[[sql]]
name = "main"
engine = "postgresql"
schema = ["sql/schema.sql"]
queries = ["sql/queries.sql"]
output = "db"
[sql.gen.go]
target = "pgx"The migration tool picks the driver from your sqlc engine setting: pgx for PostgreSQL, database-sql for MySQL/SQLite/DuckDB/SQL Server, godror for Oracle, gosnowflake for Snowflake — adjust per Configuration if needed.
What changes in query annotations
sqlc (before):
-- name: GetUser :one
SELECT * FROM users WHERE id = $1;
-- name: CreateUser :exec
INSERT INTO users (name, email)
VALUES (sqlc.arg(name), sqlc.arg(email));scythe (after):
-- @name GetUser
-- @returns :one
SELECT * FROM users WHERE id = $1;
-- @name CreateUser
-- @returns :exec
INSERT INTO users (name, email)
VALUES ($1, $2);What has no direct equivalent
sqlc.embed()— replace with explicit column selection.sqlc.slice()— replace with= ANY($1)array parameter.sqlc.narg()converts to a positional parameter with a@paramannotation documenting the original name, but nullability is not inferred from it — add@nullablemanually if the column itself isn't already nullable.
Feature comparison
| Feature | sqlc | scythe |
|---|---|---|
| Annotation style | -- name: Foo :one |
-- @name Foo + -- @returns :one |
| Named parameters | sqlc.arg(name) |
$1, $2, … |
| Config format | YAML | TOML |
| Nullable overrides | Go struct tags | -- @nullable col1, col2 |
| Non-null overrides | Not supported | -- @nonnull col1 |
| JSON column types | Not supported | -- @json data = MyType |
| Deprecation markers | Not supported | -- @deprecated Use V2 |
After migration#
- Review the generated
scythe.toml. - Verify with
scythe check. - Generate code with
scythe generate. - Run
scythe lintto catch issues sqlc might have missed.
Custom type mappings and ORM-specific extensions need manual review after migration — they aren't preserved automatically.
From sqlfluff: command replacement#
Scythe integrates sqruff (a Rust reimplementation of sqlfluff) and adds 23 codegen-aware rules plus 35 security and migration-safety rules on top.
| sqlfluff | scythe |
|---|---|
sqlfluff lint file.sql |
scythe lint file.sql |
sqlfluff fix file.sql |
scythe lint --fix file.sql |
sqlfluff format file.sql |
scythe fmt file.sql |
Configuration moves from .sqlfluff (INI) to scythe.toml:
# .sqlfluff (before)
[sqlfluff]
dialect = postgresql
exclude_rules = LT01,LT02# scythe.toml (after)
[[sql]]
engine = "postgresql"
[lint.sqruff.rules]
"LT01" = "off"
"LT02" = "off"Scythe requires setting each sqruff rule individually under [lint.sqruff.rules], using bare codes without the SQ- prefix. Not all sqlfluff rules are implemented in sqruff — check the sqruff repository for current coverage.
Next Steps#
- CLI Reference for the full flag list on
scythe migrate,scythe lint, andscythe fmt. - Linting for what scythe's own 23 schema-aware rules catch that sqlfluff/sqruff can't.