From e0675c3191ebd355b2c4e9a3facfd73e0dcb1a14 Mon Sep 17 00:00:00 2001 From: Svetlin Ralchev Date: Sun, 27 Sep 2026 11:25:26 +0400 Subject: [PATCH] feat!: return last_insert_rowid as i64, as sqlx-sqlite does `QueryResult::last_insert_rowid` now returns `i64` rather than `Option`, with 0 when the statement inserted no row -- the signature of sqlx-sqlite's `SqliteQueryResult`, so code written against it compiles unchanged on D1 and Durable Objects. That includes sqlc-gen-sqlx's SQLite output, whose `:execlastid` functions return `result.last_insert_rowid()` as an `i64`. `QueryResult::new` takes an `i64` to match. Rowids start at 1 unless a row is given 0 explicitly, so 0 is unambiguous in practice; `RETURNING` is the way to be certain. Extending a result still keeps the last non-zero rowid, so a trailing SELECT does not erase the INSERT before it. BREAKING CHANGE: `last_insert_rowid()` returns `i64`, and 0 where it returned `None`. `QueryResult::new` takes `last_insert_rowid: i64`. --- crates/sqlx-cloudflare-core/src/result.rs | 30 ++++++++++++------- crates/sqlx-cloudflare-d1/src/js.rs | 2 +- .../tests/worker/src/lib.rs | 2 +- crates/sqlx-cloudflare-do/README.md | 4 +-- crates/sqlx-cloudflare-do/src/js.rs | 2 +- .../tests/worker/src/lib.rs | 4 +-- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/crates/sqlx-cloudflare-core/src/result.rs b/crates/sqlx-cloudflare-core/src/result.rs index 3af298c..c49ea82 100644 --- a/crates/sqlx-cloudflare-core/src/result.rs +++ b/crates/sqlx-cloudflare-core/src/result.rs @@ -2,14 +2,14 @@ #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct QueryResult { rows_affected: u64, - last_insert_rowid: Option, + last_insert_rowid: i64, } impl QueryResult { - /// A result reporting `rows_affected` and, when the backend knows one, - /// `last_insert_rowid`. + /// A result reporting `rows_affected` and `last_insert_rowid`, 0 when the + /// backend reports none. #[must_use] - pub fn new(rows_affected: u64, last_insert_rowid: Option) -> Self { + pub fn new(rows_affected: u64, last_insert_rowid: i64) -> Self { Self { rows_affected, last_insert_rowid, @@ -22,9 +22,15 @@ impl QueryResult { self.rows_affected } - /// The rowid of the last row inserted, when the backend reports one. + /// The rowid of the last row inserted, or 0 when the statement inserted + /// none. + /// + /// An `i64` rather than an `Option`, as sqlx-sqlite reports it, so code + /// written against `SqliteQueryResult` compiles unchanged. Rowids start + /// at 1 unless a row is given 0 explicitly, so 0 is unambiguous in + /// practice; `RETURNING` is the way to be certain. #[must_use] - pub fn last_insert_rowid(&self) -> Option { + pub fn last_insert_rowid(&self) -> i64 { self.last_insert_rowid } } @@ -36,7 +42,9 @@ impl Extend for QueryResult { // The last statement that reported a rowid, not the last // statement: a trailing SELECT reporting none should not erase // the INSERT before it. - self.last_insert_rowid = result.last_insert_rowid.or(self.last_insert_rowid); + if result.last_insert_rowid != 0 { + self.last_insert_rowid = result.last_insert_rowid; + } } } } @@ -93,12 +101,12 @@ mod tests { let mut result = QueryResult::default(); result.extend([ - QueryResult::new(2, Some(7)), - QueryResult::new(1, Some(9)), - QueryResult::new(0, None), + QueryResult::new(2, 7), + QueryResult::new(1, 9), + QueryResult::new(0, 0), ]); assert_eq!(result.rows_affected(), 3); - assert_eq!(result.last_insert_rowid(), Some(9)); + assert_eq!(result.last_insert_rowid(), 9); } } diff --git a/crates/sqlx-cloudflare-d1/src/js.rs b/crates/sqlx-cloudflare-d1/src/js.rs index 01f9e4c..a67be86 100644 --- a/crates/sqlx-cloudflare-d1/src/js.rs +++ b/crates/sqlx-cloudflare-d1/src/js.rs @@ -199,7 +199,7 @@ fn query_result(result: &D1ResultSys) -> Result { Ok(QueryResult::new( number("changes").map_or(0, whole_number_u64), - number("last_row_id").map(whole_number_i64), + number("last_row_id").map_or(0, whole_number_i64), )) } diff --git a/crates/sqlx-cloudflare-d1/tests/worker/src/lib.rs b/crates/sqlx-cloudflare-d1/tests/worker/src/lib.rs index 0d090ad..ebe7cb5 100644 --- a/crates/sqlx-cloudflare-d1/tests/worker/src/lib.rs +++ b/crates/sqlx-cloudflare-d1/tests/worker/src/lib.rs @@ -299,7 +299,7 @@ async fn execute_reports_changes(conn: &mut D1Connection) -> Outcome { ensure(inserted.rows_affected() == 1, "insert rows_affected")?; ensure( - inserted.last_insert_rowid() == Some(id), + inserted.last_insert_rowid() == id, format!( "last_insert_rowid {:?}, id {id}", inserted.last_insert_rowid() diff --git a/crates/sqlx-cloudflare-do/README.md b/crates/sqlx-cloudflare-do/README.md index 82955fe..8ec4dd4 100644 --- a/crates/sqlx-cloudflare-do/README.md +++ b/crates/sqlx-cloudflare-do/README.md @@ -146,8 +146,8 @@ let names: Vec = conn returning the error. - **`rows_affected` comes from `changes()`.** Like SQLite's own count, it covers the rows the statement itself inserted, updated or deleted, not those - its triggers touched. A statement that wrote nothing reports 0 and no - `last_insert_rowid`. + its triggers touched. A statement that wrote nothing reports 0 for both + `rows_affected` and `last_insert_rowid`. - **No `sqlx::migrate!`.** Create the schema from the object's constructor. - **No derived TEXT enums.** `#[derive(sqlx::Type)]` works for `#[sqlx(transparent)]` newtypes and `#[repr(i32)]` enums, but sqlx only diff --git a/crates/sqlx-cloudflare-do/src/js.rs b/crates/sqlx-cloudflare-do/src/js.rs index c366e55..75f5880 100644 --- a/crates/sqlx-cloudflare-do/src/js.rs +++ b/crates/sqlx-cloudflare-do/src/js.rs @@ -117,7 +117,7 @@ pub(crate) fn execute( let result = QueryResult::new( u64::try_from(changes).map_err(|error| Error::Decode(error.into()))?, - Some(rowid), + rowid, ); Ok((rows, result)) diff --git a/crates/sqlx-cloudflare-do/tests/worker/src/lib.rs b/crates/sqlx-cloudflare-do/tests/worker/src/lib.rs index 98166ae..3782d27 100644 --- a/crates/sqlx-cloudflare-do/tests/worker/src/lib.rs +++ b/crates/sqlx-cloudflare-do/tests/worker/src/lib.rs @@ -360,7 +360,7 @@ async fn execute_reports_changes(conn: &mut DoConnection) -> Outcome { ensure(inserted.rows_affected() == 1, "insert rows_affected")?; ensure( - inserted.last_insert_rowid() == Some(id), + inserted.last_insert_rowid() == id, format!( "last_insert_rowid {:?}, id {id}", inserted.last_insert_rowid() @@ -735,7 +735,7 @@ async fn select_reports_no_changes(conn: &mut DoConnection) -> Outcome { format!("SELECT rows_affected {}", selected.rows_affected()), )?; ensure( - selected.last_insert_rowid().is_none(), + selected.last_insert_rowid() == 0, format!( "SELECT last_insert_rowid {:?}", selected.last_insert_rowid()