From 45dd04d5b823a44e11105f904ab9b995f9096d15 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Wed, 8 Jul 2026 10:50:02 +0300 Subject: [PATCH] fix(sqlite): support RETURNING clauses in write queries (#243) --- CHANGELOG.md | 4 ++++ lib/core/database/sqlite_connection.dart | 13 ++++++---- .../core/database/sqlite_connection_test.dart | 24 +++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b418fe2f..6776a31e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- **SQLite / RETURNING clause support (#243)** — support RETURNING clauses for INSERT, UPDATE, and DELETE DML queries in the SQLite database driver, returning the resulting rows to the client. + ## [0.4.7-a] - 2026-06-22 ### Added diff --git a/lib/core/database/sqlite_connection.dart b/lib/core/database/sqlite_connection.dart index d4585d4a..f61e7b76 100644 --- a/lib/core/database/sqlite_connection.dart +++ b/lib/core/database/sqlite_connection.dart @@ -97,18 +97,21 @@ class SqliteConnection { .toLowerCase(); // SQLite can execute PRAGMA, SELECT, EXPLAIN statements, which return data - final isQuery = sqlLower.startsWith('select') || + final isReadOnlyQuery = sqlLower.startsWith('select') || sqlLower.startsWith('pragma') || sqlLower.startsWith('explain') || sqlLower.startsWith('with') || sqlLower.startsWith('values'); - if (isQuery) { + final hasReturning = RegExp(r'\breturning\b').hasMatch(sqlLower); + + if (readOnly && !isReadOnlyQuery) { + throw StateError('Database connection is read-only'); + } + + if (isReadOnlyQuery || hasReturning) { return await _db!.rawQuery(sql, arguments); } else { - if (readOnly) { - throw StateError('Database connection is read-only'); - } await _db!.execute(sql, arguments); return []; } diff --git a/test/core/database/sqlite_connection_test.dart b/test/core/database/sqlite_connection_test.dart index ccf74aee..cd2612f9 100644 --- a/test/core/database/sqlite_connection_test.dart +++ b/test/core/database/sqlite_connection_test.dart @@ -91,6 +91,30 @@ void main() { expect(columns, containsAll(['id', 'name'])); }); + test('executes INSERT, UPDATE, DELETE with RETURNING clause correctly', () async { + await conn.connect(); + + await conn.execute('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)'); + + // INSERT with RETURNING + final insertRes = await conn.execute("INSERT INTO users (name) VALUES ('Alice') RETURNING id, name"); + expect(insertRes, isNotEmpty); + expect(insertRes.first['id'], 1); + expect(insertRes.first['name'], 'Alice'); + + // UPDATE with RETURNING + final updateRes = await conn.execute("UPDATE users SET name = 'Bob' WHERE id = 1 RETURNING id, name"); + expect(updateRes, isNotEmpty); + expect(updateRes.first['id'], 1); + expect(updateRes.first['name'], 'Bob'); + + // DELETE with RETURNING + final deleteRes = await conn.execute("DELETE FROM users WHERE id = 1 RETURNING id, name"); + expect(deleteRes, isNotEmpty); + expect(deleteRes.first['id'], 1); + expect(deleteRes.first['name'], 'Bob'); + }); + test('throws StateError for modify operations in read-only mode', () async { final roConn = SqliteConnection( id: 2,