Skip to content

Commit e7dd159

Browse files
authored
Fixes sub array insertion (#401)
2 parents ed02288 + a3dbfe3 commit e7dd159

5 files changed

Lines changed: 65 additions & 64 deletions

File tree

cpp/utils.cpp

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,18 +97,45 @@ inline JSVariant to_variant(jsi::Runtime &rt, const jsi::Value &value) {
9797
return JSVariant(strVal);
9898
} else if (value.isObject()) {
9999
auto obj = value.asObject(rt);
100+
size_t byteOffset = 0;
101+
size_t byteLength = 0;
102+
uint8_t *sourceData = nullptr;
103+
jsi::Function arrayBufferCtor =
104+
rt.global().getPropertyAsFunction(rt, "ArrayBuffer");
105+
jsi::Function isViewFn =
106+
arrayBufferCtor.getPropertyAsFunction(rt, "isView");
107+
bool isArrayBufferView = isViewFn.call(rt, obj).getBool();
108+
109+
if (obj.isArrayBuffer(rt)) {
110+
auto buffer = obj.getArrayBuffer(rt);
111+
sourceData = buffer.data(rt);
112+
byteLength = buffer.size(rt);
113+
} else if (isArrayBufferView) {
114+
jsi::Object bufferObject = obj.getPropertyAsObject(rt, "buffer");
115+
auto buffer = bufferObject.getArrayBuffer(rt);
116+
117+
byteOffset =
118+
static_cast<size_t>(obj.getProperty(rt, "byteOffset").asNumber());
119+
byteLength =
120+
static_cast<size_t>(obj.getProperty(rt, "byteLength").asNumber());
121+
122+
const size_t bufferSize = buffer.size(rt);
123+
if (byteOffset > bufferSize || byteLength > bufferSize - byteOffset) {
124+
throw std::runtime_error("Invalid ArrayBuffer view range");
125+
}
100126

101-
if (!obj.isArrayBuffer(rt)) {
127+
sourceData = buffer.data(rt) + byteOffset;
128+
} else {
102129
throw std::runtime_error(
103-
"Object is not an ArrayBuffer, cannot bind to SQLite");
130+
"Object is not an ArrayBuffer or ArrayBuffer view, cannot bind "
131+
"to SQLite");
104132
}
105133

106-
auto buffer = obj.getArrayBuffer(rt);
107-
uint8_t *data = new uint8_t[buffer.size(rt)];
108-
memcpy(data, buffer.data(rt), buffer.size(rt));
134+
uint8_t *data = new uint8_t[byteLength];
135+
memcpy(data, sourceData, byteLength);
109136

110137
return JSVariant(ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
111-
.size = buffer.size(rt)});
138+
.size = byteLength});
112139
}
113140

114141
throw std::runtime_error("Cannot convert JSI value to C++ Variant value");

example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2002,7 +2002,7 @@ EXTERNAL SOURCES:
20022002
SPEC CHECKSUMS:
20032003
FBLazyVector: 2e5b5553df729e080483373db6f045201ff4e6db
20042004
hermes-engine: 273e30e7fb618279934b0b95ffab60ecedb7acf5
2005-
op-sqlite: aec4b9ac1b22d981c2ddaf27a0acc199c1187a3c
2005+
op-sqlite: 4df330bec6c23e6d9c9a5e256831869c58a618ea
20062006
RCTDeprecation: c6b36da89aa26090c8684d29c2868dcca2cd4554
20072007
RCTRequired: 1413a0844770d00fa1f1bb2da4680adfa8698065
20082008
RCTTypeSafety: 354b4bb344998550c45d054ef66913837948f958

example/src/tests/blob.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
describe,
66
expect,
77
it,
8+
itOnly,
89
} from "@op-engineering/op-test";
910

1011
let db: DB;
@@ -56,10 +57,29 @@ describe("Blobs", () => {
5657

5758
const result = await db.execute("SELECT content FROM BlobTable");
5859

59-
const finalUint8 = new Uint8Array(result.rows[0]!.content as any);
60+
const finalUint8 = new Uint8Array(result.rows[0]?.content as any);
6061
expect(finalUint8[0]).toBe(42);
6162
});
6263

64+
it("Uint8Array subarray", async () => {
65+
const uint8 = new Uint8Array([97, 98, 99, 100]);
66+
const subarray = uint8.subarray(1, 3);
67+
68+
await db.execute(`INSERT OR REPLACE INTO BlobTable VALUES (?, ?);`, [
69+
1,
70+
subarray,
71+
]);
72+
73+
const result = await db.execute("SELECT content FROM BlobTable");
74+
const content = result.rows[0]?.content;
75+
expect(content).toBeTruthy();
76+
77+
const finalUint8 = new Uint8Array(content as ArrayBuffer);
78+
const res = Array.from(finalUint8);
79+
expect(res[0]).toEqual(98);
80+
expect(res[1]).toEqual(99);
81+
});
82+
6383
it("Uint16Array", async () => {
6484
const uint8 = new Uint16Array(2);
6585
uint8[0] = 42;

example/src/tests/queries.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ describe("Queries tests", () => {
101101
} catch (e: any) {
102102
expect(
103103
e.message.includes(
104-
"Exception in HostFunction: Object is not an ArrayBuffer, cannot bind to SQLite",
104+
"Object is not an ArrayBuffer or ArrayBuffer view",
105105
),
106106
).toEqual(true);
107107
}

src/functions.ts

Lines changed: 9 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -68,26 +68,6 @@ function enhanceDB(db: _InternalDB, options: DBParams): DB {
6868
}
6969
};
7070

71-
function sanitizeArrayBuffersInArray(
72-
params?: any[] | any[][],
73-
): any[] | undefined {
74-
if (!params) {
75-
return params;
76-
}
77-
78-
return params.map((p) => {
79-
if (Array.isArray(p)) {
80-
return sanitizeArrayBuffersInArray(p);
81-
}
82-
83-
if (ArrayBuffer.isView(p)) {
84-
return p.buffer;
85-
}
86-
87-
return p;
88-
});
89-
}
90-
9171
// spreading the object does not work with HostObjects (db)
9272
// We need to manually assign the fields
9373
const enhancedDb = {
@@ -112,14 +92,6 @@ function enhanceDB(db: _InternalDB, options: DBParams): DB {
11292
executeBatch: async (
11393
commands: SQLBatchTuple[],
11494
): Promise<BatchQueryResult> => {
115-
// Do normal for loop and replace in place for performance
116-
for (let i = 0; i < commands.length; i++) {
117-
// [1] is the params arg
118-
if (commands[i]![1]) {
119-
commands[i]![1] = sanitizeArrayBuffersInArray(commands[i]![1]) as any;
120-
}
121-
}
122-
12395
async function run() {
12496
try {
12597
enhancedDb.executeSync("BEGIN TRANSACTION;");
@@ -160,33 +132,24 @@ function enhanceDB(db: _InternalDB, options: DBParams): DB {
160132
query: string,
161133
params?: Scalar[],
162134
): Promise<QueryResult> => {
163-
const sanitizedParams = sanitizeArrayBuffersInArray(params);
164-
165-
return sanitizedParams
166-
? await db.executeWithHostObjects(query, sanitizedParams as Scalar[])
135+
return params
136+
? await db.executeWithHostObjects(query, params)
167137
: await db.executeWithHostObjects(query);
168138
},
169139
executeRaw: async (query: string, params?: Scalar[]) => {
170-
const sanitizedParams = sanitizeArrayBuffersInArray(params);
171-
172-
return db.executeRaw(query, sanitizedParams as Scalar[]);
140+
return db.executeRaw(query, params as Scalar[]);
173141
},
174142
executeRawSync: (query: string, params?: Scalar[]) => {
175-
const sanitizedParams = sanitizeArrayBuffersInArray(params);
176-
return db.executeRawSync(query, sanitizedParams as Scalar[]);
143+
return db.executeRawSync(query, params as Scalar[]);
177144
},
178145
// Wrapper for executeRaw, drizzleORM uses this function
179146
// at some point I changed the API but they did not pin their dependency to a specific version
180147
// so re-inserting this so it starts working again
181148
executeRawAsync: async (query: string, params?: Scalar[]) => {
182-
const sanitizedParams = sanitizeArrayBuffersInArray(params);
183-
184-
return db.executeRaw(query, sanitizedParams as Scalar[]);
149+
return db.executeRaw(query, params as Scalar[]);
185150
},
186151
executeSync: (query: string, params?: Scalar[]): QueryResult => {
187-
let res = params
188-
? db.executeSync(query, sanitizeArrayBuffersInArray(params) as Scalar[])
189-
: db.executeSync(query);
152+
let res = params ? db.executeSync(query, params) : db.executeSync(query);
190153

191154
if (!res.rows) {
192155
const rows: Record<string, Scalar>[] = [];
@@ -222,12 +185,7 @@ function enhanceDB(db: _InternalDB, options: DBParams): DB {
222185
query: string,
223186
params?: Scalar[] | undefined,
224187
): Promise<QueryResult> => {
225-
let res = params
226-
? await db.execute(
227-
query,
228-
sanitizeArrayBuffersInArray(params) as Scalar[],
229-
)
230-
: await db.execute(query);
188+
let res = params ? await db.execute(query, params) : await db.execute(query);
231189

232190
if (!res.rows) {
233191
const rows: Record<string, Scalar>[] = [];
@@ -258,14 +216,10 @@ function enhanceDB(db: _InternalDB, options: DBParams): DB {
258216

259217
return {
260218
bindSync: (params: Scalar[]) => {
261-
const sanitizedParams = sanitizeArrayBuffersInArray(params);
262-
263-
stmt.bindSync(sanitizedParams!);
219+
stmt.bindSync(params);
264220
},
265221
bind: async (params: Scalar[]) => {
266-
const sanitizedParams = sanitizeArrayBuffersInArray(params);
267-
268-
await stmt.bind(sanitizedParams!);
222+
await stmt.bind(params);
269223
},
270224
execute: stmt.execute,
271225
};

0 commit comments

Comments
 (0)