From e61bfd5962b0c99dd0941925c0ff39fc092ccbc5 Mon Sep 17 00:00:00 2001 From: Joey Bright Date: Sat, 8 Aug 2026 16:26:59 -0700 Subject: [PATCH 1/2] Implement `Sqlite.foldl` - Implements the `Sqlite.foldl` function - Replaces `Sqlite.getAll` implementation with `foldl`. Uses `Array.Builder` to (hopefully) avoid any performance issues of using immutable arrays for larger query result sets vs. the previous kernel implementation which mutated an array in place - Adds tests to ensure `Sqlite.foldl` works as expected --- integration-tests/sqlite/src/Main.gren | 32 +++++++++++++++++++++++++- src/Gren/Kernel/Sqlite.js | 9 ++++---- src/Sqlite.gren | 9 +++++++- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/integration-tests/sqlite/src/Main.gren b/integration-tests/sqlite/src/Main.gren index 517db2f..7dd1860 100644 --- a/integration-tests/sqlite/src/Main.gren +++ b/integration-tests/sqlite/src/Main.gren @@ -115,7 +115,30 @@ tests fsPerm = , await "Open db for multiple inserts" createTestDb <| \db -> await "Insert all people" (insertPeople people db) <| \summary -> concat - [ test "summary.changes is 1 (only reports from last execution)" <| \_ -> + [ awaitError "foldl with a bad decoder produces expected error" + (Sqlite.foldl (\_ acc -> acc + 1) 0 db { allPeopleQ | rowDecoder = badPersonDecoder }) <| \err -> + test "placeholder" <| \_ -> + when err is + Sqlite.DecodingError _ -> + Expect.pass + + _ -> + Expect.fail "Expected `DecodingError` from failed decoding" + , await "can foldl over people and count them" + (Sqlite.foldl (\_ acc -> acc + 1) 0 db allPeopleQ) <| \count -> + test "Count is expected" <| \_ -> + Expect.equal count 2 + , await "can foldl over people to build an list of names" + (Sqlite.foldl (\{ name } acc -> Array.pushLast name acc) [] db allPeopleQ) <| \namesArray -> + concat + [ test "Array contains the name Robin" <| \_ -> + Expect.equal (Array.member "Robin" namesArray) True + , test "Array contains the name Justin" <| \_ -> + Expect.equal (Array.member "Justin" namesArray) True + , test "Array is of expected shape" <| \_ -> + Expect.equal namesArray [ "Robin", "Justin" ] + ] + , test "summary.changes is 1 (only reports from last execution)" <| \_ -> Expect.equal 1 summary.changes , test "summary.lastInsertedRowid is > 0" <| \_ -> Expect.greaterThan 0 summary.lastInsertRowid @@ -283,6 +306,13 @@ personDecoder = Decode.succeed { name = name, role = role } +badPersonDecoder : Decoder Person +badPersonDecoder = + Decode.string "namee" <| \name -> + Decode.string "role_" <| \role -> + Decode.succeed { name = name, role = role } + + insertPerson : Person -> Sqlite.Database -> Task Sqlite.Error Sqlite.ExecutionSummary insertPerson person db = Sqlite.execute db diff --git a/src/Gren/Kernel/Sqlite.js b/src/Gren/Kernel/Sqlite.js index f346e0d..5ee456c 100644 --- a/src/Gren/Kernel/Sqlite.js +++ b/src/Gren/Kernel/Sqlite.js @@ -52,10 +52,10 @@ var _Sqlite_close = function (db) { }); }; -var _Sqlite_getAll = F2(function (query, db) { +var _Sqlite_foldl = F4(function (query, db, func, acc) { return __Scheduler_binding(function (callback) { try { - const results = []; + var acc_ = acc; const prepped = db.prepare(query.__$query); const params = __Json_unwrap(__SqliteEncode_toJson(query.__$parameters)); const rowDecoder = __SqliteDecode_toJson(query.__$rowDecoder); @@ -68,15 +68,14 @@ var _Sqlite_getAll = F2(function (query, db) { ); if (__Result_isOk(jsonResult)) { - results.push(jsonResult.a); + acc_ = A2(func, jsonResult.a, acc_); } else { return callback( __Scheduler_fail(__Sqlite_DecodingError(jsonResult.a)), ); } } - - callback(__Scheduler_succeed(results)); + callback(__Scheduler_succeed(acc_)); } catch (e) { callback(_Sqlite_constructError(e)); } diff --git a/src/Sqlite.gren b/src/Sqlite.gren index 68ca15f..66c6207 100644 --- a/src/Sqlite.gren +++ b/src/Sqlite.gren @@ -1,6 +1,7 @@ module Sqlite exposing (..) +import Array.Builder import FileSystem import FileSystem.Path exposing (Path) import Json.Decode as Decode @@ -95,7 +96,13 @@ getMaybeOne db query = getAll : Database -> Query value -> Task Error (Array value) getAll db query = - Gren.Kernel.Sqlite.getAll query db + foldl Array.Builder.pushLast (Array.Builder.empty 0) db query + |> Task.map Array.Builder.toArray + + +foldl : (a -> b -> b) -> b -> Database -> Query a -> Task Error b +foldl func acc db query = + Gren.Kernel.Sqlite.foldl query db func acc -- EXECUTIONS From b649b0929b615a745d515feb54cbf5f19de877f8 Mon Sep 17 00:00:00 2001 From: Joey Bright Date: Sat, 8 Aug 2026 16:38:54 -0700 Subject: [PATCH 2/2] Updated test name to be more descriptive --- integration-tests/sqlite/src/Main.gren | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/sqlite/src/Main.gren b/integration-tests/sqlite/src/Main.gren index 7dd1860..bb9588a 100644 --- a/integration-tests/sqlite/src/Main.gren +++ b/integration-tests/sqlite/src/Main.gren @@ -117,7 +117,7 @@ tests fsPerm = concat [ awaitError "foldl with a bad decoder produces expected error" (Sqlite.foldl (\_ acc -> acc + 1) 0 db { allPeopleQ | rowDecoder = badPersonDecoder }) <| \err -> - test "placeholder" <| \_ -> + test "expected decoding error" <| \_ -> when err is Sqlite.DecodingError _ -> Expect.pass