Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
cfbd721
fix: emit unqualified type_name for Postgres enums and composites
LeadcodeDev Jun 3, 2026
3c132b3
docs: add Rust engineering audit and remediation plan
LeadcodeDev Jun 3, 2026
d67eb39
feat: add SQL identifier quoting module
LeadcodeDev Jun 3, 2026
4742600
fix: quote SQL identifiers per dialect in generated CRUD code
LeadcodeDev Jun 3, 2026
5ca1a07
chore: ignore macOS .DS_Store files
LeadcodeDev Jun 3, 2026
d390391
fix: validate --type-overrides values via syn::parse_str
LeadcodeDev Jun 3, 2026
e941d8f
fix: redact password in database URLs on connection errors
LeadcodeDev Jun 3, 2026
f85e7ff
fix: propagate codegen parse errors instead of process::exit
LeadcodeDev Jun 3, 2026
a1328fb
fix: propagate errors from introspect instead of panicking
LeadcodeDev Jun 3, 2026
c96c3fe
fix: write generated files atomically and reject unsafe filenames
LeadcodeDev Jun 3, 2026
3ecbcb1
ci: add test, fmt, clippy workflow with Postgres+MySQL services
LeadcodeDev Jun 3, 2026
853f1fc
fix: typemap gaps for MySQL bit(1), boolean, Postgres interval, SQLit…
LeadcodeDev Jun 3, 2026
8b71ff7
feat: emit PgHasArrayType impl for Postgres enums and composites
LeadcodeDev Jun 3, 2026
0e2c341
feat: detect enum variant collisions after camelCase conversion
LeadcodeDev Jun 3, 2026
0d6dbf8
fix: wire enum collision check into codegen::generate
LeadcodeDev Jun 3, 2026
d5a4220
feat: sanitize column names that aren't valid Rust identifiers
LeadcodeDev Jun 3, 2026
89d1891
feat: accept Postgres array type names in both _x and x[] notations
LeadcodeDev Jun 3, 2026
dd6c90e
chore: document MSRV as rust 1.75 in both crates
LeadcodeDev Jun 3, 2026
ccb11c1
fix: short-circuit insert_many_transactionally on empty input
LeadcodeDev Jun 3, 2026
810d732
feat: warn when introspection returns no tables/views/enums
LeadcodeDev Jun 3, 2026
c89459b
fix: pick raw-string fence width that avoids embedded #
LeadcodeDev Jun 3, 2026
65fa852
feat: omit schema qualifier for default schemas in generated SQL
LeadcodeDev Jun 3, 2026
c66a189
chore: silence clippy warnings (manual strip_prefix, broken doc list)
LeadcodeDev Jun 3, 2026
ac18eb1
style: apply rustfmt
LeadcodeDev Jun 3, 2026
02af6e1
test: confirm MySQL inline ENUM round-trips via per-variant rename
LeadcodeDev Jun 3, 2026
dc39be5
feat: add --domain-style flag for Postgres domain rendering
LeadcodeDev Jun 3, 2026
a8dad3e
feat: detect SQLite TEXT CHECK (col IN (...)) as implicit enum
LeadcodeDev Jun 3, 2026
7a08978
feat: classify common sqlx errors by SQLSTATE for actionable messages
LeadcodeDev Jun 3, 2026
b339f38
fix: MySQL composite-PK insert uses bound PK values, not LAST_INSERT_ID
LeadcodeDev Jun 3, 2026
104b774
test: add compile-check harness for generated code
LeadcodeDev Jun 3, 2026
6a3c3ac
style: apply rustfmt to compile_check harness
LeadcodeDev Jun 3, 2026
a819c59
feat: add udt_schema field to ColumnInfo
LeadcodeDev Jun 3, 2026
714e163
feat: disambiguate enums/composites/domains across schemas
LeadcodeDev Jun 3, 2026
8eb1807
feat: surface search_path requirement for non-default-schema types
LeadcodeDev Jun 3, 2026
b89be72
fix: stop emitting manual PgHasArrayType impl (conflicts with sqlx::T…
LeadcodeDev Jun 3, 2026
727f865
feat: quote SQL identifiers only when syntactically required
LeadcodeDev Jun 3, 2026
28bf1e8
chore: centralize crate version + dependencies in workspace
LeadcodeDev Jun 3, 2026
b37bb3f
chore: uprade version
LeadcodeDev Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
test:
name: Test (unit + sqlite)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: cargo test --all
run: cargo test --all --all-features

fmt:
name: Format check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: cargo fmt --check
run: cargo fmt --all -- --check

clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- name: cargo clippy
run: cargo clippy --all-targets --all-features -- -D warnings

test-postgres:
name: Test (Postgres E2E)
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: sqlx_gen_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 3s
--health-retries 10
env:
PG_URL: postgres://postgres:postgres@localhost:5432/sqlx_gen_test
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: cargo test postgres e2e
run: cargo test --all-features --test e2e_postgres -- --include-ignored
continue-on-error: true

test-mysql:
name: Test (MySQL E2E)
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: sqlx_gen_test
ports:
- 3306:3306
options: >-
--health-cmd "mysqladmin ping -uroot -proot"
--health-interval 5s
--health-timeout 3s
--health-retries 10
env:
MYSQL_URL: mysql://root:root@localhost:3306/sqlx_gen_test
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: cargo test mysql e2e
run: cargo test --all-features --test e2e_mysql -- --include-ignored
continue-on-error: true
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
/target
.vscode
.vscode
.DS_Store
**/.DS_Store
/docs/superpowers/
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,43 @@
[workspace]
members = ["crates/sqlx_gen", "crates/sqlx_gen_macros"]
resolver = "2"

# Shared package metadata. Each crate inherits via `*.workspace = true`,
# so version bumps happen here and propagate everywhere — sqlx-gen and
# sqlx-gen-macros can never drift apart.
[workspace.package]
version = "0.5.6"
edition = "2021"
rust-version = "1.75"
license = "MIT"
repository = "https://github.com/LeadcodeDev/sqlx-gen"
keywords = ["sqlx", "codegen", "postgres", "mysql", "sqlite"]
categories = ["database", "development-tools"]

# Shared dependency definitions. The internal sqlx-gen-macros entry pins the
# version to whatever sqlx_gen itself ships, so the proc-macro and runtime
# crates are always released together.
[workspace.dependencies]
sqlx-gen-macros = { path = "crates/sqlx_gen_macros", version = "0.5.6" }
sqlx = { version = "0.8", features = [
"runtime-tokio",
"tls-rustls-ring",
"postgres",
"mysql",
"sqlite",
"chrono",
"uuid",
"json",
] }
tokio = { version = "1", features = ["full"] }
clap = { version = "4", features = ["derive", "env"] }
heck = "0.5"
thiserror = "2"
quote = "1"
proc-macro2 = "1"
syn = "2"
prettyplease = "0.2"
log = "0.4"
env_logger = "0.11"
tempfile = "3"
pretty_assertions = "1"
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,40 @@ All generated types include `#[sqlx_gen(...)]` annotations for tooling:
| Domain type | `#[sqlx_gen(kind = "domain")]` |
| Primary key field | `#[sqlx_gen(primary_key)]` |

## PostgreSQL — multi-schema setup

When you introspect more than one schema (`-s auth,billing,public`), enums,
composite types, and domains carry an unqualified
`#[sqlx(type_name = "...")]` because `sqlx::postgres::PgTypeInfo::with_name`
does not accept `schema.type`. For PG to resolve those types at runtime, the
connection must include every non-default schema in its `search_path`.

`sqlx-gen` prints the exact `SET search_path` snippet it needs after
introspection. Apply it on every new connection via an `after_connect`
hook:

```rust
use sqlx::postgres::PgPoolOptions;

let pool = PgPoolOptions::new()
.after_connect(|conn, _meta| Box::pin(async move {
sqlx::query("SET search_path TO public, auth, billing")
.execute(conn).await?;
Ok(())
}))
.connect(&url).await?;
```

If two schemas declare a type with the same name (e.g. both `auth.role` and
`billing.role`), sqlx-gen prefixes the Rust identifier with the schema
PascalCase form (`AuthRole`, `BillingRole`) to keep the generated code
unambiguous. The bare PascalCase form (`Role`) is reserved for the default
schema and for unique names.

The `sqlx_gen::codegen::required_pg_search_path(&schema_info)` helper returns
the list of non-default schemas you need to include — handy when wiring this
into a build script.

## License

MIT
52 changes: 23 additions & 29 deletions crates/sqlx_gen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
[package]
name = "sqlx-gen"
version = "0.5.5"
edition = "2021"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
keywords.workspace = true
categories.workspace = true
description = "Generate Rust structs from database schema introspection"
license = "MIT"
repository = "https://github.com/LeadcodeDev/sqlx-gen"
readme = "../../README.md"
keywords = ["sqlx", "codegen", "postgres", "mysql", "sqlite"]
categories = ["database", "development-tools"]

[[bin]]
name = "sqlx-gen"
Expand All @@ -27,31 +28,24 @@ cli = [
"dep:prettyplease",
"dep:log",
"dep:env_logger",
"dep:tempfile",
]

[dependencies]
sqlx-gen-macros = { path = "../sqlx_gen_macros", version = "0.5.4" }
sqlx = { version = "0.8", features = [
"runtime-tokio",
"tls-rustls-ring",
"postgres",
"mysql",
"sqlite",
"chrono",
"uuid",
"json",
], optional = true }
tokio = { version = "1", features = ["full"], optional = true }
clap = { version = "4", features = ["derive", "env"], optional = true }
heck = { version = "0.5", optional = true }
thiserror = { version = "2", optional = true }
quote = { version = "1", optional = true }
proc-macro2 = { version = "1", optional = true }
syn = { version = "2", optional = true }
prettyplease = { version = "0.2", optional = true }
log = { version = "0.4", optional = true }
env_logger = { version = "0.11", optional = true }
sqlx-gen-macros.workspace = true
sqlx = { workspace = true, optional = true }
tokio = { workspace = true, optional = true }
clap = { workspace = true, optional = true }
heck = { workspace = true, optional = true }
thiserror = { workspace = true, optional = true }
quote = { workspace = true, optional = true }
proc-macro2 = { workspace = true, optional = true }
syn = { workspace = true, optional = true }
prettyplease = { workspace = true, optional = true }
log = { workspace = true, optional = true }
env_logger = { workspace = true, optional = true }
tempfile = { workspace = true, optional = true }

[dev-dependencies]
pretty_assertions = "1"
tempfile = "3"
pretty_assertions.workspace = true
tempfile.workspace = true
Loading
Loading