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
6 changes: 5 additions & 1 deletion gren.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
"WebSocketServer.Connection",
"Sqlite",
"Sqlite.Decode",
"Sqlite.Encode"
"Sqlite.Decode.Row",
"Sqlite.Encode",
"Sqlite.Encode.Row",
"Sqlite.Function",
"Sqlite.Aggregate"
],
"gren-version": "0.6.0 <= v < 0.7.0",
"dependencies": {
Expand Down
449 changes: 391 additions & 58 deletions integration-tests/sqlite/src/Main.gren

Large diffs are not rendered by default.

83 changes: 80 additions & 3 deletions src/Gren/Kernel/Sqlite.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import Gren.Kernel.Json exposing (wrap, unwrap)
import Json.Decode as Decode exposing (decodeValue)
import Result exposing (isOk)
import Sqlite.Encode as SqliteEncode exposing (toJson)
import Sqlite.Encode.Row as SqliteEncodeRow exposing (toJson)
import Sqlite.Decode as SqliteDecode exposing (toJson)
import Sqlite.Aggregate as SqliteAggregate exposing (Entering, Exiting)
import Maybe exposing (Just, Nothing)

*/
Expand Down Expand Up @@ -52,6 +54,75 @@ var _Sqlite_close = function (db) {
});
};

var _Sqlite_function = F3(function (name, func, db) {
return __Scheduler_binding(function (callback) {
try {
const options = {
deterministic: true,
directOnly: true,
useBigIntArguments: false,
varargs: true,
};
const wrappedFunc = function (...args) {
const jsonArgs = args.map((v) => __Json_wrap(v));
const result = func(jsonArgs);
if (__Result_isOk(result)) {
// The triple `.a` gets the OK result, the SQLite encode
// value, and then grabs the actual value that needs to
// be returned
return result.a.a.a;
} else {
return null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to throw an exception here in order to return a detailed Error? Would be helpful, I think, to see why func(jsonArgs) !== Ok

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. I was surprised to see any type of failure resulting in a NoResults error.

@blaix blaix Sep 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for when the encoding/decoding fails on the Gren side.

}
};
db.function(name, options, wrappedFunc);
callback(__Scheduler_succeed({}));
} catch (e) {
callback(_Sqlite_constructError(e));
}
});
});

var _Sqlite_aggregate = F5(function (name, init, func, result, db) {
return __Scheduler_binding(function (callback) {
try {
const wrappedFunc = function (direction) {
var env = __SqliteAggregate_Entering;
if (direction == "inverse") {
env = __SqliteAggregate_Exiting;
}
return function (state, ...args) {
const jsonArgs = args.map((v) => __Json_wrap(v));
const result = A3(func, env, state, jsonArgs);
if (__Result_isOk(result)) {
return result.a;
} else {
return null;
Comment thread
robinheghan marked this conversation as resolved.
}
};
};
const options = {
deterministic: true,
directOnly: true,
useBigIntArguments: false,
varargs: true,
start: () => {
return init;
},
step: wrappedFunc("step"),
result: (state) => {
return result(state).a.a;
},
inverse: wrappedFunc("inverse"),
};
db.aggregate(name, options);
callback(__Scheduler_succeed({}));
} catch (e) {
callback(_Sqlite_constructError(e));
}
});
});

var _Sqlite_backup = F3(function (destination, pages, db) {
return __Scheduler_binding(function (callback) {
try {
Expand All @@ -78,7 +149,9 @@ var _Sqlite_foldl = F4(function (query, db, func, acc) {
try {
var acc_ = acc;
const prepped = db.prepare(query.__$query);
const params = __Json_unwrap(__SqliteEncode_toJson(query.__$parameters));
const params = __Json_unwrap(
__SqliteEncodeRow_toJson(query.__$parameters),
);
const rowDecoder = __SqliteDecode_toJson(query.__$rowDecoder);

for (const value of prepped.iterate(params)) {
Expand Down Expand Up @@ -107,7 +180,9 @@ var _Sqlite_getMaybeOne = F2(function (query, db) {
return __Scheduler_binding(function (callback) {
try {
const prepped = db.prepare(query.__$query);
const params = __Json_unwrap(__SqliteEncode_toJson(query.__$parameters));
const params = __Json_unwrap(
__SqliteEncodeRow_toJson(query.__$parameters),
);
const rowDecoder = __SqliteDecode_toJson(query.__$rowDecoder);
const iterator = prepped.iterate(params);

Expand Down Expand Up @@ -149,7 +224,9 @@ var _Sqlite_executeMany = F3(function (statement, values, db) {
} else {
for (const val of values) {
lastResult = prepped.run(
__Json_unwrap(__SqliteEncode_toJson(statement.__$parameters(val))),
__Json_unwrap(
__SqliteEncodeRow_toJson(statement.__$parameters(val)),
),
);
}
}
Expand Down
10 changes: 6 additions & 4 deletions src/Sqlite.gren
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import FileSystem
import FileSystem.Path exposing (Path)
import Json.Decode as Decode
import Sqlite.Decode
import Sqlite.Decode.Row
import Sqlite.Encode
import Sqlite.Encode.Row
import Task exposing (Task)
import Gren.Kernel.Sqlite

Expand Down Expand Up @@ -103,8 +105,8 @@ runBackup (Backup { destination, pages, db }) =

type alias Query value =
{ query : String
, parameters : Array Sqlite.Encode.Value
, rowDecoder : Sqlite.Decode.Decoder value
, parameters : Array Sqlite.Encode.Row.Value
, rowDecoder : Sqlite.Decode.Row.Decoder value
}


Expand Down Expand Up @@ -143,7 +145,7 @@ foldl func acc db query =

type alias Statement =
{ statement : String
, parameters : Array Sqlite.Encode.Value
, parameters : Array Sqlite.Encode.Row.Value
}


Expand Down Expand Up @@ -178,7 +180,7 @@ executeAll db statements =
executeForEach :
Database
-> { statement : String
, parameters : value -> Array Sqlite.Encode.Value
, parameters : value -> Array Sqlite.Encode.Row.Value
}
-> Array value
-> Task Error ExecutionSummary
Expand Down
88 changes: 88 additions & 0 deletions src/Sqlite/Aggregate.gren
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
module Sqlite.Aggregate exposing ( .. )

{-|-}

import Sqlite
import Sqlite.Decode as Decode
import Sqlite.Encode as Encode
import Json.Encode
import Json.Decode
import Task exposing ( Task )


type Function state =
Function (Direction -> state -> Array Json.Encode.Value -> Result String state)


{-|-}
type Direction
= Entering
| Exiting


{-|-}
type Aggregate state
= Aggregate
{ init : state
, function : Function state
, result : state -> Encode.Value
}


aggregate : { init : state, function: Function state, result : state -> Encode.Value } -> Aggregate state
aggregate { init, function, result } =
Aggregate
{ init = init
, function = function
, result = result
}


{-|-}
start : (state -> Function state) -> Function state
start func =
Function <| \direction state args ->
when func state is
Function innerFunc ->
innerFunc direction state args


{-|-}
arg : Decode.Decoder a -> (a -> Function state) -> Function state
arg decoder func =
Function <| \direction state args ->
when Array.popFirst args is
Just { first = first, rest = rest } ->
when Json.Decode.decodeValue (Decode.unwrap decoder) first is
Ok val ->
when func val is
Function innerFunc ->
innerFunc direction state rest

Err _err ->
Err "decoding error"

Nothing ->
Err "not enough arguments passed"


{-|-}
return : (Direction -> state) -> Function state
return func =
Function <| \direction _state args ->
when args is
[] ->
Ok (func direction)

many ->
Err "too many arguments passed"


{-|-}
register : String -> Sqlite.Database -> Aggregate state -> Task x {}
register name db (Aggregate { init, function, result }) =
let
(Function unwrappedFunc) =
function
in
Gren.Kernel.Sqlite.aggregate name init unwrappedFunc result db
Loading
Loading