Skip to content

Commit f5fc07a

Browse files
committed
Refactor driver api.
1 parent 6672b66 commit f5fc07a

File tree

6 files changed

+217
-265
lines changed

6 files changed

+217
-265
lines changed

packages/driver/src/driver-api.ts

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,11 @@ export type SqliteArguments =
55
| null
66
| undefined;
77

8-
export type SqliteRowRaw = SqliteValue[];
9-
export type SqliteRowObject = Record<string, SqliteValue>;
10-
export type SqliteRow = SqliteRowRaw | SqliteRowObject;
8+
export type SqliteArrayRow = SqliteValue[];
9+
export type SqliteObjectRow = Record<string, SqliteValue>;
1110

1211
export interface PrepareOptions {
13-
bigint?: boolean;
14-
rawResults?: boolean;
15-
persist?: boolean;
12+
autoFinalize?: boolean;
1613
}
1714

1815
export interface ResetOptions {
@@ -32,42 +29,56 @@ export interface SqliteDriverConnection {
3229
options?: { tables?: string[]; batchLimit?: number }
3330
): () => void;
3431

35-
getLastChanges(): Promise<SqliteChanges>;
36-
3732
close(): Promise<void>;
3833
}
3934

4035
export type SqliteParameterBinding =
41-
| (SqliteValue | undefined)[]
36+
| SqliteValue[]
4237
| Record<string, SqliteValue>
4338
| null
4439
| undefined;
4540

46-
export interface SqliteStepResult {
47-
rows?: SqliteRow[];
48-
done?: boolean;
41+
export interface QueryOptions {
42+
requireTransaction?: boolean;
43+
bigint?: boolean;
4944
}
5045

51-
export interface SqliteDriverStatement {
52-
getColumns(): Promise<string[]>;
46+
export interface StreamQueryOptions extends QueryOptions {
47+
chunkMaxRows?: number;
48+
chunkMaxSize?: number;
49+
}
5350

54-
bind(parameters: SqliteParameterBinding): void;
55-
step(n?: number, options?: StepOptions): Promise<SqliteStepResult>;
56-
finalize(): void;
57-
reset(options?: ResetOptions): void;
51+
export interface SqliteDriverStatement {
52+
all(
53+
parameters?: SqliteParameterBinding,
54+
options?: QueryOptions
55+
): Promise<SqliteObjectRow[]>;
56+
allArray(
57+
parameters: SqliteParameterBinding,
58+
options?: QueryOptions
59+
): Promise<SqliteArrayRow[]>;
60+
61+
stream(
62+
parameters?: SqliteParameterBinding,
63+
options?: StreamQueryOptions
64+
): AsyncIterator<SqliteObjectRow[]>;
65+
streamArray(
66+
parameters?: SqliteParameterBinding,
67+
options?: StreamQueryOptions
68+
): AsyncIterator<SqliteArrayRow[]>;
5869

5970
/**
60-
* Similar to step, followed by reset, and returning number of changed rows.
61-
*
62-
* Avoids the need to use a separate statement to get changes.
71+
* Run a query, and return the number of changed rows, and last insert id.
6372
*/
64-
run(options?: StepOptions): Promise<SqliteChanges>;
73+
run(
74+
parameters?: SqliteParameterBinding,
75+
options?: QueryOptions
76+
): Promise<SqliteChanges>;
6577

66-
[Symbol.dispose](): void;
67-
}
78+
getColumns(): Promise<string[]>;
6879

69-
export interface StepOptions {
70-
requireTransaction?: boolean;
80+
finalize(): void;
81+
[Symbol.dispose](): void;
7182
}
7283

7384
export interface SqliteDriverConnectionPool {

packages/driver/src/node/impl.ts

Lines changed: 54 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@ import type * as sqlite from './node-sqlite.js';
22

33
import {
44
PrepareOptions,
5-
ResetOptions,
5+
QueryOptions,
6+
SqliteArrayRow,
67
SqliteChanges,
78
SqliteDriverConnection,
89
SqliteDriverConnectionPool,
910
SqliteDriverStatement,
11+
SqliteObjectRow,
1012
SqliteParameterBinding,
11-
SqliteRow,
12-
SqliteStepResult,
13-
SqliteValue,
14-
StepOptions,
13+
StreamQueryOptions,
1514
UpdateListener
1615
} from '../driver-api.js';
1716

@@ -43,20 +42,14 @@ interface InternalStatement extends SqliteDriverStatement {
4342

4443
class NodeSqliteSyncStatement implements InternalStatement {
4544
public statement: sqlite.StatementSync;
46-
private options: PrepareOptions;
47-
private bindPositional: SqliteValue[] = [];
48-
private bindNamed: Record<string, SqliteValue> = {};
49-
private statementDone = false;
50-
private iterator: Iterator<unknown> | undefined = undefined;
5145

5246
readonly persisted: boolean;
5347

5448
[Symbol.dispose]: () => void = undefined as any;
5549

5650
constructor(statement: sqlite.StatementSync, options: PrepareOptions) {
5751
this.statement = statement;
58-
this.options = options;
59-
this.persisted = options.persist ?? false;
52+
this.persisted = options.autoFinalize ?? false;
6053

6154
if (typeof Symbol.dispose != 'undefined') {
6255
this[Symbol.dispose] = () => this.finalize();
@@ -72,120 +65,69 @@ class NodeSqliteSyncStatement implements InternalStatement {
7265
return [];
7366
}
7467

75-
bind(parameters: SqliteParameterBinding): void {
76-
if (parameters == null) {
77-
return;
78-
}
79-
if (Array.isArray(parameters)) {
80-
let bindArray = this.bindPositional;
81-
82-
for (let i = 0; i < parameters.length; i++) {
83-
if (typeof parameters[i] != 'undefined') {
84-
bindArray[i] = parameters[i]!;
85-
}
86-
}
87-
} else {
88-
let previous = this.bindNamed;
89-
this.bindNamed = { ...previous, ...parameters };
90-
}
91-
}
92-
93-
async run(options?: StepOptions): Promise<SqliteChanges> {
68+
async run(
69+
parameters: SqliteParameterBinding,
70+
options?: QueryOptions
71+
): Promise<SqliteChanges> {
9472
try {
9573
if (options?.requireTransaction) {
9674
// TODO: Implement
9775
}
9876

9977
const statement = this.statement;
100-
this.reset();
101-
102-
try {
103-
const bindNamed = this.bindNamed;
104-
const bindPositional = this.bindPositional;
105-
106-
statement.setReadBigInts(true);
107-
const r = statement.run(bindNamed, ...bindPositional);
108-
return {
109-
changes: Number(r.changes),
110-
lastInsertRowId: r.lastInsertRowid as bigint
111-
};
112-
} finally {
113-
this.reset();
114-
}
78+
statement.setReadBigInts(true);
79+
const r = statement.run(...convertParameters(parameters));
80+
return {
81+
changes: Number(r.changes),
82+
lastInsertRowId: r.lastInsertRowid as bigint
83+
};
11584
} catch (e) {
11685
throw mapError(e);
11786
}
11887
}
11988

120-
async step(n?: number, options?: StepOptions): Promise<SqliteStepResult> {
89+
async all(
90+
parameters: SqliteParameterBinding,
91+
options?: QueryOptions
92+
): Promise<SqliteObjectRow[]> {
12193
try {
122-
const all = n == null;
123-
124-
const statement = this.statement;
125-
if (this.statementDone) {
126-
return { done: true };
127-
}
128-
12994
if (options?.requireTransaction) {
130-
// TODO: implement
95+
// TODO: Implement
13196
}
13297

133-
const bindNamed = this.bindNamed;
134-
const bindPositional = this.bindPositional;
135-
136-
let iterator = this.iterator;
137-
const num_rows = n ?? 1;
138-
if (iterator == null) {
139-
if (this.options.rawResults) {
140-
// Not supported
141-
}
142-
if (this.options.bigint) {
143-
statement.setReadBigInts(true);
144-
}
145-
iterator = statement
146-
.all(bindNamed, ...bindPositional)
147-
[Symbol.iterator]();
148-
this.iterator = iterator;
149-
}
150-
let rows: SqliteRow[] = [];
151-
let isDone = false;
152-
for (let i = 0; i < num_rows || all; i++) {
153-
const { value, done } = iterator.next();
154-
if (done) {
155-
isDone = true;
156-
break;
157-
}
158-
rows.push(value as SqliteRow);
159-
}
160-
if (isDone) {
161-
this.statementDone = true;
162-
}
163-
return { rows, done: isDone };
98+
const statement = this.statement;
99+
statement.setReadBigInts(options?.bigint ?? false);
100+
const rows = statement.all(...convertParameters(parameters));
101+
return rows;
164102
} catch (e) {
165103
throw mapError(e);
166104
}
167105
}
168106

169-
finalize(): void {
170-
const existingIter = this.iterator;
171-
if (existingIter != null) {
172-
existingIter.return?.();
173-
}
174-
this.iterator = undefined;
175-
this.statementDone = false;
107+
allArray(
108+
parameters: SqliteParameterBinding,
109+
options: QueryOptions
110+
): Promise<SqliteArrayRow[]> {
111+
throw new Error('array rows are not supported');
176112
}
177113

178-
reset(options?: ResetOptions): void {
179-
if (this.iterator) {
180-
const iter = this.iterator;
181-
iter.return?.();
182-
this.iterator = undefined;
183-
}
184-
if (options?.clearBindings) {
185-
this.bindNamed = {};
186-
this.bindPositional = [];
187-
}
188-
this.statementDone = false;
114+
async *stream(
115+
parameters: SqliteParameterBinding,
116+
options: StreamQueryOptions
117+
): AsyncIterator<SqliteObjectRow[]> {
118+
const rows = await this.all(parameters, options);
119+
yield rows;
120+
}
121+
122+
streamArray(
123+
parameters: SqliteParameterBinding,
124+
options: StreamQueryOptions
125+
): AsyncIterator<SqliteArrayRow[]> {
126+
throw new Error('array rows are not supported');
127+
}
128+
129+
finalize(): void {
130+
// We don't use any iterators internally - nothing to cancel here
189131
}
190132
}
191133

@@ -243,3 +185,11 @@ export class NodeSqliteConnection implements SqliteDriverConnection {
243185
throw new Error('not supported yet');
244186
}
245187
}
188+
189+
function convertParameters(parameters: SqliteParameterBinding): any[] {
190+
if (Array.isArray(parameters)) {
191+
return parameters;
192+
} else {
193+
return [parameters];
194+
}
195+
}

packages/driver/src/util/ErrorStatement.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import {
22
PrepareOptions,
3-
ResetOptions,
3+
QueryOptions,
4+
SqliteArrayRow,
45
SqliteChanges,
56
SqliteDriverStatement,
7+
SqliteObjectRow,
68
SqliteParameterBinding,
7-
SqliteStepResult,
8-
StepOptions
9+
StreamQueryOptions
910
} from '../driver-api.js';
1011
import { SqliteDriverError } from '../worker_threads/async-commands.js';
1112

@@ -27,28 +28,45 @@ export class ErrorStatement implements SqliteDriverStatement {
2728
) {
2829
this.error = error;
2930
this.source = source;
30-
this.persisted = options.persist ?? false;
31+
this.persisted = options.autoFinalize ?? false;
3132
}
3233

33-
async getColumns(): Promise<string[]> {
34+
all(
35+
parameters: SqliteParameterBinding,
36+
options: QueryOptions
37+
): Promise<SqliteObjectRow[]> {
3438
throw this.error;
3539
}
36-
bind(parameters: SqliteParameterBinding): void {
37-
// no-op
40+
allArray(
41+
parameters: SqliteParameterBinding,
42+
options: QueryOptions
43+
): Promise<SqliteArrayRow[]> {
44+
throw this.error;
3845
}
39-
async step(n?: number, options?: StepOptions): Promise<SqliteStepResult> {
46+
stream(
47+
parameters: SqliteParameterBinding,
48+
options: StreamQueryOptions
49+
): AsyncIterator<SqliteObjectRow[]> {
4050
throw this.error;
4151
}
42-
43-
async run(options?: StepOptions): Promise<SqliteChanges> {
52+
streamArray(
53+
parameters: SqliteParameterBinding,
54+
options: StreamQueryOptions
55+
): AsyncIterator<SqliteArrayRow[]> {
4456
throw this.error;
4557
}
4658

47-
finalize(): void {
48-
// no-op
59+
async getColumns(): Promise<string[]> {
60+
throw this.error;
61+
}
62+
async run(
63+
parameters: SqliteParameterBinding,
64+
options?: QueryOptions
65+
): Promise<SqliteChanges> {
66+
throw this.error;
4967
}
5068

51-
reset(options?: ResetOptions): void {
69+
finalize(): void {
5270
// no-op
5371
}
5472

0 commit comments

Comments
 (0)