Skip to content

Commit 26402bf

Browse files
committed
Deploying to gh-pages from @ 5e5b063 🚀
1 parent fb3c81a commit 26402bf

24 files changed

Lines changed: 2428 additions & 909 deletions

‎.eslintrc.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = {
1616
ignorePatterns: [
1717
"/dist/",
1818
"/examples/",
19+
"/documentation/",
1920
"/node_modules/",
2021
"/out/",
2122
"/src/shell-post.js",

‎README.md‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,33 @@ db.create_function("add_js", add);
7575
// Run a query in which the function is used
7676
db.run("INSERT INTO hello VALUES (add_js(7, 3), add_js('Hello ', 'world'));"); // Inserts 10 and 'Hello world'
7777

78+
// You can create custom aggregation functions, by passing a name
79+
// and a set of functions to `db.create_aggregate`:
80+
//
81+
// - an `init` function. This function receives no argument and returns
82+
// the initial value for the state of the aggregate function.
83+
// - a `step` function. This function takes two arguments
84+
// - the current state of the aggregation
85+
// - a new value to aggregate to the state
86+
// It should return a new value for the state.
87+
// - a `finalize` function. This function receives a state object, and
88+
// returns the final value of the aggregate. It can be omitted, in which case
89+
// the final value of the state will be returned directly by the aggregate function.
90+
//
91+
// Here is an example aggregation function, `json_agg`, which will collect all
92+
// input values and return them as a JSON array:
93+
db.create_aggregate(
94+
"json_agg",
95+
{
96+
init: () => [],
97+
step: (state, val) => [...state, val],
98+
finalize: (state) => JSON.stringify(state),
99+
}
100+
);
101+
102+
db.exec("SELECT json_agg(column1) FROM (VALUES ('hello'), ('world'))");
103+
// -> The result of the query is the string '["hello","world"]'
104+
78105
// Export the database to an Uint8Array containing the SQLite database file
79106
const binaryArray = db.export();
80107
```

‎dist/sql-asm-debug.js‎

Lines changed: 216 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -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(...*):?} */
124405124556
var _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(...*):?} */
124408124562
var _sqlite3_column_count = Module["_sqlite3_column_count"] = createExportWrapper("sqlite3_column_count");
124409124563

0 commit comments

Comments
 (0)