Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 16 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ name: CI

on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read
Expand All @@ -11,6 +18,7 @@ jobs:
unit:
name: Unit tests (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
Expand All @@ -26,8 +34,15 @@ jobs:
- run: rvpm doc

database-e2e:
name: PostgreSQL, MySQL, and SQLite end-to-end
name: PostgreSQL ${{ matrix.postgres }}, MySQL, and SQLite end-to-end
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
postgres: ["16.9-alpine", "17.5-alpine"]
env:
NEST_POSTGRES_IMAGE: postgres:${{ matrix.postgres }}
steps:
- uses: actions/checkout@v4
- uses: martian56/setup-raven@v1
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Changelog

## 0.2.0 - 2026-07-15

- Surface SQLite binding, execution, reading, finalization, and close failures.
- Discover SQLite result column labels for wildcard and raw queries.
- Preserve the logical behavior of empty `IN` and `NOT IN` predicates.
- Compile equality comparisons with SQL NULL as `IS NULL` and `IS NOT NULL`.
- Encode booleans portably for PostgreSQL, MySQL, and SQLite.
- Generate valid offset-only queries for every bundled dialect.
- Detect renamed or missing migrations that were already applied.
- Add a MySQL adapter mode for `NO_BACKSLASH_ESCAPES` sessions.
- Update all bundled database drivers to version 0.2.0.
- Expand Docker coverage for transactions, PostgreSQL `RETURNING`, adapter recovery,
migration history, and MySQL SQL modes.
- Test PostgreSQL 16 and 17 combinations in CI.

## 0.1.0 - 2026-07-14

- Initial release.
64 changes: 51 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ database libraries.
Nest is under active initial development. It requires Raven 2.26.1 or later (the
transaction helper passes a closure whose parameter is typed with the adapter,
which earlier compilers could not resolve). The current foundation is tested end
to end with Raven 2.26.1, PostgreSQL 17, MySQL 8.0.43, and bundled SQLite.
to end with Raven 2.26.1, PostgreSQL 16 and 17, MySQL 8.0.43, and bundled SQLite.

## What works

- Parameterized SELECT, INSERT, UPDATE, and DELETE builders.
- PostgreSQL, MySQL, and SQLite dialect-specific placeholders and identifier
quoting.
- INNER and LEFT joins, aliases, predicates, ordering, limits, and offsets.
- IN and NOT IN predicates with parameterized lists (an empty list matches nothing).
- IN and NOT IN predicates with parameterized lists. Empty `IN` matches nothing;
empty `NOT IN` keeps every row.
- INSERT ... RETURNING on PostgreSQL and SQLite to read generated columns.
- Transactions: begin, commit, rollback, and a transaction() helper that commits
on success and rolls back on error.
Expand All @@ -31,14 +32,14 @@ to end with Raven 2.26.1, PostgreSQL 17, MySQL 8.0.43, and bundled SQLite.

## Install

When the first Nest release is published, add it to `rv.toml`:
Add Nest to `rv.toml`:

```toml
[dependencies]
"github.com/martian56/nest-orm" = "v0.1.0"
"github.com/martian56/nest-orm" = "v0.2.0"
```

Nest currently depends on version `v0.1.0` of all three Raven database drivers.
Nest currently depends on version `v0.2.0` of all three Raven database drivers.

## Quick start with SQLite

Expand Down Expand Up @@ -88,13 +89,18 @@ let my = mysql(mysql_connection)
```

The adapters own their connection wrapper. Call `db.close()` when finished.
The SQLite adapter also exposes `db.try_close()` when shutdown failures need to
be handled explicitly.

## Query builders

Values are always sent through driver parameters. Nest validates and quotes
identifiers separately, so neither values nor identifiers can silently become
arbitrary SQL syntax.

Passing `null()` to `where_eq` or `where_not_eq` produces `IS NULL` or
`IS NOT NULL`; ordered comparisons with NULL are rejected as mistakes.

```raven
let query = select("users")
.column("users.id")
Expand Down Expand Up @@ -128,9 +134,42 @@ let deleted = delete_all(db, delete_from("users").where_eq("id", integer(7)))?
```

`inserted`, `changed`, and `deleted` are `QueryResult` values. Their `affected`
field reports changed rows. `last_insert_id` is populated by MySQL and SQLite;
PostgreSQL applications should select generated values with an explicit query
until Nest adds RETURNING support.
field reports changed rows. `last_insert_id` is populated by MySQL and SQLite.
PostgreSQL and SQLite can return generated values in the same round trip:

```raven
let inserted = insert_one(
db,
insert_into("users").value("name", text("Ada")).returning("id"),
)?
let id = inserted.rows[0].int_named("id")?
```

MySQL does not support `INSERT ... RETURNING`; Nest reports that combination at
query compilation, and MySQL-generated ids remain available as
`last_insert_id`.

## Transactions

Use `transaction` to commit a successful closure or roll back its error:

```raven
let result = transaction(db, fun(tx: Sqlite) -> Result<QueryResult, Error> =
insert_one(tx, insert_into("users").value("name", text("Grace")))
)?
```

`begin`, `commit`, and `rollback` are also public for explicit control. The
helper preserves the closure's original error if its best-effort rollback also
fails.

## MySQL SQL modes

The default `mysql(connection)` adapter matches MySQL's standard string-literal
rules. If the connection session enables `NO_BACKSLASH_ESCAPES`, construct it
with `mysql_no_backslash_escapes(connection)` so placeholders inside raw SQL are
scanned with the same rules. Builder-generated queries remain parameterized in
either mode.

## Model mapping

Expand Down Expand Up @@ -182,6 +221,8 @@ let report = migrate(db, migrations)?
Nest creates `nest_migrations`, applies only missing versions, and requires the
input list to contain unique positive versions in increasing order.
`rollback_last(db, migrations)` reverts the latest applied migration.
Nest rejects an applied migration whose version is missing from the compiled
history or whose name changed, preventing silent history drift.

PostgreSQL and SQLite migrations run in a transaction. MySQL automatically
commits many DDL statements, so Nest records a MySQL migration only after all
Expand Down Expand Up @@ -235,11 +276,8 @@ shell script to keep the database containers running after tests.

- Values use the text parameter formats exposed by the three current drivers.
- Results are collected in memory; streaming cursors are not implemented.
- SQLite wildcard/raw-query columns use generated names when the underlying
driver cannot expose column labels. Explicit `.column(...)` selections have
stable names.
- PostgreSQL RETURNING, upserts, aggregates, subqueries, connection pools,
relations, eager loading, and derive-generated model mappings are planned.
- Upserts, aggregates, subqueries, connection pools, relations, eager loading,
and derive-generated model mappings are not implemented yet.
- Migration SQL is intentionally explicit because column types and many DDL
operations differ across databases.

Expand Down
Loading
Loading