expo-sqlite 12.2.1 → 13.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/android/CMakeLists.txt +1 -1
- package/android/build.gradle +2 -2
- package/android/src/main/cpp/NativeDatabaseBinding.cpp +4 -4
- package/android/src/main/cpp/NativeDatabaseBinding.h +2 -2
- package/android/src/main/cpp/NativeStatementBinding.cpp +22 -15
- package/android/src/main/cpp/NativeStatementBinding.h +1 -0
- package/android/src/main/java/expo/modules/sqlite/NativeDatabase.kt +3 -1
- package/android/src/main/java/expo/modules/sqlite/NativeDatabaseBinding.kt +4 -4
- package/android/src/main/java/expo/modules/sqlite/NativeStatement.kt +2 -0
- package/android/src/main/java/expo/modules/sqlite/NativeStatementBinding.kt +5 -4
- package/android/src/main/java/expo/modules/sqlite/SQLExceptions.kt +6 -0
- package/android/src/main/java/expo/modules/sqlite/SQLiteModule.kt +21 -21
- package/android/src/main/java/expo/modules/sqlite/SQLiteModuleNext.kt +122 -132
- package/build/next/ExpoSQLiteNext.d.ts +4 -4
- package/build/next/ExpoSQLiteNext.d.ts.map +1 -1
- package/build/next/ExpoSQLiteNext.js +3 -3
- package/build/next/ExpoSQLiteNext.js.map +1 -1
- package/build/next/NativeDatabase.d.ts +3 -3
- package/build/next/NativeDatabase.d.ts.map +1 -1
- package/build/next/NativeDatabase.js.map +1 -1
- package/build/next/NativeStatement.d.ts +37 -35
- package/build/next/NativeStatement.d.ts.map +1 -1
- package/build/next/NativeStatement.js.map +1 -1
- package/build/next/SQLiteDatabase.d.ts +266 -0
- package/build/next/SQLiteDatabase.d.ts.map +1 -0
- package/build/next/{Database.js → SQLiteDatabase.js} +69 -59
- package/build/next/SQLiteDatabase.js.map +1 -0
- package/build/next/SQLiteStatement.d.ts +190 -0
- package/build/next/SQLiteStatement.d.ts.map +1 -0
- package/build/next/SQLiteStatement.js +275 -0
- package/build/next/SQLiteStatement.js.map +1 -0
- package/build/next/hooks.d.ts +26 -14
- package/build/next/hooks.d.ts.map +1 -1
- package/build/next/hooks.js +121 -33
- package/build/next/hooks.js.map +1 -1
- package/build/next/index.d.ts +2 -2
- package/build/next/index.d.ts.map +1 -1
- package/build/next/index.js +2 -2
- package/build/next/index.js.map +1 -1
- package/build/next/paramUtils.d.ts +18 -0
- package/build/next/paramUtils.d.ts.map +1 -0
- package/build/next/paramUtils.js +72 -0
- package/build/next/paramUtils.js.map +1 -0
- package/ios/Exceptions.swift +12 -0
- package/ios/NativeDatabase.swift +4 -3
- package/ios/NativeStatement.swift +1 -0
- package/ios/SQLiteModule.swift +20 -21
- package/ios/SQLiteModuleNext.swift +126 -131
- package/ios/crsqlite.xcframework/Info.plist +4 -0
- package/ios/crsqlite.xcframework/ios-arm64/crsqlite.framework/Info.plist +4 -0
- package/package.json +4 -3
- package/src/next/ExpoSQLiteNext.ts +4 -4
- package/src/next/NativeDatabase.ts +3 -3
- package/src/next/NativeStatement.ts +43 -48
- package/src/next/{Database.ts → SQLiteDatabase.ts} +134 -112
- package/src/next/SQLiteStatement.ts +528 -0
- package/src/next/hooks.tsx +202 -51
- package/src/next/index.ts +2 -2
- package/src/next/paramUtils.ts +94 -0
- package/build/next/Database.d.ts +0 -272
- package/build/next/Database.d.ts.map +0 -1
- package/build/next/Database.js.map +0 -1
- package/build/next/Statement.d.ts +0 -142
- package/build/next/Statement.d.ts.map +0 -1
- package/build/next/Statement.js +0 -184
- package/build/next/Statement.js.map +0 -1
- package/src/next/Statement.ts +0 -310
package/src/next/hooks.tsx
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import type
|
|
3
|
+
import type { SQLiteOpenOptions } from './NativeDatabase';
|
|
4
|
+
import { openDatabaseAsync, type SQLiteDatabase } from './SQLiteDatabase';
|
|
5
5
|
|
|
6
6
|
export interface SQLiteProviderProps {
|
|
7
7
|
/**
|
|
8
8
|
* The name of the database file to open.
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
databaseName: string;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Open options.
|
|
14
14
|
*/
|
|
15
|
-
options?:
|
|
15
|
+
options?: SQLiteOpenOptions;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* The children to render.
|
|
@@ -23,49 +23,130 @@ export interface SQLiteProviderProps {
|
|
|
23
23
|
* A custom initialization handler to run before rendering the children.
|
|
24
24
|
* You can use this to run database migrations or other setup tasks.
|
|
25
25
|
*/
|
|
26
|
-
|
|
26
|
+
onInit?: (db: SQLiteDatabase) => Promise<void>;
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
30
|
-
* @default
|
|
29
|
+
* Handle errors from SQLiteProvider.
|
|
30
|
+
* @default rethrow the error
|
|
31
31
|
*/
|
|
32
|
-
|
|
32
|
+
onError?: (error: Error) => void;
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
* @default
|
|
35
|
+
* Enable [`React.Suspense`](https://react.dev/reference/react/Suspense) integration.
|
|
36
|
+
* @default false
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* export default function App() {
|
|
40
|
+
* return (
|
|
41
|
+
* <Suspense fallback={<Text>Loading...</Text>}>
|
|
42
|
+
* <SQLiteProvider databaseName="test.db" useSuspense={true}>
|
|
43
|
+
* <Main />
|
|
44
|
+
* </SQLiteProvider>
|
|
45
|
+
* </Suspense>
|
|
46
|
+
* );
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
37
49
|
*/
|
|
38
|
-
|
|
50
|
+
useSuspense?: boolean;
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
/**
|
|
42
54
|
* Create a context for the SQLite database
|
|
43
55
|
*/
|
|
44
|
-
const SQLiteContext = createContext<
|
|
56
|
+
const SQLiteContext = createContext<SQLiteDatabase | null>(null);
|
|
45
57
|
|
|
46
58
|
/**
|
|
47
59
|
* Context.Provider component that provides a SQLite database to all children.
|
|
48
60
|
* All descendants of this component will be able to access the database using the [`useSQLiteContext`](#usesqlitecontext) hook.
|
|
49
61
|
*/
|
|
50
62
|
export function SQLiteProvider({
|
|
51
|
-
dbName,
|
|
52
|
-
options,
|
|
53
63
|
children,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
onError,
|
|
65
|
+
useSuspense = false,
|
|
66
|
+
...props
|
|
57
67
|
}: SQLiteProviderProps) {
|
|
58
|
-
|
|
68
|
+
if (onError != null && useSuspense) {
|
|
69
|
+
throw new Error('Cannot use `onError` with `useSuspense`, use error boundaries instead.');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (useSuspense) {
|
|
73
|
+
return <SQLiteProviderSuspense {...props}>{children}</SQLiteProviderSuspense>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<SQLiteProviderNonSuspense {...props} onError={onError}>
|
|
78
|
+
{children}
|
|
79
|
+
</SQLiteProviderNonSuspense>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* A global hook for accessing the SQLite database across components.
|
|
85
|
+
* This hook should only be used within a [`<SQLiteProvider>`](#sqliteprovider) component.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```tsx
|
|
89
|
+
* export default function App() {
|
|
90
|
+
* return (
|
|
91
|
+
* <SQLiteProvider databaseName="test.db">
|
|
92
|
+
* <Main />
|
|
93
|
+
* </SQLiteProvider>
|
|
94
|
+
* );
|
|
95
|
+
* }
|
|
96
|
+
*
|
|
97
|
+
* export function Main() {
|
|
98
|
+
* const db = useSQLiteContext();
|
|
99
|
+
* console.log('sqlite version', db.getSync('SELECT sqlite_version()'));
|
|
100
|
+
* return <View />
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export function useSQLiteContext(): SQLiteDatabase {
|
|
105
|
+
const context = useContext(SQLiteContext);
|
|
106
|
+
if (context == null) {
|
|
107
|
+
throw new Error('useSQLiteContext must be used within a <SQLiteProvider>');
|
|
108
|
+
}
|
|
109
|
+
return context;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
//#region Internals
|
|
113
|
+
|
|
114
|
+
type DatabaseInstanceType = Pick<SQLiteProviderProps, 'databaseName' | 'options' | 'onInit'> & {
|
|
115
|
+
promise: Promise<SQLiteDatabase> | null;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
let databaseInstance: DatabaseInstanceType | null = null;
|
|
119
|
+
|
|
120
|
+
function SQLiteProviderSuspense({
|
|
121
|
+
databaseName,
|
|
122
|
+
options,
|
|
123
|
+
children,
|
|
124
|
+
onInit,
|
|
125
|
+
}: Omit<SQLiteProviderProps, 'onError' | 'useSuspense'>) {
|
|
126
|
+
const databasePromise = getDatabaseAsync({
|
|
127
|
+
databaseName,
|
|
128
|
+
options,
|
|
129
|
+
onInit,
|
|
130
|
+
});
|
|
131
|
+
const database = use(databasePromise);
|
|
132
|
+
return <SQLiteContext.Provider value={database}>{children}</SQLiteContext.Provider>;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function SQLiteProviderNonSuspense({
|
|
136
|
+
databaseName,
|
|
137
|
+
options,
|
|
138
|
+
children,
|
|
139
|
+
onInit,
|
|
140
|
+
onError,
|
|
141
|
+
}: Omit<SQLiteProviderProps, 'useSuspense'>) {
|
|
142
|
+
const databaseRef = useRef<SQLiteDatabase | null>(null);
|
|
59
143
|
const [loading, setLoading] = useState(true);
|
|
60
144
|
const [error, setError] = useState<Error | null>(null);
|
|
61
145
|
|
|
62
146
|
useEffect(() => {
|
|
63
147
|
async function setup() {
|
|
64
148
|
try {
|
|
65
|
-
const db = await
|
|
66
|
-
if (initHandler != null) {
|
|
67
|
-
await initHandler(db);
|
|
68
|
-
}
|
|
149
|
+
const db = await openDatabaseWithInitAsync({ databaseName, options, onInit });
|
|
69
150
|
databaseRef.current = db;
|
|
70
151
|
setLoading(false);
|
|
71
152
|
} catch (e) {
|
|
@@ -73,7 +154,7 @@ export function SQLiteProvider({
|
|
|
73
154
|
}
|
|
74
155
|
}
|
|
75
156
|
|
|
76
|
-
async function teardown(db:
|
|
157
|
+
async function teardown(db: SQLiteDatabase | null) {
|
|
77
158
|
try {
|
|
78
159
|
await db?.closeAsync();
|
|
79
160
|
} catch (e) {
|
|
@@ -89,48 +170,118 @@ export function SQLiteProvider({
|
|
|
89
170
|
databaseRef.current = null;
|
|
90
171
|
setLoading(true);
|
|
91
172
|
};
|
|
92
|
-
}, [
|
|
173
|
+
}, [databaseName, options, onInit]);
|
|
93
174
|
|
|
94
175
|
if (error != null) {
|
|
95
176
|
const handler =
|
|
96
|
-
|
|
177
|
+
onError ??
|
|
97
178
|
((e) => {
|
|
98
179
|
throw e;
|
|
99
180
|
});
|
|
100
181
|
handler(error);
|
|
101
182
|
}
|
|
102
|
-
|
|
103
183
|
if (loading || !databaseRef.current) {
|
|
104
|
-
return
|
|
184
|
+
return null;
|
|
105
185
|
}
|
|
106
186
|
return <SQLiteContext.Provider value={databaseRef.current}>{children}</SQLiteContext.Provider>;
|
|
107
187
|
}
|
|
108
188
|
|
|
189
|
+
function getDatabaseAsync({
|
|
190
|
+
databaseName,
|
|
191
|
+
options,
|
|
192
|
+
onInit,
|
|
193
|
+
}: Pick<SQLiteProviderProps, 'databaseName' | 'options' | 'onInit'>): Promise<SQLiteDatabase> {
|
|
194
|
+
if (
|
|
195
|
+
databaseInstance?.promise != null &&
|
|
196
|
+
databaseInstance?.databaseName === databaseName &&
|
|
197
|
+
databaseInstance?.options === options &&
|
|
198
|
+
databaseInstance?.onInit === onInit
|
|
199
|
+
) {
|
|
200
|
+
return databaseInstance.promise;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let promise: Promise<SQLiteDatabase>;
|
|
204
|
+
if (databaseInstance?.promise != null) {
|
|
205
|
+
promise = databaseInstance.promise
|
|
206
|
+
.then((db) => {
|
|
207
|
+
db.closeAsync();
|
|
208
|
+
})
|
|
209
|
+
.then(() => {
|
|
210
|
+
return openDatabaseWithInitAsync({ databaseName, options, onInit });
|
|
211
|
+
});
|
|
212
|
+
} else {
|
|
213
|
+
promise = openDatabaseWithInitAsync({ databaseName, options, onInit });
|
|
214
|
+
}
|
|
215
|
+
databaseInstance = {
|
|
216
|
+
databaseName,
|
|
217
|
+
options,
|
|
218
|
+
onInit,
|
|
219
|
+
promise,
|
|
220
|
+
};
|
|
221
|
+
return promise;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function openDatabaseWithInitAsync({
|
|
225
|
+
databaseName,
|
|
226
|
+
options,
|
|
227
|
+
onInit,
|
|
228
|
+
}: Pick<SQLiteProviderProps, 'databaseName' | 'options' | 'onInit'>): Promise<SQLiteDatabase> {
|
|
229
|
+
const database = await openDatabaseAsync(databaseName, options);
|
|
230
|
+
if (onInit != null) {
|
|
231
|
+
await onInit(database);
|
|
232
|
+
}
|
|
233
|
+
return database;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
//#endregion
|
|
237
|
+
|
|
238
|
+
//#region Private Suspense API similar to `React.use`
|
|
239
|
+
|
|
240
|
+
// Referenced from https://github.com/vercel/swr/blob/1d8110900d1aee3747199bfb377b149b7ff6848e/_internal/src/types.ts#L27-L31
|
|
241
|
+
type ReactUsePromise<T, E extends Error = Error> = Promise<T> & {
|
|
242
|
+
status?: 'pending' | 'fulfilled' | 'rejected';
|
|
243
|
+
value?: T;
|
|
244
|
+
reason?: E;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// Referenced from https://github.com/reactjs/react.dev/blob/6570e6cd79a16ac3b1a2902632eddab7e6abb9ad/src/content/reference/react/Suspense.md
|
|
109
248
|
/**
|
|
110
|
-
* A
|
|
111
|
-
* This hook should only be used within a [`<SQLiteProvider>`](#sqliteprovider) component.
|
|
112
|
-
*
|
|
113
|
-
* @example
|
|
114
|
-
* ```tsx
|
|
115
|
-
* export default function App() {
|
|
116
|
-
* return (
|
|
117
|
-
* <SQLiteProvider dbName="test.db">
|
|
118
|
-
* <Main />
|
|
119
|
-
* </SQLiteProvider>
|
|
120
|
-
* );
|
|
121
|
-
* }
|
|
122
|
-
*
|
|
123
|
-
* export function Main() {
|
|
124
|
-
* const db = useSQLiteContext();
|
|
125
|
-
* console.log('sqlite version', db.getSync('SELECT sqlite_version()'));
|
|
126
|
-
* return <View />
|
|
127
|
-
* }
|
|
128
|
-
* ```
|
|
249
|
+
* A custom hook like [`React.use`](https://react.dev/reference/react/use) hook using private Suspense implementation.
|
|
129
250
|
*/
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
251
|
+
function use<T>(promise: Promise<T> | ReactUsePromise<T>) {
|
|
252
|
+
if (isReactUsePromise(promise)) {
|
|
253
|
+
if (promise.status === 'fulfilled') {
|
|
254
|
+
if (promise.value === undefined) {
|
|
255
|
+
throw new Error('[use] Unexpected undefined value from promise');
|
|
256
|
+
}
|
|
257
|
+
return promise.value;
|
|
258
|
+
} else if (promise.status === 'rejected') {
|
|
259
|
+
throw promise.reason;
|
|
260
|
+
} else if (promise.status === 'pending') {
|
|
261
|
+
throw promise;
|
|
262
|
+
}
|
|
263
|
+
throw new Error('[use] Promise is in an invalid state');
|
|
134
264
|
}
|
|
135
|
-
|
|
265
|
+
|
|
266
|
+
const suspensePromise = promise as ReactUsePromise<T>;
|
|
267
|
+
suspensePromise.status = 'pending';
|
|
268
|
+
suspensePromise.then(
|
|
269
|
+
(result: T) => {
|
|
270
|
+
suspensePromise.status = 'fulfilled';
|
|
271
|
+
suspensePromise.value = result;
|
|
272
|
+
},
|
|
273
|
+
(reason) => {
|
|
274
|
+
suspensePromise.status = 'rejected';
|
|
275
|
+
suspensePromise.reason = reason;
|
|
276
|
+
}
|
|
277
|
+
);
|
|
278
|
+
throw suspensePromise;
|
|
136
279
|
}
|
|
280
|
+
|
|
281
|
+
function isReactUsePromise<T>(
|
|
282
|
+
promise: Promise<T> | ReactUsePromise<T>
|
|
283
|
+
): promise is ReactUsePromise<T> {
|
|
284
|
+
return typeof promise === 'object' && promise !== null && 'status' in promise;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
//#endregion
|
package/src/next/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
1
|
+
export * from './SQLiteDatabase';
|
|
2
|
+
export * from './SQLiteStatement';
|
|
3
3
|
export * from './hooks';
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SQLiteBindBlobParams,
|
|
3
|
+
SQLiteBindParams,
|
|
4
|
+
SQLiteBindPrimitiveParams,
|
|
5
|
+
SQLiteBindValue,
|
|
6
|
+
type SQLiteColumnNames,
|
|
7
|
+
type SQLiteColumnValues,
|
|
8
|
+
} from './NativeStatement';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Normalize the bind params to data structure that can be passed to native module.
|
|
12
|
+
* The data structure is a tuple of [primitiveParams, blobParams, shouldPassAsArray].
|
|
13
|
+
* @hidden
|
|
14
|
+
*/
|
|
15
|
+
export function normalizeParams(
|
|
16
|
+
...params: any[]
|
|
17
|
+
): [SQLiteBindPrimitiveParams, SQLiteBindBlobParams, boolean] {
|
|
18
|
+
let bindParams = params.length > 1 ? params : (params[0] as SQLiteBindParams);
|
|
19
|
+
if (bindParams == null) {
|
|
20
|
+
bindParams = [];
|
|
21
|
+
}
|
|
22
|
+
if (
|
|
23
|
+
typeof bindParams !== 'object' ||
|
|
24
|
+
bindParams instanceof ArrayBuffer ||
|
|
25
|
+
ArrayBuffer.isView(bindParams)
|
|
26
|
+
) {
|
|
27
|
+
bindParams = [bindParams];
|
|
28
|
+
}
|
|
29
|
+
const shouldPassAsArray = Array.isArray(bindParams);
|
|
30
|
+
if (Array.isArray(bindParams)) {
|
|
31
|
+
bindParams = bindParams.reduce<Record<string, SQLiteBindValue>>((acc, value, index) => {
|
|
32
|
+
acc[index] = value;
|
|
33
|
+
return acc;
|
|
34
|
+
}, {});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const primitiveParams: SQLiteBindPrimitiveParams = {};
|
|
38
|
+
const blobParams: SQLiteBindBlobParams = {};
|
|
39
|
+
for (const key in bindParams) {
|
|
40
|
+
const value = bindParams[key];
|
|
41
|
+
if (value instanceof Uint8Array) {
|
|
42
|
+
blobParams[key] = value;
|
|
43
|
+
} else {
|
|
44
|
+
primitiveParams[key] = value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return [primitiveParams, blobParams, shouldPassAsArray];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Compose `columnNames` and `columnValues` to an row object.
|
|
53
|
+
* @hidden
|
|
54
|
+
*/
|
|
55
|
+
export function composeRow<T>(columnNames: SQLiteColumnNames, columnValues: SQLiteColumnValues): T {
|
|
56
|
+
const row = {};
|
|
57
|
+
if (columnNames.length !== columnValues.length) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`Column names and values count mismatch. Names: ${columnNames.length}, Values: ${columnValues.length}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
for (let i = 0; i < columnNames.length; i++) {
|
|
63
|
+
row[columnNames[i]] = columnValues[i];
|
|
64
|
+
}
|
|
65
|
+
return row as T;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Compose `columnNames` and `columnValuesList` to an array of row objects.
|
|
70
|
+
* @hidden
|
|
71
|
+
*/
|
|
72
|
+
export function composeRows<T>(
|
|
73
|
+
columnNames: SQLiteColumnNames,
|
|
74
|
+
columnValuesList: SQLiteColumnValues[]
|
|
75
|
+
): T[] {
|
|
76
|
+
if (columnValuesList.length === 0) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
if (columnNames.length !== columnValuesList[0].length) {
|
|
80
|
+
// We only check the first row because SQLite returns the same column count for all rows.
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Column names and values count mismatch. Names: ${columnNames.length}, Values: ${columnValuesList[0].length}`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
const results: T[] = [];
|
|
86
|
+
for (const columnValues of columnValuesList) {
|
|
87
|
+
const row = {};
|
|
88
|
+
for (let i = 0; i < columnNames.length; i++) {
|
|
89
|
+
row[columnNames[i]] = columnValues[i];
|
|
90
|
+
}
|
|
91
|
+
results.push(row as T);
|
|
92
|
+
}
|
|
93
|
+
return results;
|
|
94
|
+
}
|