Skip to content

Commit 1fab926

Browse files
committed
feat: Correctly transform ArrayBuffer to write in DB
1 parent 409daf3 commit 1fab926

4 files changed

Lines changed: 106 additions & 41 deletions

File tree

src/sqlite.common.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ export interface SQLiteDatabase {
4242
export function isNothing(x: any) {
4343
return x === undefined || x === null;
4444
}
45+
export function arrayToNativeByteArray(val) {
46+
const length = val.length;
47+
const result = Array.create('byte', length);
48+
for (let i = 0; i < length; i++) {
49+
result[i] = val[i];
50+
}
51+
return result;
52+
}
4553

4654
export function paramToString(p: SqliteParam) {
4755
if (isNothing(p)) {
@@ -57,15 +65,33 @@ export function paramToString(p: SqliteParam) {
5765
if (p instanceof java.lang.Object) {
5866
return p;
5967
}
68+
if (p instanceof ArrayBuffer) {
69+
return arrayToNativeByteArray(new Uint8Array(p));
70+
} else if (p.buffer) {
71+
return arrayToNativeByteArray(p);
72+
} else if (Array.isArray(p)) {
73+
return arrayToNativeByteArray(p);
74+
}
75+
6076
} else {
6177
if (p instanceof NSData) {
6278
return p;
6379
}
6480
if (p instanceof NSObject) {
6581
return p;
6682
}
83+
if (p instanceof ArrayBuffer) {
84+
return NSData.dataWithData(p as any);
85+
}
86+
if (p.buffer) {
87+
return NSData.dataWithData(p.buffer);
88+
}
89+
if (Array.isArray(p)) {
90+
return NSData.dataWithData(new Uint8Array(p).buffer as any);
91+
}
6792
}
6893
if (p.hasOwnProperty('length') && !Array.isArray(p)) {
94+
// native array
6995
return p;
7096
}
7197

src/sqlite.ios.ts

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -120,27 +120,34 @@ const getNewCursorStatement = (
120120
// return cursorSt.count;
121121
// };
122122

123-
function getResultsAsObject (cursorSt: FMResultSet): SqliteRow {
123+
function getResultsAsObject (cursorSt: FMResultSet, transformBlobs?: boolean): SqliteRow {
124124
// const count = cursorSt.columnCount;
125125
// if (!count) {
126126
// return null;
127127
// }
128128
const data = {};
129129
const dict = cursorSt.resultDictionary;
130130
dict.enumerateKeysAndObjectsUsingBlock((key: any, value: any) => {
131-
data[key] = value;
131+
if (transformBlobs && value instanceof NSData) {
132+
133+
} else {
134+
data[key] = value;
135+
}
132136
});
133137
// for (let index = 0; index < count; index++) {
134138
// data[cursorSt.columnNameForIndex(index)] = getValues(cursorSt, index);
135139
// }
136140
return data;
137141
};
138142

139-
function getResultsAsArray (cursorSt: FMResultSet): SqliteParam[] {
143+
function getResultsAsArray (cursorSt: FMResultSet, transformBlobs?: boolean): SqliteParam[] {
140144
const data = [];
141145
const dict = cursorSt.resultDictionary;
142146
dict.enumerateKeysAndObjectsUsingBlock((key: any, value: any) => {
143-
data.push(value);
147+
if (transformBlobs && value instanceof NSData) {
148+
} else {
149+
data.push(value);
150+
}
144151
});
145152
// for (let index = 0; index < count; index++) {
146153
// data[cursorSt.columnNameForIndex(index)] = getValues(cursorSt, index);
@@ -256,7 +263,8 @@ function getRaw(
256263
db: FMDatabase,
257264
query: string,
258265
params: SqliteParams,
259-
asObject: boolean
266+
asObject: boolean,
267+
transformBlobs?: boolean
260268
): SqliteRow | SqliteParam[] {
261269
// const statement = prepareStatement(db, query);
262270
// const cursorSt = getNewCursorStatement(statement);
@@ -276,7 +284,7 @@ function getRaw(
276284
paramsToStringArray(params)
277285
);
278286
if (s) {
279-
return asObject ? getResultsAsObject(s) : getResultsAsArray(s);
287+
return asObject ? getResultsAsObject(s, transformBlobs) : getResultsAsArray(s, transformBlobs);
280288
// while (s.next()) {
281289
// //retrieve values for each record
282290
// const row = getResults(s);
@@ -295,7 +303,8 @@ function eachRaw(
295303
params: SqliteParams,
296304
asObject: boolean,
297305
callback: (error: Error, result: SqliteRow | SqliteParam[]) => void,
298-
complete: (error: Error, count: number) => void
306+
complete: (error: Error, count: number) => void,
307+
transformBlobs?: boolean
299308
) {
300309
// const statement = prepareStatement(db, query);
301310
// const cursorSt = getNewCursorStatement(statement);
@@ -313,7 +322,7 @@ function eachRaw(
313322
if (s) {
314323
while (s.next()) {
315324
//retrieve values for each record
316-
const row = getResults(s);
325+
const row = getResults(s, transformBlobs);
317326
if (row) {
318327
count++;
319328
callback(null, row);
@@ -356,7 +365,8 @@ function selectRaw(
356365
db: FMDatabase,
357366
query: string,
358367
params: SqliteParams,
359-
asObject: boolean
368+
asObject: boolean,
369+
transformBlobs?: boolean
360370
): SqliteRow[] | SqliteParam[][] {
361371
// const statement = prepareStatement(db, query);
362372
// const cursorSt = getNewCursorStatement(statement);
@@ -370,7 +380,7 @@ function selectRaw(
370380
if (s) {
371381
while (s.next()) {
372382
//retrieve values for each record
373-
const row = getResults(s);
383+
const row = getResults(s, transformBlobs);
374384
if (row) {
375385
rows = [...rows, row];
376386
}
@@ -446,10 +456,14 @@ async function transactionRaw<T = any>(
446456

447457
export class SQLiteDatabase {
448458
db: FMDatabase;
459+
transformBlobs: boolean;
449460
constructor(public filePath: string, options?: {
450461
threading?: boolean;
462+
transformBlobs?: boolean;
451463
readOnly?: boolean;
452-
}) {}
464+
}) {
465+
this.transformBlobs = !options || options.transformBlobs !== false;
466+
}
453467
isOpen = false;
454468
async open() {
455469
if (!this.db) {
@@ -481,32 +495,33 @@ export class SQLiteDatabase {
481495
async execute(query: string, params?: SqliteParams) {
482496
return execRaw(this.db, query, params);
483497
}
484-
async get(query: string, params?: SqliteParams) {
485-
return (getRaw(this.db, query, params, true) || null);
498+
async get(query: string, params?: SqliteParams, transformBlobs?: boolean) {
499+
return (getRaw(this.db, query, params, true, transformBlobs ?? this.transformBlobs) || null);
486500
}
487-
async getArray(query: string, params?: SqliteParams) {
488-
return (getRaw(this.db, query, params, false) || null) as SqliteParam[];
501+
async getArray(query: string, params?: SqliteParams, transformBlobs?: boolean) {
502+
return (getRaw(this.db, query, params, false, transformBlobs ?? this.transformBlobs) || null) as SqliteParam[];
489503
}
490-
async select(query: string, params?: SqliteParams) {
491-
return selectRaw(this.db, query, params, true) as SqliteRow[];
504+
async select(query: string, params?: SqliteParams, transformBlobs?: boolean) {
505+
return selectRaw(this.db, query, params, true, transformBlobs ?? this.transformBlobs) as SqliteRow[];
492506
}
493507
async each(
494508
query: string,
495509
params: SqliteParams,
496510
callback: (error: Error, result: SqliteRow) => void,
497-
complete: (error: Error, count: number) => void
511+
complete: (error: Error, count: number) => void,
512+
transformBlobs?: boolean
498513
) {
499514
return eachRaw(
500515
this.db,
501516
query,
502517
params,
503518
true,
504519
callback as (error: Error, result: any) => void,
505-
complete
520+
complete, transformBlobs ?? this.transformBlobs
506521
);
507522
}
508-
async selectArray(query: string, params?: SqliteParams) {
509-
return selectRaw(this.db, query, params, false) as SqliteParam[][];
523+
async selectArray(query: string, params?: SqliteParams, transformBlobs?: boolean) {
524+
return selectRaw(this.db, query, params, false, transformBlobs ?? this.transformBlobs) as SqliteParam[][];
510525
}
511526
_isInTransaction = false;
512527
async transaction<T = any>(action: (cancel?: () => void) => Promise<T>): Promise<T> {
@@ -526,6 +541,7 @@ export function openOrCreate(
526541
filePath: string,
527542
flags?: number, options?: {
528543
readOnly?: boolean;
544+
transformBlobs?: boolean;
529545
threading?: boolean;
530546
},
531547
): SQLiteDatabase {

src/sqlitedatabase.android.ts

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,22 @@ import { SqliteParam, SqliteParams, SqliteRow, paramsToStringArray, throwError }
33

44
type Db = android.database.sqlite.SQLiteDatabase;
55

6-
type FromCursor<T> = (cursor: android.database.Cursor) => T;
6+
type FromCursor<T> = (cursor: android.database.Cursor, transformBlobs?: boolean) => T;
77

8-
const dataFromCursor = (cursor: android.database.Cursor) => {
8+
export function byteArrayToBuffer(value) {
9+
if (!value) {
10+
return null;
11+
}
12+
const length = value.length;
13+
const ret = new Uint8Array(length);
14+
const isString = typeof value === 'string';
15+
for (let i = 0; i < length; i++) {
16+
ret[i] = isString ? value.charCodeAt(i) : value[i];
17+
}
18+
return ret;
19+
}
20+
21+
const dataFromCursor = (cursor: android.database.Cursor, transformBlobs?: boolean) => {
922
const colCount = cursor.getColumnCount();
1023
const data: SqliteRow = {};
1124
for (let i = 0; i < colCount; i++) {
@@ -25,7 +38,7 @@ const dataFromCursor = (cursor: android.database.Cursor) => {
2538
break;
2639

2740
case android.database.Cursor.FIELD_TYPE_BLOB:
28-
data[name] = cursor.getBlob(i) as any;
41+
data[name] = transformBlobs ? byteArrayToBuffer(cursor.getBlob(i)): cursor.getBlob(i);
2942
break;
3043

3144
case android.database.Cursor.FIELD_TYPE_NULL:
@@ -38,8 +51,7 @@ const dataFromCursor = (cursor: android.database.Cursor) => {
3851
}
3952
return data;
4053
};
41-
42-
const arrayFromCursor = (cursor: android.database.Cursor) => {
54+
const arrayFromCursor = (cursor: android.database.Cursor, transformBlobs?: boolean) => {
4355
const colCount = cursor.getColumnCount();
4456
const data: SqliteParam[] = [];
4557
for (let i = 0; i < colCount; i++) {
@@ -61,7 +73,7 @@ const arrayFromCursor = (cursor: android.database.Cursor) => {
6173
data.push(cursor.getDouble(i));
6274
break;
6375
case android.database.Cursor.FIELD_TYPE_BLOB:
64-
data.push(cursor.getBlob(i) as any);
76+
data.push(transformBlobs ? byteArrayToBuffer(cursor.getBlob(i)): cursor.getBlob(i));
6577
break;
6678

6779
case android.database.Cursor.FIELD_TYPE_NULL:
@@ -75,20 +87,20 @@ const arrayFromCursor = (cursor: android.database.Cursor) => {
7587
return data;
7688
};
7789

78-
const rawSql = <T>(onCursor: FromCursor<T>) => (db: Db) => (sql: string, params?: SqliteParams) => {
90+
const rawSql = <T>(onCursor: FromCursor<T>) => (db: Db, transformBlobs?: boolean) => (sql: string, params?: SqliteParams) => {
7991
const parameters = paramsToStringArray(params) as string[];
8092
const cursor = db.rawQuery(sql, parameters);
8193
try {
8294
const result: T[] = [];
8395
while (cursor.moveToNext()) {
84-
result.push(onCursor(cursor));
96+
result.push(onCursor(cursor, transformBlobs));
8597
}
8698
return result;
8799
} finally {
88100
cursor.close();
89101
}
90102
};
91-
const eachRaw = <T>(onCursor: FromCursor<T>) => (db: Db) => (
103+
const eachRaw = <T>(onCursor: FromCursor<T>) => (db: Db, transformBlobs?: boolean) => (
92104
sql: string,
93105
params: SqliteParams,
94106
callback: (error: Error, result: T) => void,
@@ -100,7 +112,7 @@ const eachRaw = <T>(onCursor: FromCursor<T>) => (db: Db) => (
100112
.then(() => {
101113
const count = 0;
102114
while (cursor.moveToNext()) {
103-
const result = onCursor(cursor);
115+
const result = onCursor(cursor, transformBlobs);
104116
callback(null, result);
105117
}
106118
cursor.close();
@@ -139,13 +151,16 @@ const messagePromises: { [key: string]: { resolve: Function; reject: Function; t
139151
export class SQLiteDatabaseBase {
140152
db: android.database.sqlite.SQLiteDatabase;
141153
flags;
154+
transformBlobs: boolean;
142155
constructor(public filePath: string, options?: {
143156
threading?: boolean;
144157
readOnly?: boolean;
145158
flags?: number;
159+
transformBlobs?: boolean;
146160
}) {
147161
this.threading = options && options.threading === true;
148162
this.flags = options?.flags;
163+
this.transformBlobs = !options || options.transformBlobs !== false;
149164
}
150165
_isInTransaction = false;
151166
threading = false;
@@ -211,6 +226,9 @@ export class SQLiteDatabaseBase {
211226
{
212227
type: 'call',
213228
id,
229+
dbOptions: {
230+
transformBlobs:this.transformBlobs
231+
},
214232
nativeDataKeys: keys,
215233
},
216234
messageData
@@ -254,7 +272,7 @@ export class SQLiteDatabaseBase {
254272
}
255273
return this.db.execSQL(query, paramsToStringArray(params));
256274
}
257-
async get(query: string, params?: SqliteParams) {
275+
async get(query: string, params?: SqliteParams, transformBlobs?: boolean) {
258276
if (this.threading) {
259277
return this.sendMessageToWorker(
260278
{
@@ -266,9 +284,9 @@ export class SQLiteDatabaseBase {
266284
}
267285
);
268286
}
269-
return rawSql(dataFromCursor)(this.db)(query, params)[0] || null;
287+
return rawSql(dataFromCursor)(this.db, transformBlobs ?? this.transformBlobs)(query, params)[0] || null;
270288
}
271-
async getArray(query: string, params?: SqliteParams) {
289+
async getArray(query: string, params?: SqliteParams, transformBlobs?: boolean) {
272290
if (this.threading) {
273291
return this.sendMessageToWorker(
274292
{
@@ -280,9 +298,9 @@ export class SQLiteDatabaseBase {
280298
}
281299
);
282300
}
283-
return rawSql(arrayFromCursor)(this.db)(query, params)[0] || null;
301+
return rawSql(arrayFromCursor)(this.db, transformBlobs ?? this.transformBlobs)(query, params)[0] || null;
284302
}
285-
async select(query: string, params?: SqliteParams) {
303+
async select(query: string, params?: SqliteParams, transformBlobs?: boolean) {
286304
if (this.threading) {
287305
return this.sendMessageToWorker(
288306
{
@@ -294,9 +312,9 @@ export class SQLiteDatabaseBase {
294312
}
295313
);
296314
}
297-
return rawSql(dataFromCursor)(this.db)(query, params);
315+
return rawSql(dataFromCursor)(this.db, transformBlobs ?? this.transformBlobs)(query, params);
298316
}
299-
async selectArray(query: string, params?: SqliteParams) {
317+
async selectArray(query: string, params?: SqliteParams, transformBlobs?: boolean) {
300318
if (this.threading) {
301319
return this.sendMessageToWorker(
302320
{
@@ -308,15 +326,16 @@ export class SQLiteDatabaseBase {
308326
}
309327
);
310328
}
311-
return rawSql(arrayFromCursor)(this.db)(query, params);
329+
return rawSql(arrayFromCursor)(this.db, transformBlobs ?? this.transformBlobs)(query, params);
312330
}
313331
async each(
314332
query: string,
315333
params: SqliteParams,
316334
callback: (error: Error, result: any) => void,
317-
complete: (error: Error, count: number) => void
335+
complete: (error: Error, count: number) => void,
336+
transformBlobs?: boolean
318337
) {
319-
return eachRaw(dataFromCursor)(this.db)(query, params, callback, complete);
338+
return eachRaw(dataFromCursor)(this.db, transformBlobs ?? this.transformBlobs)(query, params, callback, complete);
320339
}
321340
async transaction<T = any>(action: (cancel?: () => void) => Promise<T>): Promise<T> {
322341
return transactionRaw(this.db, action);

0 commit comments

Comments
 (0)