diff --git a/docs/types/sqlite.d.ts b/docs/types/sqlite.d.ts index cd3630cf..059bed3f 100644 --- a/docs/types/sqlite.d.ts +++ b/docs/types/sqlite.d.ts @@ -49,6 +49,12 @@ declare module 'tjs:sqlite'{ readOnly: boolean; } + export interface ITransaction extends Function { + deferred: Function; + immediate: Function; + exclusive: Function; + } + export class Database { /** * Opens a SQLite database. @@ -73,6 +79,45 @@ declare module 'tjs:sqlite'{ */ prepare(sql: string): IStatement; + /** + * Wrap the given function so it runs in a [transaction](https://sqlite.org/lang_transaction.html). + * When the (returned) function is invoked, it will start a new transaction. When the function returns, + * the transaction will be committed. If an exception is thrown, the transaction will be rolled back. + * + * ```js + * const ins = db.prepare('INSERT INTO test (txt, int) VALUES(?, ?)'); + * const insMany = db.transaction(datas => { + * for (const data of datas) { + * ins.run(data); + * } + * }); + * + * insMany([ + * [ '1234', 1234 ], + * [ '4321', 4321 ], + * ]); + * ``` + * Transaction functions can be called from inside other transaction functions. When doing so, + * the inner transaction becomes a [savepoint](https://www.sqlite.org/lang_savepoint.html). If an error + * is thrown inside of a nested transaction function, the nested transaction function will roll back + * to the state just before the savepoint. If the error is not caught in the outer transaction function, + * this will cause the outer transaction function to roll back as well. + * + * Transactions also come with deferred, immediate, and exclusive versions: + * + * ```js + * insertMany(datas); // uses "BEGIN" + * insertMany.deferred(datas); // uses "BEGIN DEFERRED" + * insertMany.immediate(datas); // uses "BEGIN IMMEDIATE" + * insertMany.exclusive(datas); // uses "BEGIN EXCLUSIVE" + * ``` + * + * NOTE: This implementation was mostly taken from [better-sqlite3](https://github.com/WiseLibs/better-sqlite3/blob/6acc3fcebe469969aa29319714b187a53ada0934/docs/api.md#transactionfunction---function). + * + * @param fn - The function to be wrapped in a transaction. + */ + transaction(fn: Function): ITransaction; + /** * Closes the database. No further operations can be performed afterwards. */ diff --git a/src/_sqlite3.c b/src/_sqlite3.c index fad684dd..3f00d50b 100644 --- a/src/_sqlite3.c +++ b/src/_sqlite3.c @@ -229,6 +229,15 @@ static JSValue tjs_sqlite3_prepare(JSContext *ctx, JSValue this_val, int argc, J return obj; } +static JSValue tjs_sqlite3_in_transaction(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + TJSSqlite3Handle *h = tjs_sqlite3_get(ctx, argv[0]); + + if (!h) + return JS_EXCEPTION; + + return JS_NewBool(ctx, !sqlite3_get_autocommit(h->handle)); +} + static JSValue tjs_sqlite3_stmt_finalize(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { TJSSqlite3Stmt *h = tjs_sqlite3_stmt_get(ctx, argv[0]); @@ -512,6 +521,7 @@ static const JSCFunctionListEntry tjs_sqlite3_funcs[] = { TJS_CFUNC_DEF("close", 1, tjs_sqlite3_close), TJS_CFUNC_DEF("exec", 2, tjs_sqlite3_exec), TJS_CFUNC_DEF("prepare", 2, tjs_sqlite3_prepare), + TJS_CFUNC_DEF("in_transaction", 1, tjs_sqlite3_in_transaction), TJS_CFUNC_DEF("stmt_finalize", 1, tjs_sqlite3_stmt_finalize), TJS_CFUNC_DEF("stmt_expand", 1, tjs_sqlite3_stmt_expand), TJS_CFUNC_DEF("stmt_all", 2, tjs_sqlite3_stmt_all), diff --git a/src/bundles/c/stdlib/sqlite.c b/src/bundles/c/stdlib/sqlite.c index 9e391c7b..05a4c6fd 100644 --- a/src/bundles/c/stdlib/sqlite.c +++ b/src/bundles/c/stdlib/sqlite.c @@ -2,158 +2,348 @@ #include -const uint32_t tjs__sqlite_size = 1198; +const uint32_t tjs__sqlite_size = 2715; -const uint8_t tjs__sqlite[1198] = { - 0x0c, 0x20, 0x14, 0x74, 0x6a, 0x73, 0x3a, 0x73, +const uint8_t tjs__sqlite[2715] = { + 0x0c, 0x44, 0x14, 0x74, 0x6a, 0x73, 0x3a, 0x73, 0x71, 0x6c, 0x69, 0x74, 0x65, 0x10, 0x44, 0x61, - 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x02, 0x68, - 0x02, 0x65, 0x02, 0x6c, 0x02, 0x73, 0x02, 0x69, - 0x02, 0x6f, 0x02, 0x74, 0x02, 0x72, 0x02, 0x6e, - 0x10, 0x3a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x3a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x10, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, - 0x79, 0x24, 0x53, 0x51, 0x4c, 0x49, 0x54, 0x45, - 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x43, 0x52, - 0x45, 0x41, 0x54, 0x45, 0x28, 0x53, 0x51, 0x4c, - 0x49, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x45, 0x4e, - 0x5f, 0x52, 0x45, 0x41, 0x44, 0x4f, 0x4e, 0x4c, - 0x59, 0x2a, 0x53, 0x51, 0x4c, 0x49, 0x54, 0x45, - 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x52, 0x45, - 0x41, 0x44, 0x57, 0x52, 0x49, 0x54, 0x45, 0x08, - 0x6f, 0x70, 0x65, 0x6e, 0x0a, 0x63, 0x6c, 0x6f, - 0x73, 0x65, 0x14, 0x49, 0x6e, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x20, 0x44, 0x42, 0x0e, 0x70, 0x72, - 0x65, 0x70, 0x61, 0x72, 0x65, 0x1a, 0x73, 0x74, - 0x6d, 0x74, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, - 0x69, 0x7a, 0x65, 0x16, 0x73, 0x74, 0x6d, 0x74, - 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x10, - 0x73, 0x74, 0x6d, 0x74, 0x5f, 0x61, 0x6c, 0x6c, - 0x10, 0x73, 0x74, 0x6d, 0x74, 0x5f, 0x72, 0x75, - 0x6e, 0x22, 0x74, 0x6a, 0x73, 0x2e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x10, 0x5f, 0x73, 0x71, 0x6c, - 0x69, 0x74, 0x65, 0x33, 0x1c, 0x6b, 0x53, 0x71, - 0x6c, 0x69, 0x74, 0x65, 0x33, 0x48, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x18, 0x6b, 0x53, 0x71, 0x6c, - 0x69, 0x74, 0x65, 0x33, 0x53, 0x74, 0x6d, 0x74, - 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x06, 0x61, 0x6c, 0x6c, 0x06, 0x72, 0x75, - 0x6e, 0x0d, 0xb2, 0x03, 0x00, 0x01, 0x00, 0x03, - 0xb4, 0x03, 0x00, 0x00, 0x0c, 0x20, 0x02, 0x01, - 0x9e, 0x01, 0x00, 0x02, 0x00, 0x04, 0x06, 0x09, - 0x9f, 0x01, 0x02, 0xea, 0x01, 0x03, 0x00, 0x70, - 0xea, 0x01, 0x05, 0x00, 0x70, 0xb6, 0x03, 0x00, - 0x01, 0xb8, 0x03, 0x01, 0x01, 0xba, 0x03, 0x02, - 0x01, 0xbc, 0x03, 0x03, 0x01, 0xbe, 0x03, 0x04, - 0x01, 0xc0, 0x03, 0x05, 0x01, 0x0c, 0x40, 0x03, - 0x01, 0x00, 0x02, 0x04, 0x00, 0x06, 0x03, 0x00, - 0x9b, 0x01, 0x06, 0xc2, 0x03, 0x00, 0x01, 0x00, - 0xc4, 0x03, 0x00, 0x01, 0x00, 0xc2, 0x03, 0x01, - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x20, 0xc4, 0x03, - 0x01, 0x01, 0x20, 0xc6, 0x03, 0x02, 0x00, 0x20, - 0x10, 0x00, 0x01, 0x00, 0xea, 0x01, 0x00, 0x0d, - 0xb8, 0x03, 0x01, 0x00, 0xba, 0x03, 0x02, 0x00, - 0x08, 0xcb, 0x2b, 0x65, 0x00, 0x00, 0x11, 0xe9, - 0x06, 0xc7, 0x1b, 0x24, 0x00, 0x00, 0x0e, 0x61, - 0x01, 0x00, 0x61, 0x00, 0x00, 0xd0, 0x11, 0xf1, - 0xe9, 0x08, 0x0e, 0x04, 0xe4, 0x00, 0x00, 0x00, - 0xd8, 0xc8, 0xd1, 0x11, 0xf1, 0xe9, 0x12, 0x0e, - 0x0b, 0xb3, 0x96, 0x4c, 0xe5, 0x00, 0x00, 0x00, - 0xb4, 0x96, 0x4c, 0xe6, 0x00, 0x00, 0x00, 0xd9, - 0xc9, 0x61, 0x02, 0x00, 0xb3, 0xca, 0xd1, 0x41, - 0xe5, 0x00, 0x00, 0x00, 0x11, 0xe9, 0x10, 0x0e, - 0x62, 0x02, 0x00, 0xdd, 0x41, 0xe7, 0x00, 0x00, - 0x00, 0xa4, 0x11, 0x63, 0x02, 0x00, 0x0e, 0xd1, - 0x41, 0xe6, 0x00, 0x00, 0x00, 0xe9, 0x11, 0x62, - 0x02, 0x00, 0xdd, 0x41, 0xe8, 0x00, 0x00, 0x00, - 0xa4, 0x11, 0x63, 0x02, 0x00, 0xeb, 0x0f, 0x62, - 0x02, 0x00, 0xdd, 0x41, 0xe9, 0x00, 0x00, 0x00, - 0xa4, 0x11, 0x63, 0x02, 0x00, 0x0e, 0xc7, 0xde, - 0x1b, 0x11, 0xb0, 0xea, 0x04, 0x1b, 0x71, 0x1b, - 0x1b, 0xdd, 0x42, 0xea, 0x00, 0x00, 0x00, 0xd0, - 0x62, 0x02, 0x00, 0x24, 0x02, 0x00, 0x1b, 0x71, - 0x1b, 0x49, 0x29, 0x0c, 0x42, 0x03, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x04, 0x02, 0x00, 0x16, 0x01, - 0x10, 0x00, 0x01, 0x00, 0xba, 0x03, 0x02, 0x00, - 0xb8, 0x03, 0x01, 0x00, 0x08, 0xc8, 0xc4, 0xdc, - 0x47, 0x11, 0xe9, 0x0e, 0x0e, 0xdd, 0x42, 0xeb, - 0x00, 0x00, 0x00, 0xc4, 0xdc, 0x47, 0x24, 0x01, + 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x02, 0x54, + 0x02, 0x6e, 0x02, 0x73, 0x02, 0x68, 0x02, 0x6d, + 0x02, 0x49, 0x02, 0x75, 0x02, 0x6f, 0x02, 0x45, + 0x02, 0x65, 0x02, 0x72, 0x02, 0x6c, 0x10, 0x3a, + 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x3a, 0x0c, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x10, 0x72, + 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x24, + 0x53, 0x51, 0x4c, 0x49, 0x54, 0x45, 0x5f, 0x4f, + 0x50, 0x45, 0x4e, 0x5f, 0x43, 0x52, 0x45, 0x41, + 0x54, 0x45, 0x28, 0x53, 0x51, 0x4c, 0x49, 0x54, + 0x45, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x52, + 0x45, 0x41, 0x44, 0x4f, 0x4e, 0x4c, 0x59, 0x2a, + 0x53, 0x51, 0x4c, 0x49, 0x54, 0x45, 0x5f, 0x4f, + 0x50, 0x45, 0x4e, 0x5f, 0x52, 0x45, 0x41, 0x44, + 0x57, 0x52, 0x49, 0x54, 0x45, 0x08, 0x6f, 0x70, + 0x65, 0x6e, 0x0a, 0x63, 0x6c, 0x6f, 0x73, 0x65, + 0x14, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x20, 0x44, 0x42, 0x0e, 0x70, 0x72, 0x65, 0x70, + 0x61, 0x72, 0x65, 0x1c, 0x69, 0x6e, 0x5f, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x02, 0x69, 0x50, 0x45, 0x78, 0x70, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x20, 0x61, 0x72, 0x67, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, + 0x62, 0x65, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x64, 0x65, + 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x12, 0x69, + 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, + 0x12, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, + 0x76, 0x65, 0x02, 0x74, 0x0c, 0x43, 0x4f, 0x4d, + 0x4d, 0x49, 0x54, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x10, 0x52, 0x4f, 0x4c, 0x4c, 0x42, + 0x41, 0x43, 0x4b, 0x10, 0x72, 0x6f, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x26, 0x53, 0x41, 0x56, + 0x45, 0x50, 0x4f, 0x49, 0x4e, 0x54, 0x20, 0x60, + 0x09, 0x5f, 0x62, 0x73, 0x33, 0x2e, 0x09, 0x60, + 0x12, 0x73, 0x61, 0x76, 0x65, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x22, 0x52, 0x45, 0x4c, 0x45, 0x41, + 0x53, 0x45, 0x20, 0x60, 0x09, 0x5f, 0x62, 0x73, + 0x33, 0x2e, 0x09, 0x60, 0x0e, 0x72, 0x65, 0x6c, + 0x65, 0x61, 0x73, 0x65, 0x2a, 0x52, 0x4f, 0x4c, + 0x4c, 0x42, 0x41, 0x43, 0x4b, 0x20, 0x54, 0x4f, + 0x20, 0x60, 0x09, 0x5f, 0x62, 0x73, 0x33, 0x2e, + 0x09, 0x60, 0x14, 0x72, 0x6f, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x54, 0x6f, 0x0c, 0x61, 0x73, + 0x73, 0x69, 0x67, 0x6e, 0x0a, 0x42, 0x45, 0x47, + 0x49, 0x4e, 0x0a, 0x62, 0x65, 0x67, 0x69, 0x6e, + 0x1c, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x44, + 0x45, 0x46, 0x45, 0x52, 0x52, 0x45, 0x44, 0x1e, + 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x49, 0x4d, + 0x4d, 0x45, 0x44, 0x49, 0x41, 0x54, 0x45, 0x1e, + 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x45, 0x58, + 0x43, 0x4c, 0x55, 0x53, 0x49, 0x56, 0x45, 0x02, + 0x64, 0x02, 0x4f, 0x02, 0x76, 0x02, 0x70, 0x02, + 0x61, 0x02, 0x63, 0x02, 0x66, 0x1a, 0x69, 0x6e, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x06, 0x72, 0x75, 0x6e, 0x08, + 0x63, 0x61, 0x6c, 0x6c, 0x1a, 0x73, 0x74, 0x6d, + 0x74, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x16, 0x73, 0x74, 0x6d, 0x74, 0x5f, + 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x10, 0x73, + 0x74, 0x6d, 0x74, 0x5f, 0x61, 0x6c, 0x6c, 0x10, + 0x73, 0x74, 0x6d, 0x74, 0x5f, 0x72, 0x75, 0x6e, + 0x22, 0x74, 0x6a, 0x73, 0x2e, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x10, 0x5f, 0x73, 0x71, 0x6c, 0x69, + 0x74, 0x65, 0x33, 0x1c, 0x6b, 0x53, 0x71, 0x6c, + 0x69, 0x74, 0x65, 0x33, 0x48, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x6b, + 0x53, 0x71, 0x6c, 0x69, 0x74, 0x65, 0x33, 0x53, + 0x74, 0x6d, 0x74, 0x10, 0x66, 0x69, 0x6e, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x06, 0x61, 0x6c, 0x6c, + 0x0d, 0xb2, 0x03, 0x00, 0x01, 0x00, 0x04, 0xb4, + 0x03, 0x00, 0x00, 0x0c, 0x20, 0x02, 0x01, 0x9e, + 0x01, 0x00, 0x02, 0x00, 0x04, 0x09, 0x0d, 0xc5, + 0x01, 0x02, 0xea, 0x01, 0x03, 0x00, 0x70, 0xea, + 0x01, 0x05, 0x00, 0x70, 0xb6, 0x03, 0x00, 0x01, + 0xb8, 0x03, 0x01, 0x01, 0xba, 0x03, 0x02, 0x01, + 0xbc, 0x03, 0x03, 0x01, 0xbe, 0x03, 0x04, 0x01, + 0xc0, 0x03, 0x05, 0x01, 0xc2, 0x03, 0x06, 0x01, + 0xc4, 0x03, 0x07, 0x01, 0xc6, 0x03, 0x08, 0x01, + 0x0c, 0x40, 0x03, 0x01, 0x00, 0x02, 0x04, 0x00, + 0x06, 0x03, 0x00, 0x9b, 0x01, 0x06, 0xc8, 0x03, + 0x00, 0x01, 0x00, 0xca, 0x03, 0x00, 0x01, 0x00, + 0xc8, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x20, 0xca, 0x03, 0x01, 0x01, 0x20, 0xcc, 0x03, + 0x02, 0x00, 0x20, 0x10, 0x00, 0x01, 0x00, 0xea, + 0x01, 0x00, 0x0d, 0xb8, 0x03, 0x01, 0x00, 0xba, + 0x03, 0x02, 0x00, 0x08, 0xcb, 0x2b, 0x65, 0x00, + 0x00, 0x11, 0xe9, 0x06, 0xc7, 0x1b, 0x24, 0x00, + 0x00, 0x0e, 0x61, 0x01, 0x00, 0x61, 0x00, 0x00, + 0xd0, 0x11, 0xf1, 0xe9, 0x08, 0x0e, 0x04, 0xe7, + 0x00, 0x00, 0x00, 0xd8, 0xc8, 0xd1, 0x11, 0xf1, + 0xe9, 0x12, 0x0e, 0x0b, 0xb3, 0x96, 0x4c, 0xe8, + 0x00, 0x00, 0x00, 0xb4, 0x96, 0x4c, 0xe9, 0x00, + 0x00, 0x00, 0xd9, 0xc9, 0x61, 0x02, 0x00, 0xb3, + 0xca, 0xd1, 0x41, 0xe8, 0x00, 0x00, 0x00, 0x11, + 0xe9, 0x10, 0x0e, 0x62, 0x02, 0x00, 0xdd, 0x41, + 0xea, 0x00, 0x00, 0x00, 0xa4, 0x11, 0x63, 0x02, + 0x00, 0x0e, 0xd1, 0x41, 0xe9, 0x00, 0x00, 0x00, + 0xe9, 0x11, 0x62, 0x02, 0x00, 0xdd, 0x41, 0xeb, + 0x00, 0x00, 0x00, 0xa4, 0x11, 0x63, 0x02, 0x00, + 0xeb, 0x0f, 0x62, 0x02, 0x00, 0xdd, 0x41, 0xec, + 0x00, 0x00, 0x00, 0xa4, 0x11, 0x63, 0x02, 0x00, + 0x0e, 0xc7, 0xde, 0x1b, 0x11, 0xb0, 0xea, 0x04, + 0x1b, 0x71, 0x1b, 0x1b, 0xdd, 0x42, 0xed, 0x00, + 0x00, 0x00, 0xd0, 0x62, 0x02, 0x00, 0x24, 0x02, + 0x00, 0x1b, 0x71, 0x1b, 0x49, 0x29, 0x0c, 0x42, + 0x03, 0x01, 0x00, 0x00, 0x01, 0x00, 0x04, 0x02, + 0x00, 0x28, 0x01, 0x10, 0x00, 0x01, 0x00, 0xba, + 0x03, 0x02, 0x00, 0xb8, 0x03, 0x01, 0x00, 0x08, + 0xc8, 0xc4, 0xdc, 0x47, 0x11, 0xe9, 0x20, 0x0e, + 0xdd, 0x42, 0xee, 0x00, 0x00, 0x00, 0xc4, 0xdc, + 0x47, 0x24, 0x01, 0x00, 0x0e, 0xc4, 0xdc, 0x1b, + 0x11, 0xb0, 0xea, 0x04, 0x1b, 0x71, 0x1b, 0x1b, + 0x07, 0x1b, 0x71, 0x1b, 0x16, 0x49, 0x29, 0x0c, + 0x42, 0x03, 0x01, 0x00, 0x01, 0x01, 0x01, 0x04, + 0x02, 0x00, 0x25, 0x02, 0xc8, 0x03, 0x00, 0x01, + 0x00, 0x10, 0x00, 0x01, 0x00, 0xba, 0x03, 0x02, + 0x00, 0xb8, 0x03, 0x01, 0x00, 0x08, 0xc8, 0xc4, + 0xdc, 0x47, 0x96, 0xe9, 0x10, 0x38, 0x92, 0x00, + 0x00, 0x00, 0x11, 0x04, 0xef, 0x00, 0x00, 0x00, + 0x21, 0x01, 0x00, 0x2f, 0xdd, 0x42, 0x85, 0x00, + 0x00, 0x00, 0xc4, 0xdc, 0x47, 0xd0, 0x24, 0x02, 0x00, 0x29, 0x0c, 0x42, 0x03, 0x01, 0x00, 0x01, - 0x01, 0x01, 0x04, 0x02, 0x00, 0x25, 0x02, 0xc2, + 0x01, 0x01, 0x06, 0x03, 0x00, 0x2a, 0x02, 0xc8, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0x01, 0x00, - 0xba, 0x03, 0x02, 0x00, 0xb8, 0x03, 0x01, 0x00, - 0x08, 0xc8, 0xc4, 0xdc, 0x47, 0x96, 0xe9, 0x10, - 0x38, 0x92, 0x00, 0x00, 0x00, 0x11, 0x04, 0xec, - 0x00, 0x00, 0x00, 0x21, 0x01, 0x00, 0x2f, 0xdd, - 0x42, 0x85, 0x00, 0x00, 0x00, 0xc4, 0xdc, 0x47, - 0xd0, 0x24, 0x02, 0x00, 0x29, 0x0c, 0x42, 0x03, - 0x01, 0x00, 0x01, 0x01, 0x01, 0x06, 0x03, 0x00, - 0x2a, 0x02, 0xc2, 0x03, 0x00, 0x01, 0x00, 0x10, - 0x00, 0x01, 0x00, 0xba, 0x03, 0x02, 0x00, 0xc0, - 0x03, 0x05, 0x00, 0xb8, 0x03, 0x01, 0x00, 0x08, - 0xc8, 0xc4, 0xdc, 0x47, 0x96, 0xe9, 0x10, 0x38, - 0x92, 0x00, 0x00, 0x00, 0x11, 0x04, 0xec, 0x00, - 0x00, 0x00, 0x21, 0x01, 0x00, 0x2f, 0xdd, 0x11, - 0xde, 0x42, 0xed, 0x00, 0x00, 0x00, 0xc4, 0xdc, - 0x47, 0xd0, 0x24, 0x02, 0x00, 0x21, 0x01, 0x00, - 0x28, 0x0c, 0x42, 0x03, 0x01, 0x00, 0x01, 0x01, - 0x01, 0x03, 0x02, 0x00, 0x20, 0x02, 0xc2, 0x03, - 0x00, 0x01, 0x00, 0x10, 0x00, 0x01, 0x00, 0xea, - 0x01, 0x01, 0x0d, 0xbe, 0x03, 0x04, 0x00, 0x08, - 0xc8, 0x2b, 0x65, 0x00, 0x00, 0x11, 0xe9, 0x06, - 0xc4, 0x1b, 0x24, 0x00, 0x00, 0x0e, 0xc4, 0xdd, - 0x1b, 0x11, 0xb0, 0xea, 0x04, 0x1b, 0x71, 0x1b, - 0x1b, 0xd0, 0x1b, 0x71, 0x1b, 0x49, 0x29, 0x0c, - 0x42, 0x03, 0x01, 0x00, 0x00, 0x01, 0x00, 0x04, - 0x02, 0x00, 0x0f, 0x01, 0x10, 0x00, 0x01, 0x00, - 0xb8, 0x03, 0x01, 0x00, 0xbe, 0x03, 0x04, 0x00, - 0x08, 0xc8, 0xdc, 0x42, 0xee, 0x00, 0x00, 0x00, - 0xc4, 0xdd, 0x47, 0x24, 0x01, 0x00, 0x29, 0x0c, - 0x42, 0x03, 0x01, 0x00, 0x00, 0x01, 0x00, 0x04, - 0x02, 0x00, 0x0e, 0x01, 0x10, 0x00, 0x01, 0x00, - 0xb8, 0x03, 0x01, 0x00, 0xbe, 0x03, 0x04, 0x00, - 0x08, 0xc8, 0xdc, 0x42, 0xef, 0x00, 0x00, 0x00, - 0xc4, 0xdd, 0x47, 0x25, 0x01, 0x00, 0x0c, 0x40, - 0x03, 0x01, 0x00, 0x01, 0x01, 0x00, 0x04, 0x02, - 0x00, 0x32, 0x02, 0xc2, 0x03, 0x00, 0x01, 0x00, - 0x10, 0x00, 0x01, 0x00, 0xb8, 0x03, 0x01, 0x00, - 0xbe, 0x03, 0x04, 0x00, 0x08, 0xc8, 0x0d, 0x00, - 0x00, 0xd8, 0x11, 0xe9, 0x1c, 0x0e, 0xd0, 0xe8, - 0xb4, 0xae, 0x11, 0xe9, 0x14, 0x0e, 0xd0, 0xb3, - 0x47, 0x97, 0x04, 0x48, 0x00, 0x00, 0x00, 0xac, - 0x11, 0xe9, 0x06, 0x0e, 0xd0, 0xb3, 0x47, 0xd8, - 0x0e, 0xdc, 0x42, 0xf0, 0x00, 0x00, 0x00, 0xc4, - 0xdd, 0x47, 0xd0, 0x25, 0x02, 0x00, 0x0c, 0x40, - 0x03, 0x01, 0x00, 0x01, 0x01, 0x00, 0x04, 0x02, - 0x00, 0x33, 0x02, 0xc2, 0x03, 0x00, 0x01, 0x00, - 0x10, 0x00, 0x01, 0x00, 0xb8, 0x03, 0x01, 0x00, - 0xbe, 0x03, 0x04, 0x00, 0x08, 0xc8, 0x0d, 0x00, - 0x00, 0xd8, 0x11, 0xe9, 0x1c, 0x0e, 0xd0, 0xe8, - 0xb4, 0xae, 0x11, 0xe9, 0x14, 0x0e, 0xd0, 0xb3, - 0x47, 0x97, 0x04, 0x48, 0x00, 0x00, 0x00, 0xac, - 0x11, 0xe9, 0x06, 0x0e, 0xd0, 0xb3, 0x47, 0xd8, - 0x0e, 0xdc, 0x42, 0xf1, 0x00, 0x00, 0x00, 0xc4, - 0xdd, 0x47, 0xd0, 0x24, 0x02, 0x00, 0x29, 0x08, - 0xe9, 0x02, 0x29, 0x38, 0x8a, 0x00, 0x00, 0x00, - 0x38, 0x96, 0x00, 0x00, 0x00, 0x42, 0x11, 0x00, - 0x00, 0x00, 0x04, 0xf2, 0x00, 0x00, 0x00, 0x24, - 0x01, 0x00, 0x47, 0xe4, 0x41, 0xf3, 0x00, 0x00, - 0x00, 0xe1, 0x38, 0x96, 0x00, 0x00, 0x00, 0x04, - 0xf4, 0x00, 0x00, 0x00, 0xee, 0xe2, 0x06, 0x61, - 0x00, 0x00, 0xbd, 0x00, 0x56, 0xde, 0x00, 0x00, - 0x00, 0x00, 0xbe, 0x01, 0x54, 0xeb, 0x00, 0x00, - 0x00, 0x00, 0xbe, 0x02, 0x54, 0x85, 0x00, 0x00, - 0x00, 0x00, 0xbe, 0x03, 0x54, 0xed, 0x00, 0x00, - 0x00, 0x00, 0x06, 0xc8, 0x0e, 0x68, 0x00, 0x00, - 0xe3, 0x38, 0x96, 0x00, 0x00, 0x00, 0x04, 0xf5, - 0x00, 0x00, 0x00, 0xee, 0x5f, 0x04, 0x00, 0x06, - 0x61, 0x01, 0x00, 0xbd, 0x04, 0x56, 0xe0, 0x00, - 0x00, 0x00, 0x00, 0xbe, 0x05, 0x54, 0xf6, 0x00, - 0x00, 0x00, 0x00, 0xbe, 0x06, 0x54, 0x36, 0x00, - 0x00, 0x00, 0x00, 0xbe, 0x07, 0x54, 0xf7, 0x00, - 0x00, 0x00, 0x00, 0xbe, 0x08, 0x54, 0xf8, 0x00, - 0x00, 0x00, 0x00, 0x06, 0xc9, 0x0e, 0x68, 0x01, - 0x00, 0x5f, 0x05, 0x00, 0x06, 0x2e, + 0xba, 0x03, 0x02, 0x00, 0xc6, 0x03, 0x08, 0x00, + 0xb8, 0x03, 0x01, 0x00, 0x08, 0xc8, 0xc4, 0xdc, + 0x47, 0x96, 0xe9, 0x10, 0x38, 0x92, 0x00, 0x00, + 0x00, 0x11, 0x04, 0xef, 0x00, 0x00, 0x00, 0x21, + 0x01, 0x00, 0x2f, 0xdd, 0x11, 0xde, 0x42, 0xf0, + 0x00, 0x00, 0x00, 0xc4, 0xdc, 0x47, 0xd0, 0x24, + 0x02, 0x00, 0x21, 0x01, 0x00, 0x28, 0x0c, 0x42, + 0x03, 0x01, 0x00, 0x00, 0x01, 0x00, 0x04, 0x02, + 0x00, 0x17, 0x01, 0x10, 0x00, 0x01, 0x00, 0xba, + 0x03, 0x02, 0x00, 0xb8, 0x03, 0x01, 0x00, 0x08, + 0xc8, 0xc4, 0xdc, 0x47, 0xe9, 0x0e, 0xdd, 0x42, + 0xf1, 0x00, 0x00, 0x00, 0xc4, 0xdc, 0x47, 0x24, + 0x01, 0x00, 0x28, 0xb4, 0x96, 0x28, 0x0c, 0x42, + 0x03, 0x01, 0x00, 0x01, 0x04, 0x01, 0x06, 0x02, + 0x00, 0x92, 0x02, 0x05, 0xc8, 0x03, 0x00, 0x01, + 0x00, 0xca, 0x03, 0x01, 0x00, 0x20, 0xcc, 0x03, + 0x01, 0x01, 0x20, 0xe4, 0x03, 0x01, 0x02, 0x20, + 0x10, 0x00, 0x01, 0x00, 0xc0, 0x03, 0x05, 0x00, + 0xc2, 0x03, 0x06, 0x00, 0x08, 0xcb, 0x61, 0x02, + 0x00, 0x61, 0x01, 0x00, 0x61, 0x00, 0x00, 0xd0, + 0xf4, 0xea, 0x10, 0x38, 0xc7, 0x00, 0x00, 0x00, + 0x11, 0x04, 0xf3, 0x00, 0x00, 0x00, 0x21, 0x01, + 0x00, 0x2f, 0xc7, 0xc8, 0xdc, 0x62, 0x00, 0x00, + 0xee, 0xc9, 0x0b, 0x0b, 0xdd, 0xd0, 0x62, 0x00, + 0x00, 0x62, 0x01, 0x00, 0x41, 0x16, 0x00, 0x00, + 0x00, 0xf0, 0x4c, 0x3f, 0x00, 0x00, 0x00, 0x4c, + 0x16, 0x00, 0x00, 0x00, 0x0b, 0xdd, 0xd0, 0x62, + 0x00, 0x00, 0x62, 0x01, 0x00, 0x41, 0xf4, 0x00, + 0x00, 0x00, 0xf0, 0x4c, 0x3f, 0x00, 0x00, 0x00, + 0x4c, 0xf4, 0x00, 0x00, 0x00, 0x0b, 0xdd, 0xd0, + 0x62, 0x00, 0x00, 0x62, 0x01, 0x00, 0x41, 0xf5, + 0x00, 0x00, 0x00, 0xf0, 0x4c, 0x3f, 0x00, 0x00, + 0x00, 0x4c, 0xf5, 0x00, 0x00, 0x00, 0x0b, 0xdd, + 0xd0, 0x62, 0x00, 0x00, 0x62, 0x01, 0x00, 0x41, + 0xf6, 0x00, 0x00, 0x00, 0xf0, 0x4c, 0x3f, 0x00, + 0x00, 0x00, 0x4c, 0xf6, 0x00, 0x00, 0x00, 0xca, + 0x38, 0x90, 0x00, 0x00, 0x00, 0x42, 0x58, 0x00, + 0x00, 0x00, 0x62, 0x02, 0x00, 0x41, 0x16, 0x00, + 0x00, 0x00, 0x41, 0x3f, 0x00, 0x00, 0x00, 0x62, + 0x02, 0x00, 0x24, 0x02, 0x00, 0x0e, 0x38, 0x90, + 0x00, 0x00, 0x00, 0x42, 0x58, 0x00, 0x00, 0x00, + 0x62, 0x02, 0x00, 0x41, 0xf4, 0x00, 0x00, 0x00, + 0x41, 0x3f, 0x00, 0x00, 0x00, 0x62, 0x02, 0x00, + 0x24, 0x02, 0x00, 0x0e, 0x38, 0x90, 0x00, 0x00, + 0x00, 0x42, 0x58, 0x00, 0x00, 0x00, 0x62, 0x02, + 0x00, 0x41, 0xf5, 0x00, 0x00, 0x00, 0x41, 0x3f, + 0x00, 0x00, 0x00, 0x62, 0x02, 0x00, 0x24, 0x02, + 0x00, 0x0e, 0x38, 0x90, 0x00, 0x00, 0x00, 0x42, + 0x58, 0x00, 0x00, 0x00, 0x62, 0x02, 0x00, 0x41, + 0xf6, 0x00, 0x00, 0x00, 0x41, 0x3f, 0x00, 0x00, + 0x00, 0x62, 0x02, 0x00, 0x24, 0x02, 0x00, 0x0e, + 0x62, 0x02, 0x00, 0x41, 0x16, 0x00, 0x00, 0x00, + 0x41, 0x3f, 0x00, 0x00, 0x00, 0x28, 0x0c, 0x02, + 0x02, 0x01, 0x00, 0x01, 0x02, 0x01, 0x07, 0x01, + 0x00, 0xd1, 0x02, 0x03, 0xee, 0x03, 0x00, 0x01, + 0x00, 0xc8, 0x03, 0x01, 0x00, 0x20, 0xca, 0x03, + 0x03, 0x01, 0x20, 0xbc, 0x03, 0x03, 0x00, 0x61, + 0x00, 0x00, 0x7b, 0xde, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x11, 0xea, 0x0f, 0x0e, 0x38, 0xb2, + 0x00, 0x00, 0x00, 0x11, 0x21, 0x00, 0x00, 0x16, + 0x3d, 0xeb, 0x03, 0x0f, 0x0f, 0x42, 0x40, 0x00, + 0x00, 0x00, 0xd0, 0x24, 0x01, 0x00, 0xc8, 0x62, + 0x00, 0x00, 0x96, 0x69, 0x20, 0x01, 0x00, 0x00, + 0x61, 0x01, 0x00, 0x0b, 0xd0, 0x42, 0xf0, 0x00, + 0x00, 0x00, 0x04, 0xf8, 0x00, 0x00, 0x00, 0x24, + 0x01, 0x00, 0x4c, 0xf9, 0x00, 0x00, 0x00, 0xd0, + 0x42, 0xf0, 0x00, 0x00, 0x00, 0x04, 0xfa, 0x00, + 0x00, 0x00, 0x24, 0x01, 0x00, 0x4c, 0xfb, 0x00, + 0x00, 0x00, 0xd0, 0x42, 0xf0, 0x00, 0x00, 0x00, + 0x04, 0xfc, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, + 0x4c, 0xfd, 0x00, 0x00, 0x00, 0xd0, 0x42, 0xf0, + 0x00, 0x00, 0x00, 0x04, 0xfe, 0x00, 0x00, 0x00, + 0x24, 0x01, 0x00, 0x4c, 0xff, 0x00, 0x00, 0x00, + 0xd0, 0x42, 0xf0, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x4c, 0x01, + 0x01, 0x00, 0x00, 0xc9, 0x0b, 0x38, 0x90, 0x00, + 0x00, 0x00, 0x42, 0x02, 0x01, 0x00, 0x00, 0x0b, + 0xd0, 0x42, 0xf0, 0x00, 0x00, 0x00, 0x04, 0x03, + 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x4c, 0x04, + 0x01, 0x00, 0x00, 0x62, 0x01, 0x00, 0x24, 0x02, + 0x00, 0x4c, 0x16, 0x00, 0x00, 0x00, 0x38, 0x90, + 0x00, 0x00, 0x00, 0x42, 0x02, 0x01, 0x00, 0x00, + 0x0b, 0xd0, 0x42, 0xf0, 0x00, 0x00, 0x00, 0x04, + 0x05, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x4c, + 0x04, 0x01, 0x00, 0x00, 0x62, 0x01, 0x00, 0x24, + 0x02, 0x00, 0x4c, 0xf4, 0x00, 0x00, 0x00, 0x38, + 0x90, 0x00, 0x00, 0x00, 0x42, 0x02, 0x01, 0x00, + 0x00, 0x0b, 0xd0, 0x42, 0xf0, 0x00, 0x00, 0x00, + 0x04, 0x06, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, + 0x4c, 0x04, 0x01, 0x00, 0x00, 0x62, 0x01, 0x00, + 0x24, 0x02, 0x00, 0x4c, 0xf5, 0x00, 0x00, 0x00, + 0x38, 0x90, 0x00, 0x00, 0x00, 0x42, 0x02, 0x01, + 0x00, 0x00, 0x0b, 0xd0, 0x42, 0xf0, 0x00, 0x00, + 0x00, 0x04, 0x07, 0x01, 0x00, 0x00, 0x24, 0x01, + 0x00, 0x4c, 0x04, 0x01, 0x00, 0x00, 0x62, 0x01, + 0x00, 0x24, 0x02, 0x00, 0x4c, 0xf6, 0x00, 0x00, + 0x00, 0x11, 0x63, 0x00, 0x00, 0x0e, 0xdc, 0x42, + 0x41, 0x00, 0x00, 0x00, 0xd0, 0x62, 0x00, 0x00, + 0x24, 0x02, 0x00, 0x0e, 0x62, 0x00, 0x00, 0x28, + 0x0c, 0x00, 0x02, 0x01, 0x00, 0x03, 0x06, 0x03, + 0x02, 0x00, 0x01, 0x32, 0x09, 0xee, 0x03, 0x00, + 0x01, 0x40, 0xc8, 0x03, 0x00, 0x01, 0x40, 0x00, + 0x00, 0x01, 0x00, 0xca, 0x03, 0x00, 0x00, 0x40, + 0xcc, 0x03, 0x00, 0x01, 0x40, 0xe4, 0x03, 0x00, + 0x02, 0x40, 0x90, 0x04, 0x00, 0x03, 0x40, 0x92, + 0x04, 0x00, 0x04, 0x40, 0x94, 0x04, 0x00, 0x05, + 0x40, 0x0c, 0x43, 0x02, 0x01, 0x00, 0x00, 0x07, + 0x00, 0x06, 0x08, 0x00, 0xc5, 0x01, 0x07, 0x96, + 0x04, 0x01, 0x00, 0x20, 0x98, 0x04, 0x01, 0x01, + 0x20, 0x9a, 0x04, 0x01, 0x02, 0x20, 0x9c, 0x04, + 0x02, 0x03, 0x20, 0x9c, 0x04, 0x03, 0x03, 0x03, + 0x10, 0x00, 0x01, 0x00, 0x98, 0x01, 0x00, 0x01, + 0x00, 0xc8, 0x03, 0x01, 0x03, 0x90, 0x04, 0x03, + 0x01, 0x92, 0x04, 0x04, 0x01, 0x94, 0x04, 0x05, + 0x01, 0xca, 0x03, 0x00, 0x01, 0xcc, 0x03, 0x01, + 0x01, 0xe4, 0x03, 0x02, 0x01, 0xee, 0x03, 0x00, + 0x03, 0x08, 0xc1, 0x05, 0x0c, 0x00, 0xc1, 0x06, + 0x61, 0x02, 0x00, 0x61, 0x01, 0x00, 0x61, 0x00, + 0x00, 0x06, 0xc8, 0x06, 0xc9, 0x06, 0xca, 0xdc, + 0x41, 0x0f, 0x01, 0x00, 0x00, 0xe9, 0x14, 0xdd, + 0x11, 0x63, 0x00, 0x00, 0x0e, 0xde, 0x11, 0x63, + 0x01, 0x00, 0x0e, 0xdf, 0x11, 0x63, 0x02, 0x00, + 0xeb, 0x18, 0x5e, 0x04, 0x00, 0x11, 0x63, 0x00, + 0x00, 0x0e, 0x5e, 0x05, 0x00, 0x11, 0x63, 0x01, + 0x00, 0x0e, 0x5e, 0x06, 0x00, 0x11, 0x63, 0x02, + 0x00, 0x0e, 0x6c, 0x43, 0x00, 0x00, 0x00, 0x61, + 0x03, 0x00, 0x62, 0x00, 0x00, 0x42, 0x10, 0x01, + 0x00, 0x00, 0x24, 0x00, 0x00, 0x0e, 0x38, 0x9b, + 0x00, 0x00, 0x00, 0x41, 0x3a, 0x00, 0x00, 0x00, + 0x41, 0x59, 0x00, 0x00, 0x00, 0x42, 0x11, 0x01, + 0x00, 0x00, 0x5e, 0x07, 0x00, 0xc0, 0x05, 0xc0, + 0x06, 0x24, 0x03, 0x00, 0xcb, 0x62, 0x01, 0x00, + 0x42, 0x10, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, + 0x0e, 0x62, 0x03, 0x00, 0x6f, 0x28, 0xc1, 0x04, + 0x6c, 0x34, 0x00, 0x00, 0x00, 0xdc, 0x41, 0x0f, + 0x01, 0x00, 0x00, 0x11, 0xe9, 0x24, 0x0e, 0x62, + 0x02, 0x00, 0x42, 0x10, 0x01, 0x00, 0x00, 0x24, + 0x00, 0x00, 0x0e, 0x62, 0x02, 0x00, 0x5e, 0x06, + 0x00, 0xaf, 0x11, 0xe9, 0x0d, 0x0e, 0x62, 0x01, + 0x00, 0x42, 0x10, 0x01, 0x00, 0x00, 0x24, 0x00, + 0x00, 0x0e, 0xc0, 0x04, 0x2f, 0x2f, 0xd2, 0x70, + 0x11, 0x41, 0x04, 0x01, 0x00, 0x00, 0xc8, 0x11, + 0x41, 0xf9, 0x00, 0x00, 0x00, 0xc9, 0x11, 0x41, + 0xfb, 0x00, 0x00, 0x00, 0xca, 0x11, 0x41, 0xfd, + 0x00, 0x00, 0x00, 0xcb, 0x11, 0x41, 0xff, 0x00, + 0x00, 0x00, 0xc1, 0x04, 0x11, 0x41, 0x01, 0x01, + 0x00, 0x00, 0xc1, 0x05, 0x0e, 0xbe, 0x00, 0x28, + 0x0c, 0x42, 0x03, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x03, 0x02, 0x00, 0x20, 0x02, 0xc8, 0x03, 0x00, + 0x01, 0x00, 0x10, 0x00, 0x01, 0x00, 0xea, 0x01, + 0x01, 0x0d, 0xc4, 0x03, 0x07, 0x00, 0x08, 0xc8, + 0x2b, 0x65, 0x00, 0x00, 0x11, 0xe9, 0x06, 0xc4, + 0x1b, 0x24, 0x00, 0x00, 0x0e, 0xc4, 0xdd, 0x1b, + 0x11, 0xb0, 0xea, 0x04, 0x1b, 0x71, 0x1b, 0x1b, + 0xd0, 0x1b, 0x71, 0x1b, 0x49, 0x29, 0x0c, 0x42, + 0x03, 0x01, 0x00, 0x00, 0x01, 0x00, 0x04, 0x02, + 0x00, 0x0f, 0x01, 0x10, 0x00, 0x01, 0x00, 0xb8, + 0x03, 0x01, 0x00, 0xc4, 0x03, 0x07, 0x00, 0x08, + 0xc8, 0xdc, 0x42, 0x12, 0x01, 0x00, 0x00, 0xc4, + 0xdd, 0x47, 0x24, 0x01, 0x00, 0x29, 0x0c, 0x42, + 0x03, 0x01, 0x00, 0x00, 0x01, 0x00, 0x04, 0x02, + 0x00, 0x0e, 0x01, 0x10, 0x00, 0x01, 0x00, 0xb8, + 0x03, 0x01, 0x00, 0xc4, 0x03, 0x07, 0x00, 0x08, + 0xc8, 0xdc, 0x42, 0x13, 0x01, 0x00, 0x00, 0xc4, + 0xdd, 0x47, 0x25, 0x01, 0x00, 0x0c, 0x40, 0x03, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x04, 0x02, 0x00, + 0x32, 0x02, 0xc8, 0x03, 0x00, 0x01, 0x00, 0x10, + 0x00, 0x01, 0x00, 0xb8, 0x03, 0x01, 0x00, 0xc4, + 0x03, 0x07, 0x00, 0x08, 0xc8, 0x0d, 0x00, 0x00, + 0xd8, 0x11, 0xe9, 0x1c, 0x0e, 0xd0, 0xe8, 0xb4, + 0xae, 0x11, 0xe9, 0x14, 0x0e, 0xd0, 0xb3, 0x47, + 0x97, 0x04, 0x48, 0x00, 0x00, 0x00, 0xac, 0x11, + 0xe9, 0x06, 0x0e, 0xd0, 0xb3, 0x47, 0xd8, 0x0e, + 0xdc, 0x42, 0x14, 0x01, 0x00, 0x00, 0xc4, 0xdd, + 0x47, 0xd0, 0x25, 0x02, 0x00, 0x0c, 0x40, 0x03, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x04, 0x02, 0x00, + 0x33, 0x02, 0xc8, 0x03, 0x00, 0x01, 0x00, 0x10, + 0x00, 0x01, 0x00, 0xb8, 0x03, 0x01, 0x00, 0xc4, + 0x03, 0x07, 0x00, 0x08, 0xc8, 0x0d, 0x00, 0x00, + 0xd8, 0x11, 0xe9, 0x1c, 0x0e, 0xd0, 0xe8, 0xb4, + 0xae, 0x11, 0xe9, 0x14, 0x0e, 0xd0, 0xb3, 0x47, + 0x97, 0x04, 0x48, 0x00, 0x00, 0x00, 0xac, 0x11, + 0xe9, 0x06, 0x0e, 0xd0, 0xb3, 0x47, 0xd8, 0x0e, + 0xdc, 0x42, 0x15, 0x01, 0x00, 0x00, 0xc4, 0xdd, + 0x47, 0xd0, 0x24, 0x02, 0x00, 0x29, 0x08, 0xe9, + 0x02, 0x29, 0x38, 0x8a, 0x00, 0x00, 0x00, 0x38, + 0x96, 0x00, 0x00, 0x00, 0x42, 0x11, 0x00, 0x00, + 0x00, 0x04, 0x16, 0x01, 0x00, 0x00, 0x24, 0x01, + 0x00, 0x47, 0xe4, 0x41, 0x17, 0x01, 0x00, 0x00, + 0xe1, 0x38, 0x96, 0x00, 0x00, 0x00, 0x04, 0x18, + 0x01, 0x00, 0x00, 0xee, 0xe2, 0x06, 0x61, 0x00, + 0x00, 0xbd, 0x00, 0x56, 0xdf, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x01, 0x54, 0xee, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x02, 0x54, 0x85, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x03, 0x54, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x04, 0x54, 0x0f, 0x01, 0x00, 0x00, + 0x01, 0xbe, 0x05, 0x54, 0x19, 0x01, 0x00, 0x00, + 0x00, 0x06, 0xc8, 0x0e, 0x68, 0x00, 0x00, 0x5f, + 0x04, 0x00, 0xbe, 0x06, 0x4d, 0xe0, 0x00, 0x00, + 0x00, 0x5f, 0x05, 0x00, 0xbe, 0x07, 0x4d, 0xe1, + 0x00, 0x00, 0x00, 0x5f, 0x06, 0x00, 0x38, 0x96, + 0x00, 0x00, 0x00, 0x04, 0x1a, 0x01, 0x00, 0x00, + 0xee, 0x5f, 0x07, 0x00, 0x06, 0x61, 0x01, 0x00, + 0xbd, 0x08, 0x56, 0xe3, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x09, 0x54, 0x1b, 0x01, 0x00, 0x00, 0x00, + 0xbe, 0x0a, 0x54, 0x36, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x0b, 0x54, 0x1c, 0x01, 0x00, 0x00, 0x00, + 0xbe, 0x0c, 0x54, 0x10, 0x01, 0x00, 0x00, 0x00, + 0x06, 0xc9, 0x0e, 0x68, 0x01, 0x00, 0x5f, 0x08, + 0x00, 0x06, 0x2e, }; diff --git a/src/js/stdlib/sqlite.js b/src/js/stdlib/sqlite.js index c934bcf0..a1ab7ef1 100644 --- a/src/js/stdlib/sqlite.js +++ b/src/js/stdlib/sqlite.js @@ -2,6 +2,7 @@ const core = globalThis[Symbol.for('tjs.internal.core')]; const sqlite3 = core._sqlite3; const kSqlite3Handle = Symbol('kSqlite3Handle'); +let controllers; class Database { constructor(dbName = ':memory:', options = { create: true, readOnly: false }) { @@ -23,6 +24,7 @@ class Database { close() { if (this[kSqlite3Handle]) { sqlite3.close(this[kSqlite3Handle]); + this[kSqlite3Handle] = null; } } @@ -41,8 +43,107 @@ class Database { return new Statement(sqlite3.prepare(this[kSqlite3Handle], sql)); } + + // Code for transactions is largely copied from better-sqlite3 and Bun + // https://github.com/JoshuaWise/better-sqlite3/blob/master/lib/methods/transaction.js + // https://github.com/oven-sh/bun/blob/main/src/js/bun/sqlite.ts + + get inTransaction() { + if (!this[kSqlite3Handle]) { + return false; + } + + return sqlite3.in_transaction(this[kSqlite3Handle]); + } + + transaction(fn) { + if (typeof fn !== 'function') { + throw new TypeError('Expected first argument to be a function'); + } + + const db = this; + const controller = getController(db); + + // Each version of the transaction function has these same properties. + const properties = { + default: { value: wrapTransaction(fn, db, controller.default) }, + deferred: { value: wrapTransaction(fn, db, controller.deferred) }, + immediate: { value: wrapTransaction(fn, db, controller.immediate) }, + exclusive: { value: wrapTransaction(fn, db, controller.exclusive) }, + }; + + Object.defineProperties(properties.default.value, properties); + Object.defineProperties(properties.deferred.value, properties); + Object.defineProperties(properties.immediate.value, properties); + Object.defineProperties(properties.exclusive.value, properties); + + // Return the default version of the transaction function. + return properties.default.value; + } } +// Return the database's cached transaction controller, or create a new one. +const getController = db => { + let controller = (controllers ||= new WeakMap()).get(db); + + if (!controller) { + const shared = { + commit: db.prepare('COMMIT'), + rollback: db.prepare('ROLLBACK'), + savepoint: db.prepare('SAVEPOINT `\t_bs3.\t`'), + release: db.prepare('RELEASE `\t_bs3.\t`'), + rollbackTo: db.prepare('ROLLBACK TO `\t_bs3.\t`'), + }; + + controller = { + default: Object.assign({ begin: db.prepare('BEGIN') }, shared), + deferred: Object.assign({ begin: db.prepare('BEGIN DEFERRED') }, shared), + immediate: Object.assign({ begin: db.prepare('BEGIN IMMEDIATE') }, shared), + exclusive: Object.assign({ begin: db.prepare('BEGIN EXCLUSIVE') }, shared), + }; + + controllers.set(db, controller); + } + + return controller; +}; + +// Return a new transaction function by wrapping the given function. +const wrapTransaction = (fn, db, { begin, commit, rollback, savepoint, release, rollbackTo }) => + function transaction() { + let before, after, undo; + + if (db.inTransaction) { + before = savepoint; + after = release; + undo = rollbackTo; + } else { + before = begin; + after = commit; + undo = rollback; + } + + try { + before.run(); + + const result = Function.prototype.apply.call(fn, this, arguments); + + after.run(); + + return result; + } catch (ex) { + if (db.inTransaction) { + undo.run(); + + if (undo !== rollback) { + after.run(); + } + } + + throw ex; + } + }; + const kSqlite3Stmt = Symbol('kSqlite3Stmt'); class Statement { diff --git a/tests/test-sqlite.js b/tests/test-sqlite.js index cc8580cf..8e597888 100644 --- a/tests/test-sqlite.js +++ b/tests/test-sqlite.js @@ -82,3 +82,116 @@ assert.ok(result.isFile, 'file was created ok'); await tjs.unlink(newDb); testNewDbNoCreate(); + +function testTransactions() { + const db = new Database(); + + assert.falsy(db.inTransaction); + + db.exec('CREATE TABLE test (txt TEXT NOT NULL, int INTEGER, double FLOAT, data BLOB)'); + + const ins = db.prepare('INSERT INTO test (txt, int, double, data) VALUES(?, ?, ?, ?)'); + const insMany = db.transaction(datas => { + assert.ok(db.inTransaction); + + for (const data of datas) { + ins.run(data); + } + }); + + insMany([ + [ 'foo', 42, 4.2, new Uint8Array(16).fill(42) ], + [ 'foo', 43, 4.3, new Uint8Array(16).fill(43) ], + [ 'bar', 69, 6.9, new Uint8Array(16).fill(69) ], + [ 'baz', 666, 6.6, null ], + ]); + + const data1 = db.prepare('SELECT * FROM test').all(); + + assert.eq(data1.length, 4); +} + +function testTransactionsError() { + const db = new Database(); + + assert.falsy(db.inTransaction); + + db.exec('CREATE TABLE test (txt TEXT NOT NULL, int INTEGER, double FLOAT, data BLOB)'); + + const ins = db.prepare('INSERT INTO test (txt, int, double, data) VALUES(?, ?, ?, ?)'); + const insMany = db.transaction(datas => { + assert.ok(db.inTransaction); + + for (const data of datas) { + ins.run(data); + } + + throw new Error('oops!'); + }); + + assert.throws(() => insMany([ + [ 'foo', 42, 4.2, new Uint8Array(16).fill(42) ], + [ 'foo', 43, 4.3, new Uint8Array(16).fill(43) ], + [ 'bar', 69, 6.9, new Uint8Array(16).fill(69) ], + [ 'baz', 666, 6.6, null ], + ]), Error, 'an error is thrown'); + + const data1 = db.prepare('SELECT * FROM test').all(); + + assert.falsy(db.inTransaction); + assert.eq(data1.length, 0); +} + +function testTransactionsNested() { + const db = new Database(); + + assert.falsy(db.inTransaction); + + db.exec('CREATE TABLE test (txt TEXT NOT NULL, int INTEGER, double FLOAT, data BLOB)'); + + const ins = db.prepare('INSERT INTO test (txt, int, double, data) VALUES(?, ?, ?, ?)'); + const ins2 = db.prepare('INSERT INTO test (txt, int) VALUES(?, ?)'); + + const insMany = db.transaction(datas => { + assert.ok(db.inTransaction); + + for (const data of datas) { + ins.run(data); + } + + throw new Error('oops!'); + }); + + const insMany2 = db.transaction(datas => { + assert.ok(db.inTransaction); + + for (const data of datas) { + ins.run(data); + } + + try { + insMany([ + [ 'foo', 42, 4.2, new Uint8Array(16).fill(42) ], + [ 'foo', 43, 4.3, new Uint8Array(16).fill(43) ], + [ 'bar', 69, 6.9, new Uint8Array(16).fill(69) ], + [ 'baz', 666, 6.6, null ] + ]); + } catch(_) { + // Ignore, so the outer transaction succeeds. + } + }); + + insMany2([ + [ '1234', 1234 ], + [ '4321', 4321 ], + ]); + + const data1 = db.prepare('SELECT * FROM test').all(); + + assert.falsy(db.inTransaction); + assert.eq(data1.length, 2); +} + +testTransactions(); +testTransactionsError(); +testTransactionsNested()