Skip to content

Commit 86cb375

Browse files
authored
Merge pull request #473 from drizzle-team/mysql2-iterator
2 parents 0f8d15b + 4f0bf07 commit 86cb375

File tree

26 files changed

+498
-206
lines changed

26 files changed

+498
-206
lines changed

changelogs/drizzle-orm/0.24.0.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- 🎉 Added iterator support to `mysql2` (sponsored by @ryzen ❤)
2+
-`.prepare()` in MySQL no longer requires a name argument

drizzle-orm/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "drizzle-orm",
3-
"version": "0.23.13",
3+
"version": "0.24.0",
44
"description": "Drizzle ORM package for SQL databases",
55
"scripts": {
66
"build": "tsc && resolve-tspaths && cp ../README.md package.json dist/",

drizzle-orm/src/mysql-core/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,27 @@ Subqueries in joins are supported, too:
495495
const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id));
496496
```
497497

498+
#### Querying large datasets
499+
500+
If you need to return a very large amount of rows from a query and you don't want to load them all into memory, you can use `.iterator()` to convert the query into an async iterator:
501+
502+
```typescript
503+
const iterator = await db.select().from(users).iterator();
504+
for await (const row of iterator) {
505+
console.log(row);
506+
}
507+
```
508+
509+
It also works with prepared statements:
510+
511+
```typescript
512+
const query = await db.select().from(users).prepare();
513+
const iterator = await query.iterator();
514+
for await (const row of iterator) {
515+
console.log(row);
516+
}
517+
```
518+
498519
#### List of all filter operators
499520

500521
```typescript

drizzle-orm/src/mysql-core/db.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,22 @@ import type {
1717
MySqlSession,
1818
MySqlTransaction,
1919
MySqlTransactionConfig,
20+
PreparedQueryHKTBase,
2021
QueryResultHKT,
2122
QueryResultKind,
2223
} from './session';
2324
import type { WithSubqueryWithSelection } from './subquery';
2425
import type { AnyMySqlTable } from './table';
2526

26-
export class MySqlDatabase<TQueryResult extends QueryResultHKT> {
27+
export class MySqlDatabase<
28+
TQueryResult extends QueryResultHKT,
29+
TPreparedQueryHKT extends PreparedQueryHKTBase,
30+
> {
2731
constructor(
2832
/** @internal */
2933
readonly dialect: MySqlDialect,
3034
/** @internal */
31-
readonly session: MySqlSession<any>,
35+
readonly session: MySqlSession<any, any>,
3236
) {}
3337

3438
$with<TAlias extends string>(alias: TAlias) {
@@ -51,30 +55,32 @@ export class MySqlDatabase<TQueryResult extends QueryResultHKT> {
5155
with(...queries: WithSubquery[]) {
5256
const self = this;
5357

54-
function select(): MySqlSelectBuilder<undefined>;
55-
function select<TSelection extends SelectedFields>(fields: TSelection): MySqlSelectBuilder<TSelection>;
56-
function select(fields?: SelectedFields): MySqlSelectBuilder<SelectedFields | undefined> {
58+
function select(): MySqlSelectBuilder<undefined, TPreparedQueryHKT>;
59+
function select<TSelection extends SelectedFields>(
60+
fields: TSelection,
61+
): MySqlSelectBuilder<TSelection, TPreparedQueryHKT>;
62+
function select(fields?: SelectedFields): MySqlSelectBuilder<SelectedFields | undefined, TPreparedQueryHKT> {
5763
return new MySqlSelectBuilder(fields ?? undefined, self.session, self.dialect, queries);
5864
}
5965

6066
return { select };
6167
}
6268

63-
select(): MySqlSelectBuilder<undefined>;
64-
select<TSelection extends SelectedFields>(fields: TSelection): MySqlSelectBuilder<TSelection>;
65-
select(fields?: SelectedFields): MySqlSelectBuilder<SelectedFields | undefined> {
69+
select(): MySqlSelectBuilder<undefined, TPreparedQueryHKT>;
70+
select<TSelection extends SelectedFields>(fields: TSelection): MySqlSelectBuilder<TSelection, TPreparedQueryHKT>;
71+
select(fields?: SelectedFields): MySqlSelectBuilder<SelectedFields | undefined, TPreparedQueryHKT> {
6672
return new MySqlSelectBuilder(fields ?? undefined, this.session, this.dialect);
6773
}
6874

69-
update<TTable extends AnyMySqlTable>(table: TTable): MySqlUpdateBuilder<TTable, TQueryResult> {
75+
update<TTable extends AnyMySqlTable>(table: TTable): MySqlUpdateBuilder<TTable, TQueryResult, TPreparedQueryHKT> {
7076
return new MySqlUpdateBuilder(table, this.session, this.dialect);
7177
}
7278

73-
insert<TTable extends AnyMySqlTable>(table: TTable): MySqlInsertBuilder<TTable, TQueryResult> {
79+
insert<TTable extends AnyMySqlTable>(table: TTable): MySqlInsertBuilder<TTable, TQueryResult, TPreparedQueryHKT> {
7480
return new MySqlInsertBuilder(table, this.session, this.dialect);
7581
}
7682

77-
delete<TTable extends AnyMySqlTable>(table: TTable): MySqlDelete<TTable, TQueryResult> {
83+
delete<TTable extends AnyMySqlTable>(table: TTable): MySqlDelete<TTable, TQueryResult, TPreparedQueryHKT> {
7884
return new MySqlDelete(table, this.session, this.dialect);
7985
}
8086

@@ -85,7 +91,7 @@ export class MySqlDatabase<TQueryResult extends QueryResultHKT> {
8591
}
8692

8793
transaction<T>(
88-
transaction: (tx: MySqlTransaction<TQueryResult>, config?: MySqlTransactionConfig) => Promise<T>,
94+
transaction: (tx: MySqlTransaction<TQueryResult, TPreparedQueryHKT>, config?: MySqlTransactionConfig) => Promise<T>,
8995
config?: MySqlTransactionConfig,
9096
): Promise<T> {
9197
return this.session.transaction(transaction, config);

drizzle-orm/src/mysql-core/query-builders/delete.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { MySqlDialect } from '~/mysql-core/dialect';
22
import type {
33
MySqlSession,
4-
PreparedQuery,
54
PreparedQueryConfig,
5+
PreparedQueryHKTBase,
6+
PreparedQueryKind,
67
QueryResultHKT,
78
QueryResultKind,
89
} from '~/mysql-core/session';
@@ -20,11 +21,13 @@ export interface MySqlDeleteConfig {
2021
export interface MySqlDelete<
2122
TTable extends AnyMySqlTable,
2223
TQueryResult extends QueryResultHKT,
24+
TPreparedQueryHKT extends PreparedQueryHKTBase,
2325
> extends QueryPromise<QueryResultKind<TQueryResult, never>> {}
2426

2527
export class MySqlDelete<
2628
TTable extends AnyMySqlTable,
2729
TQueryResult extends QueryResultHKT,
30+
TPreparedQueryHKT extends PreparedQueryHKTBase,
2831
> extends QueryPromise<QueryResultKind<TQueryResult, never>> implements SQLWrapper {
2932
private config: MySqlDeleteConfig;
3033

@@ -54,23 +57,30 @@ export class MySqlDelete<
5457
return rest;
5558
}
5659

57-
private _prepare(name?: string): PreparedQuery<
58-
PreparedQueryConfig & {
59-
execute: TQueryResult;
60-
}
61-
> {
62-
return this.session.prepareQuery(this.dialect.sqlToQuery(this.getSQL()), this.config.returning, name);
63-
}
64-
65-
prepare(name: string): PreparedQuery<
66-
PreparedQueryConfig & {
67-
execute: QueryResultKind<TQueryResult, never>;
68-
}
69-
> {
70-
return this._prepare(name);
60+
prepare() {
61+
return this.session.prepareQuery(
62+
this.dialect.sqlToQuery(this.getSQL()),
63+
this.config.returning,
64+
) as PreparedQueryKind<
65+
TPreparedQueryHKT,
66+
PreparedQueryConfig & {
67+
execute: QueryResultKind<TQueryResult, never>;
68+
iterator: never;
69+
},
70+
true
71+
>;
7172
}
7273

7374
override execute: ReturnType<this['prepare']>['execute'] = (placeholderValues) => {
74-
return this._prepare().execute(placeholderValues);
75+
return this.prepare().execute(placeholderValues);
7576
};
77+
78+
private createIterator = (): ReturnType<this['prepare']>['iterator'] => {
79+
const self = this;
80+
return async function*(placeholderValues) {
81+
yield* self.prepare().iterator(placeholderValues);
82+
};
83+
};
84+
85+
iterator = this.createIterator();
7686
}

drizzle-orm/src/mysql-core/query-builders/insert.ts

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { MySqlDialect } from '~/mysql-core/dialect';
22
import type {
33
MySqlSession,
4-
PreparedQuery,
54
PreparedQueryConfig,
5+
PreparedQueryHKTBase,
6+
PreparedQueryKind,
67
QueryResultHKT,
78
QueryResultKind,
89
} from '~/mysql-core/session';
@@ -32,7 +33,11 @@ export type MySqlInsertValue<TTable extends AnyMySqlTable> = Simplify<
3233
}
3334
>;
3435

35-
export class MySqlInsertBuilder<TTable extends AnyMySqlTable, TQueryResult extends QueryResultHKT> {
36+
export class MySqlInsertBuilder<
37+
TTable extends AnyMySqlTable,
38+
TQueryResult extends QueryResultHKT,
39+
TPreparedQueryHKT extends PreparedQueryHKTBase,
40+
> {
3641
private shouldIgnore: boolean = false;
3742

3843
constructor(
@@ -46,15 +51,15 @@ export class MySqlInsertBuilder<TTable extends AnyMySqlTable, TQueryResult exten
4651
return this;
4752
}
4853

49-
values(value: MySqlInsertValue<TTable>): MySqlInsert<TTable, TQueryResult>;
50-
values(values: MySqlInsertValue<TTable>[]): MySqlInsert<TTable, TQueryResult>;
54+
values(value: MySqlInsertValue<TTable>): MySqlInsert<TTable, TQueryResult, TPreparedQueryHKT>;
55+
values(values: MySqlInsertValue<TTable>[]): MySqlInsert<TTable, TQueryResult, TPreparedQueryHKT>;
5156
/**
5257
* @deprecated Pass the array of values without spreading it.
5358
*/
54-
values(...values: MySqlInsertValue<TTable>[]): MySqlInsert<TTable, TQueryResult>;
59+
values(...values: MySqlInsertValue<TTable>[]): MySqlInsert<TTable, TQueryResult, TPreparedQueryHKT>;
5560
values(
5661
...values: MySqlInsertValue<TTable>[] | [MySqlInsertValue<TTable>] | [MySqlInsertValue<TTable>[]]
57-
): MySqlInsert<TTable, TQueryResult> {
62+
): MySqlInsert<TTable, TQueryResult, TPreparedQueryHKT> {
5863
if (values.length === 1) {
5964
if (Array.isArray(values[0])) {
6065
values = values[0];
@@ -80,13 +85,18 @@ export class MySqlInsertBuilder<TTable extends AnyMySqlTable, TQueryResult exten
8085
}
8186
}
8287

83-
export interface MySqlInsert<TTable extends AnyMySqlTable, TQueryResult extends QueryResultHKT, TReturning = undefined>
84-
extends QueryPromise<QueryResultKind<TQueryResult, never>>, SQLWrapper
85-
{}
86-
export class MySqlInsert<TTable extends AnyMySqlTable, TQueryResult extends QueryResultHKT, TReturning = undefined>
87-
extends QueryPromise<QueryResultKind<TQueryResult, never>>
88-
implements SQLWrapper
89-
{
88+
export interface MySqlInsert<
89+
TTable extends AnyMySqlTable,
90+
TQueryResult extends QueryResultHKT,
91+
TPreparedQueryHKT extends PreparedQueryHKTBase,
92+
TReturning = undefined,
93+
> extends QueryPromise<QueryResultKind<TQueryResult, never>>, SQLWrapper {}
94+
export class MySqlInsert<
95+
TTable extends AnyMySqlTable,
96+
TQueryResult extends QueryResultHKT,
97+
TPreparedQueryHKT extends PreparedQueryHKTBase,
98+
TReturning = undefined,
99+
> extends QueryPromise<QueryResultKind<TQueryResult, never>> implements SQLWrapper {
90100
declare protected $table: TTable;
91101
declare protected $return: TReturning;
92102

@@ -122,23 +132,30 @@ export class MySqlInsert<TTable extends AnyMySqlTable, TQueryResult extends Quer
122132
return rest;
123133
}
124134

125-
private _prepare(name?: string): PreparedQuery<
126-
PreparedQueryConfig & {
127-
execute: QueryResultKind<TQueryResult, never>;
128-
}
129-
> {
130-
return this.session.prepareQuery(this.dialect.sqlToQuery(this.getSQL()), this.config.returning, name);
131-
}
132-
133-
prepare(name: string): PreparedQuery<
134-
PreparedQueryConfig & {
135-
execute: QueryResultKind<TQueryResult, never>;
136-
}
137-
> {
138-
return this._prepare(name);
135+
prepare() {
136+
return this.session.prepareQuery(
137+
this.dialect.sqlToQuery(this.getSQL()),
138+
this.config.returning,
139+
) as PreparedQueryKind<
140+
TPreparedQueryHKT,
141+
PreparedQueryConfig & {
142+
execute: QueryResultKind<TQueryResult, never>;
143+
iterator: never;
144+
},
145+
true
146+
>;
139147
}
140148

141149
override execute: ReturnType<this['prepare']>['execute'] = (placeholderValues) => {
142-
return this._prepare().execute(placeholderValues);
150+
return this.prepare().execute(placeholderValues);
143151
};
152+
153+
private createIterator = (): ReturnType<this['prepare']>['iterator'] => {
154+
const self = this;
155+
return async function*(placeholderValues) {
156+
yield* self.prepare().iterator(placeholderValues);
157+
};
158+
};
159+
160+
iterator = this.createIterator();
144161
}

drizzle-orm/src/mysql-core/query-builders/query-builder.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,22 @@ export class QueryBuilderInstance {
3737
with(...queries: WithSubquery[]) {
3838
const self = this;
3939

40-
function select(): MySqlSelectBuilder<undefined, 'qb'>;
41-
function select<TSelection extends SelectedFields>(fields: TSelection): MySqlSelectBuilder<TSelection, 'qb'>;
40+
function select(): MySqlSelectBuilder<undefined, never, 'qb'>;
41+
function select<TSelection extends SelectedFields>(fields: TSelection): MySqlSelectBuilder<TSelection, never, 'qb'>;
4242
function select<TSelection extends SelectedFields>(
4343
fields?: TSelection,
44-
): MySqlSelectBuilder<TSelection | undefined, 'qb'> {
44+
): MySqlSelectBuilder<TSelection | undefined, never, 'qb'> {
4545
return new self.MySqlSelectBuilder(fields ?? undefined, undefined, self.getDialect(), queries);
4646
}
4747

4848
return { select };
4949
}
5050

51-
select(): MySqlSelectBuilder<undefined, 'qb'>;
52-
select<TSelection extends SelectedFields>(fields: TSelection): MySqlSelectBuilder<TSelection, 'qb'>;
53-
select<TSelection extends SelectedFields>(fields?: TSelection): MySqlSelectBuilder<TSelection | undefined, 'qb'> {
51+
select(): MySqlSelectBuilder<undefined, never, 'qb'>;
52+
select<TSelection extends SelectedFields>(fields: TSelection): MySqlSelectBuilder<TSelection, never, 'qb'>;
53+
select<TSelection extends SelectedFields>(
54+
fields?: TSelection,
55+
): MySqlSelectBuilder<TSelection | undefined, never, 'qb'> {
5456
return new this.MySqlSelectBuilder(fields ?? undefined, undefined, this.getDialect());
5557
}
5658

0 commit comments

Comments
 (0)