Repository navigation
Expand file tree
/
Copy pathexecute.ts
More file actions
142 lines (131 loc) · 4.43 KB
/
Copy pathexecute.ts
File metadata and controls
142 lines (131 loc) · 4.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import { HybridNitroSQLite } from '../nitro'
import type { QueryResult, QueryResultRow, SQLiteQueryParams } from '../types'
import NitroSQLiteError from '../NitroSQLiteError'
import type { NitroSQLiteQueryResult } from '../specs/NitroSQLiteQueryResult.nitro'
import {
isDatabaseOpen,
queueStatementAsync,
startOperationSync,
} from '../DatabaseQueue'
import type { DatabaseQueueKey } from '../DatabaseQueue'
/** Execute one SQL statement synchronously by database name.
* Uses the managed queue when the database has an open managed connection.
* @param dbName Name of an open native database.
* @param query SQL statement with optional positional placeholders.
* @param params Values bound to the placeholders.
* @returns The query result with typed rows.
*/
export function execute<Row extends QueryResultRow = never>(
dbName: string,
query: string,
params?: SQLiteQueryParams,
): QueryResult<Row> {
if (!isDatabaseOpen(dbName)) {
return executeNative(dbName, query, params)
}
return executeManaged(dbName, query, params)
}
export function executeManaged<Row extends QueryResultRow = never>(
dbName: string,
query: string,
params?: SQLiteQueryParams,
queueKey: DatabaseQueueKey = dbName,
): QueryResult<Row> {
return startOperationSync(queueKey, () =>
executeNative(dbName, query, params),
)
}
export function executeNative<Row extends QueryResultRow = never>(
dbName: string,
query: string,
params?: SQLiteQueryParams,
): QueryResult<Row> {
try {
const nativeResult = HybridNitroSQLite.execute(dbName, query, params)
return buildJSQueryResult<Row>(nativeResult)
} catch (error) {
throw NitroSQLiteError.fromError(error)
}
}
/** Execute one SQL statement asynchronously by database name.
* Uses the managed queue when the database has an open managed connection.
* @param dbName Name of an open native database.
* @param query SQL statement with optional positional placeholders.
* @param params Values bound to the placeholders.
* @returns A promise of the complete query result after all row batches arrive.
*/
export async function executeAsync<Row extends QueryResultRow = never>(
dbName: string,
query: string,
params?: SQLiteQueryParams,
): Promise<QueryResult<Row>> {
if (!isDatabaseOpen(dbName)) {
return executeAsyncNative(dbName, query, params)
}
return executeAsyncManaged(dbName, query, params)
}
export async function executeAsyncManaged<Row extends QueryResultRow = never>(
dbName: string,
query: string,
params?: SQLiteQueryParams,
queueKey: DatabaseQueueKey = dbName,
): Promise<QueryResult<Row>> {
return queueStatementAsync(queueKey, () =>
executeAsyncNative(dbName, query, params),
)
}
export async function executeAsyncNative<Row extends QueryResultRow = never>(
dbName: string,
query: string,
params?: SQLiteQueryParams,
): Promise<QueryResult<Row>> {
try {
// Large results arrive in batches, so rows are converted while SQLite reads the rest.
let leadingRows: Row[] | undefined
let receivedBatchCount = 0
let onBatchReceived: (() => void) | undefined
const nativeResult = await HybridNitroSQLite.executeAsync(
dbName,
query,
params,
(rows) => {
if (leadingRows === undefined) {
leadingRows = rows as Row[]
} else {
Array.prototype.push.apply(leadingRows, rows as Row[])
}
receivedBatchCount++
onBatchReceived?.()
},
)
// Native computation can finish before its queued JavaScript callbacks run.
const expectedBatchCount = nativeResult.rowBatchCount ?? 0
if (receivedBatchCount < expectedBatchCount) {
await new Promise<void>((resolve) => {
onBatchReceived = () => {
if (receivedBatchCount >= expectedBatchCount) resolve()
}
})
}
return buildJSQueryResult<Row>(nativeResult, leadingRows)
} catch (error) {
throw NitroSQLiteError.fromError(error)
}
}
export function buildJSQueryResult<Row extends QueryResultRow = never>(
result: NitroSQLiteQueryResult,
leadingRows?: Row[],
): QueryResult<Row> {
const resultWithRows = result as QueryResult<Row> & { results: Row[] }
if (leadingRows !== undefined) {
Array.prototype.push.apply(leadingRows, result.results as Row[])
resultWithRows.results = leadingRows
}
const results = resultWithRows.results
resultWithRows.rows = {
_array: results,
length: results.length,
item: (idx: number) => results[idx],
}
return resultWithRows
}