diff --git a/src/api.js b/src/api.js index 126baf1c..b70dfddb 100644 --- a/src/api.js +++ b/src/api.js @@ -10,6 +10,7 @@ stackRestore stackSave UTF8ToString + lengthBytesUTF8 stringToNewUTF8 removeFunction addFunction @@ -137,7 +138,7 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() { ); var sqlite3_column_text = cwrap( "sqlite3_column_text", - "string", + "number", ["number", "number"] ); var sqlite3_column_blob = cwrap( @@ -188,7 +189,7 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() { "number", ["number"] ); - var sqlite3_value_text = cwrap("sqlite3_value_text", "string", ["number"]); + var sqlite3_value_text = cwrap("sqlite3_value_text", "number", ["number"]); var sqlite3_value_blob = cwrap("sqlite3_value_blob", "number", ["number"]); var sqlite3_value_double = cwrap( "sqlite3_value_double", @@ -378,12 +379,11 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() { pos = this.pos; this.pos += 1; } - var text = sqlite3_column_text(this.stmt, pos); if (typeof BigInt !== "function") { throw new Error("BigInt is not supported"); } /* global BigInt */ - return BigInt(text); + return BigInt(this.getString(pos)); }; Statement.prototype.getString = function getString(pos) { @@ -391,7 +391,9 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() { pos = this.pos; this.pos += 1; } - return sqlite3_column_text(this.stmt, pos); + var ptr = sqlite3_column_text(this.stmt, pos); + var size = sqlite3_column_bytes(this.stmt, pos); + return UTF8ToString(ptr, size, true); }; Statement.prototype.getBlob = function getBlob(pos) { @@ -547,7 +549,7 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() { this.stmt, pos, strptr, - -1, + lengthBytesUTF8(string), 0 )); return true; @@ -1181,7 +1183,9 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() { ) { arg = sqlite3_value_double(value_ptr); } else if (value_type === SQLITE_TEXT) { - arg = sqlite3_value_text(value_ptr); + var text_size = sqlite3_value_bytes(value_ptr); + var text_ptr = sqlite3_value_text(value_ptr); + arg = UTF8ToString(text_ptr, text_size, true); } else if (value_type === SQLITE_BLOB) { arg = extract_blob(value_ptr); } else arg = null; @@ -1198,7 +1202,7 @@ Module["onRuntimeInitialized"] = function onRuntimeInitialized() { sqlite3_result_double(cx, result); break; case "string": - sqlite3_result_text(cx, result, -1, -1); + sqlite3_result_text(cx, result, lengthBytesUTF8(result), -1); break; case "object": if (result === null) { diff --git a/src/exported_runtime_methods.json b/src/exported_runtime_methods.json index f099056f..6d2c5f25 100644 --- a/src/exported_runtime_methods.json +++ b/src/exported_runtime_methods.json @@ -4,6 +4,7 @@ "stackSave", "stackRestore", "UTF8ToString", +"lengthBytesUTF8", "stringToNewUTF8", "writeArrayToMemory", "removeFunction", diff --git a/test/test_functions.js b/test/test_functions.js index 017b7a8a..eb1cc2a8 100644 --- a/test/test_functions.js +++ b/test/test_functions.js @@ -53,6 +53,27 @@ exports.test = function(SQL, assert){ // Test api support of different sqlite types and special values db.create_function("identityFunction", function (x) { return x;} ); + + var capturedText; + db.create_function("CaptureEmbeddedNul", function (x) { + capturedText = x; + return 1; + }); + db.exec("SELECT CaptureEmbeddedNul(CAST(x'410042' AS TEXT));"); + assert.equal( + capturedText, + "A\0B", + "TEXT arguments to JavaScript functions preserve embedded NUL" + ); + + db.create_function("ReturnEmbeddedNul", function () { return "A\0B"; }); + result = db.exec("SELECT ReturnEmbeddedNul()"); + assert.equal( + result[0]["values"][0][0], + "A\0B", + "TEXT results from JavaScript functions preserve embedded NUL" + ); + var verbose=false; function canHandle(testData) { diff --git a/test/test_statement.js b/test/test_statement.js index f8a88186..d56dcd46 100644 --- a/test/test_statement.js +++ b/test/test_statement.js @@ -44,6 +44,25 @@ exports.test = function(sql, assert){ stmt = db.prepare("SELECT str FROM data WHERE str=?"); assert.deepEqual(stmt.getAsObject(['粵語😄']), {'str':'粵語😄'}, "UTF8 support in prepared statements"); + stmt.free(); + + var nulText = "A\0漢😄"; + stmt = db.prepare("SELECT ?"); + assert.deepEqual( + stmt.get([nulText]), + [nulText], + "Binding TEXT preserves embedded NUL and multibyte UTF-8" + ); + stmt.free(); + + stmt = db.prepare("SELECT CAST(x'410042' AS TEXT)"); + stmt.step(); + assert.strictEqual( + stmt.get()[0], + "A\0B", + "Reading TEXT preserves embedded NUL" + ); + stmt.free(); // Prepare an sql statement stmt = db.prepare("SELECT * FROM alphabet WHERE code BETWEEN :start AND :end ORDER BY code");