aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bun.js/bindings/sqlite/JSSQLStatement.cpp135
-rw-r--r--test/bun.js/sqlite-cross-process.js45
-rw-r--r--test/bun.js/sqlite.test.js26
3 files changed, 142 insertions, 64 deletions
diff --git a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp
index efcb12355..39f1f42ed 100644
--- a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp
+++ b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp
@@ -727,9 +727,11 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteFunction, (JSC::JSGlobalObject * l
thisObject->databases[handle]->version++;
}
- // we don't care about the results, therefore the row-by-row output doesn't matter
- // that's why we don't bother to loop through the results
- if (rc != SQLITE_DONE and rc != SQLITE_ROW) {
+ while (rc == SQLITE_ROW) {
+ rc = sqlite3_step(statement);
+ }
+
+ if (rc != SQLITE_DONE) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(rc))));
// we finalize after just incase something about error messages in
// sqlite depends on the existence of the most recent statement i don't
@@ -1206,44 +1208,46 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionAll, (JSC::JSGlob
}
size_t columnCount = castedThis->columnNames->size();
- int counter = 0;
+ JSValue result = jsUndefined();
if (status == SQLITE_ROW) {
// this is a count from UPDATE or another query like that
if (columnCount == 0) {
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(jsNumber(sqlite3_changes(castedThis->version_db->db))));
- }
-
- JSC::JSArray* resultArray = JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0);
- {
- JSC::ObjectInitializationScope initializationScope(vm);
- JSC::GCDeferralContext deferralContext(vm);
+ result = jsNumber(sqlite3_changes(castedThis->version_db->db));
while (status == SQLITE_ROW) {
- JSC::JSValue result = constructResultObject(lexicalGlobalObject, castedThis);
- resultArray->push(lexicalGlobalObject, result);
status = sqlite3_step(stmt);
}
- }
+ } else {
- if (UNLIKELY(status != SQLITE_DONE)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
- sqlite3_reset(stmt);
- return JSValue::encode(jsUndefined());
- }
+ JSC::JSArray* resultArray = JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0);
+ {
+ JSC::ObjectInitializationScope initializationScope(vm);
+ JSC::GCDeferralContext deferralContext(vm);
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(resultArray));
+ while (status == SQLITE_ROW) {
+ JSC::JSValue result = constructResultObject(lexicalGlobalObject, castedThis);
+ resultArray->push(lexicalGlobalObject, result);
+ status = sqlite3_step(stmt);
+ }
+ }
+ result = resultArray;
+ }
} else if (status == SQLITE_DONE) {
if (columnCount == 0) {
- RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(0)));
+ result = jsNumber(sqlite3_changes(castedThis->version_db->db));
+ } else {
+ result = JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0);
}
+ }
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0)));
- } else {
+ if (UNLIKELY(status != SQLITE_DONE)) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
sqlite3_reset(stmt);
return JSValue::encode(jsUndefined());
}
+
+ RELEASE_AND_RETURN(scope, JSC::JSValue::encode(result));
}
JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionGet, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
@@ -1278,13 +1282,16 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionGet, (JSC::JSGlob
initializeColumnNames(lexicalGlobalObject, castedThis);
}
- size_t columnCount = castedThis->columnNames->size();
- int counter = 0;
-
+ JSValue result = jsNull();
if (status == SQLITE_ROW) {
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(constructResultObject(lexicalGlobalObject, castedThis)));
- } else if (status == SQLITE_DONE) {
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsNull()));
+ result = constructResultObject(lexicalGlobalObject, castedThis);
+ while (status == SQLITE_ROW) {
+ status = sqlite3_step(stmt);
+ }
+ }
+
+ if (status == SQLITE_DONE) {
+ RELEASE_AND_RETURN(scope, JSValue::encode(result));
} else {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
sqlite3_reset(stmt);
@@ -1298,8 +1305,11 @@ static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(jsSQLStatementExecuteState
JSC_DEFINE_JIT_OPERATION(jsSQLStatementExecuteStatementFunctionGetWithoutTypeChecking, EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSSQLStatement* castedThis))
{
-
- JSC::VM& vm = lexicalGlobalObject->vm();
+ VM& vm = JSC::getVM(lexicalGlobalObject);
+ IGNORE_WARNINGS_BEGIN("frame-address")
+ CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+ IGNORE_WARNINGS_END
+ JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
auto scope = DECLARE_THROW_SCOPE(vm);
auto* stmt = castedThis->stmt;
@@ -1320,13 +1330,16 @@ JSC_DEFINE_JIT_OPERATION(jsSQLStatementExecuteStatementFunctionGetWithoutTypeChe
initializeColumnNames(lexicalGlobalObject, castedThis);
}
- size_t columnCount = castedThis->columnNames->size();
- int counter = 0;
-
+ JSValue result = jsNull();
if (status == SQLITE_ROW) {
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(constructResultObject(lexicalGlobalObject, castedThis)));
- } else if (status == SQLITE_DONE) {
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsNull()));
+ result = constructResultObject(lexicalGlobalObject, castedThis);
+ while (status == SQLITE_ROW) {
+ status = sqlite3_step(stmt);
+ }
+ }
+
+ if (status == SQLITE_DONE) {
+ RELEASE_AND_RETURN(scope, JSValue::encode(result));
} else {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
sqlite3_reset(stmt);
@@ -1378,46 +1391,42 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRows, (JSC::JSGlo
}
size_t columnCount = castedThis->columnNames->size();
- int counter = 0;
-
+ JSValue result = jsNull();
if (status == SQLITE_ROW) {
// this is a count from UPDATE or another query like that
if (columnCount == 0) {
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(jsNumber(sqlite3_changes(castedThis->version_db->db))));
- }
-
- JSC::ObjectInitializationScope initializationScope(vm);
- JSC::GCDeferralContext deferralContext(vm);
-
- JSC::JSArray* resultArray = JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0);
- {
-
while (status == SQLITE_ROW) {
- JSC::JSValue result = constructResultRow(lexicalGlobalObject, castedThis, initializationScope, &deferralContext);
- resultArray->push(lexicalGlobalObject, result);
status = sqlite3_step(stmt);
}
- }
- if (UNLIKELY(status != SQLITE_DONE)) {
- throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
- sqlite3_reset(stmt);
- return JSValue::encode(jsUndefined());
- }
+ result = jsNumber(sqlite3_column_count(stmt));
- // sqlite3_reset(stmt);
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(resultArray));
- } else if (status == SQLITE_DONE) {
- if (columnCount == 0) {
- RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(0)));
+ } else {
+ JSC::ObjectInitializationScope initializationScope(vm);
+ JSC::GCDeferralContext deferralContext(vm);
+
+ JSC::JSArray* resultArray = JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0);
+ {
+
+ while (status == SQLITE_ROW) {
+ JSC::JSValue row = constructResultRow(lexicalGlobalObject, castedThis, initializationScope, &deferralContext);
+ resultArray->push(lexicalGlobalObject, row);
+ status = sqlite3_step(stmt);
+ }
+ }
+
+ result = resultArray;
}
+ }
- RELEASE_AND_RETURN(scope, JSValue::encode(JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0)));
- } else {
+ if (UNLIKELY(status != SQLITE_DONE)) {
throwException(lexicalGlobalObject, scope, createError(lexicalGlobalObject, WTF::String::fromUTF8(sqlite3_errstr(status))));
sqlite3_reset(stmt);
return JSValue::encode(jsUndefined());
}
+
+ // sqlite3_reset(stmt);
+ RELEASE_AND_RETURN(scope, JSC::JSValue::encode(result));
}
JSC_DEFINE_HOST_FUNCTION(jsSQLStatementExecuteStatementFunctionRun, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
diff --git a/test/bun.js/sqlite-cross-process.js b/test/bun.js/sqlite-cross-process.js
new file mode 100644
index 000000000..d5b9b87b5
--- /dev/null
+++ b/test/bun.js/sqlite-cross-process.js
@@ -0,0 +1,45 @@
+// https://github.com/oven-sh/bun/issues/1366
+import { Database } from "bun:sqlite";
+import { rmSync } from "fs";
+
+const dir = process.env.SQLITE_DIR;
+
+rmSync(dir + "get-persist.sqlite", { force: true });
+
+var db = Database.open(dir + "get-persist.sqlite", { create: true });
+
+// Note, I've played with various values and it doesn't seem to change
+// the behavior. The "beter-sqlite3" npm package does not exhibit this
+// bug, so it doesn't seem to be a general SQLite thing.
+db.run(`PRAGMA journal_mode = WAL`);
+db.run(`PRAGMA synchrounous = NORMAL`);
+
+db.run(
+ `CREATE TABLE IF NOT EXISTS examples (
+ id TEXT PRIMARY KEY
+ )`,
+);
+
+// This persists, but if you place this call
+db.run(
+ `
+ INSERT INTO examples
+ VALUES ('hello')
+ ON CONFLICT (id) DO
+ UPDATE SET id='hello'
+ RETURNING id
+ `,
+);
+
+db.query(`SELECT id FROM examples WHERE id='hello'`).get().id;
+db.query(
+ `
+INSERT INTO examples
+VALUES ('world')
+ON CONFLICT (id) DO
+ UPDATE SET id='world'
+RETURNING id
+`,
+).get();
+
+process.exit(0);
diff --git a/test/bun.js/sqlite.test.js b/test/bun.js/sqlite.test.js
index e5f83fe4d..f78d3b481 100644
--- a/test/bun.js/sqlite.test.js
+++ b/test/bun.js/sqlite.test.js
@@ -1,6 +1,9 @@
import { expect, it, describe } from "bun:test";
import { Database, constants } from "bun:sqlite";
-import { existsSync, fstat, writeFileSync } from "fs";
+import { existsSync, fstat, realpathSync, rmSync, writeFileSync } from "fs";
+import { spawnSync } from "bun";
+import { bunExe } from "bunExe";
+import { tmpdir } from "os";
var encode = (text) => new TextEncoder().encode(text);
it("Database.open", () => {
@@ -55,6 +58,27 @@ it("Database.open", () => {
new Database().close();
});
+it("upsert cross-process, see #1366", () => {
+ const dir = realpathSync(tmpdir()) + "/";
+ const { exitCode } = spawnSync(
+ [bunExe(), import.meta.dir + "/sqlite-cross-process.js"],
+ {
+ env: {
+ SQLITE_DIR: dir,
+ },
+ stderr: "inherit",
+ },
+ );
+ expect(exitCode).toBe(0);
+
+ const db2 = Database.open(dir + "get-persist.sqlite");
+
+ expect(db2.query(`SELECT id FROM examples`).all()).toEqual([
+ { id: "hello" },
+ { id: "world" },
+ ]);
+});
+
it("creates", () => {
const db = Database.open(":memory:");
db.exec(