@@ -317,6 +317,14 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() {
317317 "",
318318 ["number", "string", "number"]
319319 );
320+
321+ // https://www.sqlite.org/c3ref/aggregate_context.html
322+ // void *sqlite3_aggregate_context(sqlite3_context*, int nBytes)
323+ var sqlite3_aggregate_context = cwrap(
324+ "sqlite3_aggregate_context",
325+ "number",
326+ ["number", "number"]
327+ );
320328 var registerExtensionFunctions = cwrap(
321329 "RegisterExtensionFunctions",
322330 "number",
@@ -1223,81 +1231,90 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() {
12231231 return sqlite3_changes(this.db);
12241232 };
12251233
1234+ var extract_blob = function extract_blob(ptr) {
1235+ var size = sqlite3_value_bytes(ptr);
1236+ var blob_ptr = sqlite3_value_blob(ptr);
1237+ var blob_arg = new Uint8Array(size);
1238+ for (var j = 0; j < size; j += 1) {
1239+ blob_arg[j] = HEAP8[blob_ptr + j];
1240+ }
1241+ return blob_arg;
1242+ };
1243+
1244+ var parseFunctionArguments = function parseFunctionArguments(argc, argv) {
1245+ var args = [];
1246+ for (var i = 0; i < argc; i += 1) {
1247+ var value_ptr = getValue(argv + (4 * i), "i32");
1248+ var value_type = sqlite3_value_type(value_ptr);
1249+ var arg;
1250+ if (
1251+ value_type === SQLITE_INTEGER
1252+ || value_type === SQLITE_FLOAT
1253+ ) {
1254+ arg = sqlite3_value_double(value_ptr);
1255+ } else if (value_type === SQLITE_TEXT) {
1256+ arg = sqlite3_value_text(value_ptr);
1257+ } else if (value_type === SQLITE_BLOB) {
1258+ arg = extract_blob(value_ptr);
1259+ } else arg = null;
1260+ args.push(arg);
1261+ }
1262+ return args;
1263+ };
1264+ var setFunctionResult = function setFunctionResult(cx, result) {
1265+ switch (typeof result) {
1266+ case "boolean":
1267+ sqlite3_result_int(cx, result ? 1 : 0);
1268+ break;
1269+ case "number":
1270+ sqlite3_result_double(cx, result);
1271+ break;
1272+ case "string":
1273+ sqlite3_result_text(cx, result, -1, -1);
1274+ break;
1275+ case "object":
1276+ if (result === null) {
1277+ sqlite3_result_null(cx);
1278+ } else if (result.length != null) {
1279+ var blobptr = allocate(result, ALLOC_NORMAL);
1280+ sqlite3_result_blob(cx, blobptr, result.length, -1);
1281+ _free(blobptr);
1282+ } else {
1283+ sqlite3_result_error(cx, (
1284+ "Wrong API use : tried to return a value "
1285+ + "of an unknown type (" + result + ")."
1286+ ), -1);
1287+ }
1288+ break;
1289+ default:
1290+ sqlite3_result_null(cx);
1291+ }
1292+ };
1293+
12261294 /** Register a custom function with SQLite
1227- @example Register a simple function
1228- db.create_function("addOne", function (x) {return x+1;})
1229- db.exec("SELECT addOne(1)") // = 2
1295+ @example <caption> Register a simple function</caption>
1296+ db.create_function("addOne", function (x) {return x+1;})
1297+ db.exec("SELECT addOne(1)") // = 2
12301298
1231- @param {string} name the name of the function as referenced in
1232- SQL statements.
1233- @param {function} func the actual function to be executed.
1234- @return {Database} The database object. Useful for method chaining
1235- */
1299+ @param {string} name the name of the function as referenced in
1300+ SQL statements.
1301+ @param {function} func the actual function to be executed.
1302+ @return {Database} The database object. Useful for method chaining
1303+ */
12361304 Database.prototype["create_function"] = function create_function(
12371305 name,
12381306 func
12391307 ) {
12401308 function wrapped_func(cx, argc, argv) {
1309+ var args = parseFunctionArguments(argc, argv);
12411310 var result;
1242- function extract_blob(ptr) {
1243- var size = sqlite3_value_bytes(ptr);
1244- var blob_ptr = sqlite3_value_blob(ptr);
1245- var blob_arg = new Uint8Array(size);
1246- for (var j = 0; j < size; j += 1) {
1247- blob_arg[j] = HEAP8[blob_ptr + j];
1248- }
1249- return blob_arg;
1250- }
1251- var args = [];
1252- for (var i = 0; i < argc; i += 1) {
1253- var value_ptr = getValue(argv + (4 * i), "i32");
1254- var value_type = sqlite3_value_type(value_ptr);
1255- var arg;
1256- if (
1257- value_type === SQLITE_INTEGER
1258- || value_type === SQLITE_FLOAT
1259- ) {
1260- arg = sqlite3_value_double(value_ptr);
1261- } else if (value_type === SQLITE_TEXT) {
1262- arg = sqlite3_value_text(value_ptr);
1263- } else if (value_type === SQLITE_BLOB) {
1264- arg = extract_blob(value_ptr);
1265- } else arg = null;
1266- args.push(arg);
1267- }
12681311 try {
12691312 result = func.apply(null, args);
12701313 } catch (error) {
12711314 sqlite3_result_error(cx, error, -1);
12721315 return;
12731316 }
1274- switch (typeof result) {
1275- case "boolean":
1276- sqlite3_result_int(cx, result ? 1 : 0);
1277- break;
1278- case "number":
1279- sqlite3_result_double(cx, result);
1280- break;
1281- case "string":
1282- sqlite3_result_text(cx, result, -1, -1);
1283- break;
1284- case "object":
1285- if (result === null) {
1286- sqlite3_result_null(cx);
1287- } else if (result.length != null) {
1288- var blobptr = allocate(result, ALLOC_NORMAL);
1289- sqlite3_result_blob(cx, blobptr, result.length, -1);
1290- _free(blobptr);
1291- } else {
1292- sqlite3_result_error(cx, (
1293- "Wrong API use : tried to return a value "
1294- + "of an unknown type (" + result + ")."
1295- ), -1);
1296- }
1297- break;
1298- default:
1299- sqlite3_result_null(cx);
1300- }
1317+ setFunctionResult(cx, result);
13011318 }
13021319 if (Object.prototype.hasOwnProperty.call(this.functions, name)) {
13031320 removeFunction(this.functions[name]);
@@ -1321,6 +1338,137 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() {
13211338 return this;
13221339 };
13231340
1341+ /** Register a custom aggregate with SQLite
1342+ @example <caption>Register a custom sum function</caption>
1343+ db.create_aggregate("js_sum", {
1344+ init: () => 0,
1345+ step: (state, value) => state + value,
1346+ finalize: state => state
1347+ });
1348+ db.exec("SELECT js_sum(column1) FROM (VALUES (1), (2))"); // = 3
1349+
1350+ @param {string} name the name of the aggregate as referenced in
1351+ SQL statements.
1352+ @param {object} aggregateFunctions
1353+ object containing at least a step function.
1354+ @param {function(): T} [aggregateFunctions.init = ()=>null]
1355+ a function receiving no arguments and returning an initial
1356+ value for the aggregate function. The initial value will be
1357+ null if this key is omitted.
1358+ @param {function(T, any) : T} aggregateFunctions.step
1359+ a function receiving the current state and a value to aggregate
1360+ and returning a new state.
1361+ Will receive the value from init for the first step.
1362+ @param {function(T): any} [aggregateFunctions.finalize = (state)=>state]
1363+ a function returning the result of the aggregate function
1364+ given its final state.
1365+ If omitted, the value returned by the last step
1366+ will be used as the final value.
1367+ @return {Database} The database object. Useful for method chaining
1368+ @template T
1369+ */
1370+ Database.prototype["create_aggregate"] = function create_aggregate(
1371+ name,
1372+ aggregateFunctions
1373+ ) {
1374+ // Default initializer and finalizer
1375+ var init = aggregateFunctions["init"]
1376+ || function init() { return null; };
1377+ var finalize = aggregateFunctions["finalize"]
1378+ || function finalize(state) { return state; };
1379+ var step = aggregateFunctions["step"];
1380+
1381+ if (!step) {
1382+ throw "An aggregate function must have a step function in " + name;
1383+ }
1384+
1385+ // state is a state object; we'll use the pointer p to serve as the
1386+ // key for where we hold our state so that multiple invocations of
1387+ // this function never step on each other
1388+ var state = {};
1389+
1390+ function wrapped_step(cx, argc, argv) {
1391+ // > The first time the sqlite3_aggregate_context(C,N) routine is
1392+ // > called for a particular aggregate function, SQLite allocates N
1393+ // > bytes of memory, zeroes out that memory, and returns a pointer
1394+ // > to the new memory.
1395+ //
1396+ // We're going to use that pointer as a key to our state array,
1397+ // since using sqlite3_aggregate_context as it's meant to be used
1398+ // through webassembly seems to be very difficult. Just allocate
1399+ // one byte.
1400+ var p = sqlite3_aggregate_context(cx, 1);
1401+
1402+ // If this is the first invocation of wrapped_step, call `init`
1403+ //
1404+ // Make sure that every path through the step and finalize
1405+ // functions deletes the value state[p] when it's done so we don't
1406+ // leak memory and possibly stomp the init value of future calls
1407+ if (!Object.hasOwnProperty.call(state, p)) state[p] = init();
1408+
1409+ var args = parseFunctionArguments(argc, argv);
1410+ var mergedArgs = [state[p]].concat(args);
1411+ try {
1412+ state[p] = step.apply(null, mergedArgs);
1413+ } catch (error) {
1414+ delete state[p];
1415+ sqlite3_result_error(cx, error, -1);
1416+ }
1417+ }
1418+
1419+ function wrapped_finalize(cx) {
1420+ var result;
1421+ var p = sqlite3_aggregate_context(cx, 1);
1422+ try {
1423+ result = finalize(state[p]);
1424+ } catch (error) {
1425+ delete state[p];
1426+ sqlite3_result_error(cx, error, -1);
1427+ return;
1428+ }
1429+ setFunctionResult(cx, result);
1430+ delete state[p];
1431+ }
1432+
1433+ if (Object.hasOwnProperty.call(this.functions, name)) {
1434+ removeFunction(this.functions[name]);
1435+ delete this.functions[name];
1436+ }
1437+ var finalize_name = name + "__finalize";
1438+ if (Object.hasOwnProperty.call(this.functions, finalize_name)) {
1439+ removeFunction(this.functions[finalize_name]);
1440+ delete this.functions[finalize_name];
1441+ }
1442+ // The signature of the wrapped function is :
1443+ // void wrapped(sqlite3_context *db, int argc, sqlite3_value **argv)
1444+ var step_ptr = addFunction(wrapped_step, "viii");
1445+
1446+ // The signature of the wrapped function is :
1447+ // void wrapped(sqlite3_context *db)
1448+ var finalize_ptr = addFunction(wrapped_finalize, "vi");
1449+ this.functions[name] = step_ptr;
1450+ this.functions[finalize_name] = finalize_ptr;
1451+
1452+ // passing null to the sixth parameter defines this as an aggregate
1453+ // function
1454+ //
1455+ // > An aggregate SQL function requires an implementation of xStep and
1456+ // > xFinal and NULL pointer must be passed for xFunc.
1457+ // - http://www.sqlite.org/c3ref/create_function.html
1458+ this.handleError(sqlite3_create_function_v2(
1459+ this.db,
1460+ name,
1461+ step.length - 1,
1462+ SQLITE_UTF8,
1463+ 0,
1464+ 0,
1465+ step_ptr,
1466+ finalize_ptr,
1467+ 0
1468+ ));
1469+ return this;
1470+ };
1471+
13241472 // export Database to Module
13251473 Module.Database = Database;
13261474};
@@ -18723,6 +18871,8 @@ function asmFunc(env) {
1872318871 }
1872418872
1872518873 function $315($0_1, $1) {
18874+ $0_1 = $0_1 | 0;
18875+ $1 = $1 | 0;
1872618876 var $2_1 = 0;
1872718877 $2_1 = HEAP32[$0_1 + 8 >> 2];
1872818878 if (!(HEAPU8[$2_1 + 9 | 0] & 32)) {
@@ -18745,7 +18895,7 @@ function asmFunc(env) {
1874518895 $0_1 = 0
1874618896 }
1874718897 }
18748- return $0_1;
18898+ return $0_1 | 0 ;
1874918899 }
1875018900 return HEAP32[$2_1 + 16 >> 2];
1875118901 }
@@ -119866,6 +120016,7 @@ function asmFunc(env) {
119866120016 "sqlite3_result_null": $282,
119867120017 "sqlite3_result_text": $288,
119868120018 "sqlite3_sql": $300,
120019+ "sqlite3_aggregate_context": $315,
119869120020 "sqlite3_column_count": $320,
119870120021 "sqlite3_data_count": $321,
119871120022 "sqlite3_column_blob": $322,
@@ -124404,6 +124555,9 @@ var _sqlite3_result_text = Module["_sqlite3_result_text"] = createExportWrapper(
124404124555/** @type {function(...*):?} */
124405124556var _sqlite3_sql = Module["_sqlite3_sql"] = createExportWrapper("sqlite3_sql");
124406124557
124558+ /** @type {function(...*):?} */
124559+ var _sqlite3_aggregate_context = Module["_sqlite3_aggregate_context"] = createExportWrapper("sqlite3_aggregate_context");
124560+
124407124561/** @type {function(...*):?} */
124408124562var _sqlite3_column_count = Module["_sqlite3_column_count"] = createExportWrapper("sqlite3_column_count");
124409124563
0 commit comments