diff options
-rw-r--r-- | integration/bunjs-only-snippets/sqlite.test.js | 47 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/sqlite/JSSQLStatement.cpp | 11 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/sqlite/sqlite.exports.js | 15 |
3 files changed, 63 insertions, 10 deletions
diff --git a/integration/bunjs-only-snippets/sqlite.test.js b/integration/bunjs-only-snippets/sqlite.test.js index 3f1e6c5db..2250f97f0 100644 --- a/integration/bunjs-only-snippets/sqlite.test.js +++ b/integration/bunjs-only-snippets/sqlite.test.js @@ -381,3 +381,50 @@ it("db.query()", () => { db.close(); db.close(); }); + +it("db.transaction()", () => { + const db = Database.open(":memory:"); + + db.exec( + "CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)" + ); + + const insert = db.prepare( + "INSERT INTO cats (name, age) VALUES (@name, @age)" + ); + + expect(db.inTransaction).toBe(false); + const insertMany = db.transaction((cats) => { + expect(db.inTransaction).toBe(true); + try { + for (const cat of cats) insert.run(cat); + } catch (exception) { + throw exception; + } + }); + + try { + insertMany([ + { "@name": "Joey", "@age": 2 }, + { "@name": "Sally", "@age": 4 }, + { "@name": "Junior", "@age": 1 }, + { "@name": "Sally", "@age": 4 }, + ]); + throw new Error("Should have thrown"); + } catch (exception) { + expect(exception.message).toBe("constraint failed"); + } + + expect(db.inTransaction).toBe(false); + expect(db.query("SELECT * FROM cats").all().length).toBe(0); + + expect(db.inTransaction).toBe(false); + insertMany([ + { "@name": "Joey", "@age": 2 }, + { "@name": "Sally", "@age": 4 }, + { "@name": "Junior", "@age": 1 }, + ]); + expect(db.inTransaction).toBe(false); + expect(db.query("SELECT * FROM cats").all().length).toBe(3); + expect(db.inTransaction).toBe(false); +}); diff --git a/src/javascript/jsc/bindings/sqlite/JSSQLStatement.cpp b/src/javascript/jsc/bindings/sqlite/JSSQLStatement.cpp index 2540e1dc0..d7cccd771 100644 --- a/src/javascript/jsc/bindings/sqlite/JSSQLStatement.cpp +++ b/src/javascript/jsc/bindings/sqlite/JSSQLStatement.cpp @@ -254,7 +254,7 @@ static JSC::JSValue rebindObject(JSC::JSGlobalObject* globalObject, JSC::JSValue int index = sqlite3_bind_parameter_index(stmt, WTF::String(propertyName.string()).utf8().data()); if (index == 0) { - throwException(globalObject, scope, createError(globalObject, "Unknown parameter name " + propertyName.string())); + throwException(globalObject, scope, createError(globalObject, "Unknown parameter \"" + propertyName.string() + "\""_s)); return JSValue(); } @@ -646,7 +646,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementIsInTransactionFunction, (JSC::JSGlobalOb return JSValue::encode(JSC::jsUndefined()); } - RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(sqlite3_get_autocommit(db)))); + RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(!sqlite3_get_autocommit(db)))); } JSC_DEFINE_HOST_FUNCTION(jsSQLStatementPrepareStatementFunction, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) @@ -1086,6 +1086,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionAll, (JSC::JSGlob if (UNLIKELY(status != SQLITE_DONE)) { throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status)))); + sqlite3_reset(stmt); return JSValue::encode(jsUndefined()); } @@ -1098,6 +1099,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionAll, (JSC::JSGlob RELEASE_AND_RETURN(scope, JSValue::encode(JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0))); } else { throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status)))); + sqlite3_reset(stmt); return JSValue::encode(jsUndefined()); } } @@ -1145,6 +1147,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionGet, (JSC::JSGlob RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsNull())); } else { throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status)))); + sqlite3_reset(stmt); return JSValue::encode(jsUndefined()); } } @@ -1164,6 +1167,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRows, (JSC::JSGlo int statusCode = sqlite3_reset(stmt); if (UNLIKELY(statusCode != SQLITE_OK)) { throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(statusCode)))); + sqlite3_reset(stmt); return JSValue::encode(jsUndefined()); } @@ -1204,6 +1208,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRows, (JSC::JSGlo if (UNLIKELY(status != SQLITE_DONE)) { throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status)))); + sqlite3_reset(stmt); return JSValue::encode(jsUndefined()); } @@ -1217,6 +1222,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRows, (JSC::JSGlo RELEASE_AND_RETURN(scope, JSValue::encode(JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0))); } else { throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status)))); + sqlite3_reset(stmt); return JSValue::encode(jsUndefined()); } } @@ -1254,6 +1260,7 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRun, (JSC::JSGlob // sqlite3_reset(stmt); RELEASE_AND_RETURN(scope, JSC::JSValue::encode(jsUndefined())); } else { + sqlite3_reset(stmt); throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status)))); return JSValue::encode(jsUndefined()); } diff --git a/src/javascript/jsc/bindings/sqlite/sqlite.exports.js b/src/javascript/jsc/bindings/sqlite/sqlite.exports.js index bf060c5b0..acd28164c 100644 --- a/src/javascript/jsc/bindings/sqlite/sqlite.exports.js +++ b/src/javascript/jsc/bindings/sqlite/sqlite.exports.js @@ -375,13 +375,13 @@ export class Database { // Each version of the transaction function has these same properties const properties = { - default: { value: wrapTransaction(apply, fn, db, controller.default) }, - deferred: { value: wrapTransaction(apply, fn, db, controller.deferred) }, + default: { value: wrapTransaction(fn, db, controller.default) }, + deferred: { value: wrapTransaction(fn, db, controller.deferred) }, immediate: { - value: wrapTransaction(apply, fn, db, controller.immediate), + value: wrapTransaction(fn, db, controller.immediate), }, exclusive: { - value: wrapTransaction(apply, fn, db, controller.exclusive), + value: wrapTransaction(fn, db, controller.exclusive), }, database: { value: this, enumerable: true }, }; @@ -437,12 +437,11 @@ const getController = (db, self) => { // Return a new transaction function by wrapping the given function const wrapTransaction = ( - apply, fn, db, { begin, commit, rollback, savepoint, release, rollbackTo } ) => - function sqliteTransaction() { + function transaction(...args) { let before, after, undo; if (db.inTransaction) { before = savepoint; @@ -453,9 +452,9 @@ const wrapTransaction = ( after = commit; undo = rollback; } - before.run(); try { - const result = apply.call(fn, this, arguments); + before.run(); + const result = fn.apply(this, args); after.run(); return result; } catch (ex) { |