From a1d191105d043ffc20fabf9ab910416fa2df7573 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Tue, 13 May 2025 16:14:59 +0200 Subject: [PATCH] Fix crash around blobs --- .changeset/six-moose-buy.md | 5 +++++ cpp/JSIHelper.cpp | 17 ++++++++++------- cpp/JSIHelper.h | 5 ++--- cpp/sqliteExecute.cpp | 10 ++++------ tests/tests/sqlite/rawQueries.spec.ts | 15 +++++++++++++++ 5 files changed, 36 insertions(+), 16 deletions(-) create mode 100644 .changeset/six-moose-buy.md diff --git a/.changeset/six-moose-buy.md b/.changeset/six-moose-buy.md new file mode 100644 index 0000000..8b57fd7 --- /dev/null +++ b/.changeset/six-moose-buy.md @@ -0,0 +1,5 @@ +--- +"@journeyapps/react-native-quick-sqlite": patch +--- + +Fix crash when binding or reading blobs. diff --git a/cpp/JSIHelper.cpp b/cpp/JSIHelper.cpp index 6fcb149..6bdb47e 100644 --- a/cpp/JSIHelper.cpp +++ b/cpp/JSIHelper.cpp @@ -7,6 +7,8 @@ #include "JSIHelper.h" +#include + using namespace std; using namespace facebook; @@ -27,7 +29,7 @@ QuickValue createTextQuickValue(string value) { return QuickValue{ .dataType = TEXT, - .textValue = value}; + .textValue = std::move(value)}; } QuickValue createIntegerQuickValue(int value) @@ -58,12 +60,13 @@ QuickValue createDoubleQuickValue(double value) .doubleOrIntValue = value}; } -QuickValue createArrayBufferQuickValue(uint8_t *arrayBufferValue, size_t arrayBufferSize) +QuickValue createArrayBufferQuickValueByCopying(const uint8_t *arrayBufferValue, size_t arrayBufferSize) { + vector copy(arrayBufferValue, arrayBufferValue + arrayBufferSize); + return QuickValue{ .dataType = ARRAY_BUFFER, - .arrayBufferValue = shared_ptr{arrayBufferValue}, - .arrayBufferSize = arrayBufferSize}; + .arrayBuffer = copy}; } void jsiQueryArgumentsToSequelParam(jsi::Runtime &rt, jsi::Value const ¶ms, vector *target) @@ -116,7 +119,7 @@ void jsiQueryArgumentsToSequelParam(jsi::Runtime &rt, jsi::Value const ¶ms, if (obj.isArrayBuffer(rt)) { auto buf = obj.getArrayBuffer(rt); - target->push_back(createArrayBufferQuickValue(buf.data(rt), buf.size(rt))); + target->push_back(createArrayBufferQuickValueByCopying(buf.data(rt), buf.size(rt))); } } else @@ -171,10 +174,10 @@ jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult sta } else if (value.dataType == ARRAY_BUFFER) { jsi::Function array_buffer_ctor = rt.global().getPropertyAsFunction(rt, "ArrayBuffer"); - jsi::Object o = array_buffer_ctor.callAsConstructor(rt, (int) value.arrayBufferSize).getObject(rt); + jsi::Object o = array_buffer_ctor.callAsConstructor(rt, (int) value.arrayBuffer.size()).getObject(rt); jsi::ArrayBuffer buf = o.getArrayBuffer(rt); // It's a shame we have to copy here: see https://github.com/facebook/hermes/pull/419 and https://github.com/facebook/hermes/issues/564. - memcpy(buf.data(rt), value.arrayBufferValue.get(), value.arrayBufferSize); + memcpy(buf.data(rt), value.arrayBuffer.data(), value.arrayBuffer.size()); rowObject.setProperty(rt, columnName.c_str(), o); } else { diff --git a/cpp/JSIHelper.h b/cpp/JSIHelper.h index cf84c4d..997944c 100644 --- a/cpp/JSIHelper.h +++ b/cpp/JSIHelper.h @@ -41,8 +41,7 @@ struct QuickValue double doubleOrIntValue; long long int64Value; string textValue; - shared_ptr arrayBufferValue; - size_t arrayBufferSize; + vector arrayBuffer; }; /** @@ -108,7 +107,7 @@ QuickValue createIntegerQuickValue(int value); QuickValue createIntegerQuickValue(double value); QuickValue createInt64QuickValue(long long value); QuickValue createDoubleQuickValue(double value); -QuickValue createArrayBufferQuickValue(uint8_t *arrayBufferValue, size_t arrayBufferSize); +QuickValue createArrayBufferQuickValueByCopying(const uint8_t *arrayBufferValue, size_t arrayBufferSize); jsi::Value createSequelQueryExecutionResult(jsi::Runtime &rt, SQLiteOPResult status, vector> *results, vector *metadata); #endif /* JSIHelper_h */ diff --git a/cpp/sqliteExecute.cpp b/cpp/sqliteExecute.cpp index 3865d57..be6b4f3 100644 --- a/cpp/sqliteExecute.cpp +++ b/cpp/sqliteExecute.cpp @@ -8,7 +8,7 @@ void bindStatement(sqlite3_stmt *statement, vector *values) { for (int ii = 0; ii < size; ii++) { int sqIndex = ii + 1; - QuickValue value = values->at(ii); + const QuickValue& value = values->at(ii); QuickDataType dataType = value.dataType; if (dataType == NULL_VALUE) { sqlite3_bind_null(statement, sqIndex); @@ -24,8 +24,8 @@ void bindStatement(sqlite3_stmt *statement, vector *values) { sqlite3_bind_text(statement, sqIndex, value.textValue.c_str(), value.textValue.length(), SQLITE_TRANSIENT); } else if (dataType == ARRAY_BUFFER) { - sqlite3_bind_blob(statement, sqIndex, value.arrayBufferValue.get(), - value.arrayBufferSize, SQLITE_STATIC); + sqlite3_bind_blob(statement, sqIndex, value.arrayBuffer.data(), + value.arrayBuffer.size(), SQLITE_STATIC); } } } @@ -114,9 +114,7 @@ sqliteExecuteWithDB(sqlite3 *db, std::string const &query, case SQLITE_BLOB: { int blob_size = sqlite3_column_bytes(statement, i); const void *blob = sqlite3_column_blob(statement, i); - uint8_t *data; - memcpy(data, blob, blob_size); - row[column_name] = createArrayBufferQuickValue(data, blob_size); + row[column_name] = createArrayBufferQuickValueByCopying(static_cast(blob), blob_size); break; } diff --git a/tests/tests/sqlite/rawQueries.spec.ts b/tests/tests/sqlite/rawQueries.spec.ts index 1535fb4..8e942c6 100644 --- a/tests/tests/sqlite/rawQueries.spec.ts +++ b/tests/tests/sqlite/rawQueries.spec.ts @@ -133,6 +133,21 @@ export function registerBaseTests() { ]); }); + it('binding blobs', async () => { + const data = new TextEncoder().encode('hello blob'); + const res = await db.execute('SELECT hex(?) AS r', [data.buffer]); + + expect(res.rows?._array).to.eql([{ r: '68656C6C6F20626C6F62' }]); + }); + + it('reading blobs', async () => { + const res = await db.execute('SELECT unhex(1234) AS r'); + const value = res.rows._array[0].r; + + expect(value).to.be.instanceOf(ArrayBuffer); + expect([...new Uint8Array(value)]).to.eql([18, 52]); + }); + it('Failed insert', async () => { const id = chance.string(); // Setting the id to a string will throw an exception, it expects an int const { name, age, networth } = generateUserInfo();