@stamhoofd/sql 2.64.0 → 2.65.1

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.
Files changed (54) hide show
  1. package/dist/index.d.ts +11 -10
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/src/QueryableModel.d.ts +12 -0
  6. package/dist/src/QueryableModel.d.ts.map +1 -0
  7. package/dist/src/QueryableModel.js +30 -0
  8. package/dist/src/QueryableModel.js.map +1 -0
  9. package/dist/src/SQL.d.ts +3 -3
  10. package/dist/src/SQL.d.ts.map +1 -1
  11. package/dist/src/SQL.js +5 -5
  12. package/dist/src/SQL.js.map +1 -1
  13. package/dist/src/SQLExpression.d.ts +1 -0
  14. package/dist/src/SQLExpression.d.ts.map +1 -1
  15. package/dist/src/SQLExpression.js +2 -2
  16. package/dist/src/SQLExpression.js.map +1 -1
  17. package/dist/src/SQLExpressions.d.ts +28 -3
  18. package/dist/src/SQLExpressions.d.ts.map +1 -1
  19. package/dist/src/SQLExpressions.js +75 -6
  20. package/dist/src/SQLExpressions.js.map +1 -1
  21. package/dist/src/SQLInsert.d.ts.map +1 -1
  22. package/dist/src/SQLInsert.js +6 -1
  23. package/dist/src/SQLInsert.js.map +1 -1
  24. package/dist/src/SQLJoin.d.ts +3 -3
  25. package/dist/src/SQLJoin.d.ts.map +1 -1
  26. package/dist/src/SQLJoin.js +2 -2
  27. package/dist/src/SQLJoin.js.map +1 -1
  28. package/dist/src/SQLOrderBy.d.ts +3 -3
  29. package/dist/src/SQLOrderBy.d.ts.map +1 -1
  30. package/dist/src/SQLOrderBy.js +8 -4
  31. package/dist/src/SQLOrderBy.js.map +1 -1
  32. package/dist/src/SQLSelect.d.ts +19 -2
  33. package/dist/src/SQLSelect.d.ts.map +1 -1
  34. package/dist/src/SQLSelect.js +164 -2
  35. package/dist/src/SQLSelect.js.map +1 -1
  36. package/dist/src/SQLUpdate.d.ts.map +1 -1
  37. package/dist/src/SQLUpdate.js +6 -1
  38. package/dist/src/SQLUpdate.js.map +1 -1
  39. package/dist/src/SQLWhere.d.ts +18 -10
  40. package/dist/src/SQLWhere.d.ts.map +1 -1
  41. package/dist/src/SQLWhere.js +32 -17
  42. package/dist/src/SQLWhere.js.map +1 -1
  43. package/dist/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +2 -2
  45. package/src/QueryableModel.ts +35 -0
  46. package/src/SQL.ts +7 -8
  47. package/src/SQLExpression.ts +12 -12
  48. package/src/SQLExpressions.ts +92 -8
  49. package/src/SQLInsert.ts +7 -1
  50. package/src/SQLJoin.ts +11 -11
  51. package/src/SQLOrderBy.ts +28 -24
  52. package/src/SQLSelect.ts +210 -4
  53. package/src/SQLUpdate.ts +7 -1
  54. package/src/SQLWhere.ts +54 -26
@@ -1,30 +1,30 @@
1
1
  export type SQLExpressionOptions = {
2
- defaultNamespace?: string
2
+ defaultNamespace?: string;
3
+ parentNamespace?: string;
3
4
  };
4
5
 
5
- export type NormalizedSQLQuery = {query: string; params: any[]}
6
- export type SQLQuery = NormalizedSQLQuery|string
6
+ export type NormalizedSQLQuery = { query: string; params: any[] };
7
+ export type SQLQuery = NormalizedSQLQuery | string;
7
8
 
8
- export function joinSQLQuery(queries: (SQLQuery|undefined|null)[], seperator?: string): NormalizedSQLQuery {
9
- queries = queries.filter(q => q !== undefined && q !== null)
9
+ export function joinSQLQuery(queries: (SQLQuery | undefined | null)[], seperator?: string): NormalizedSQLQuery {
10
+ queries = queries.filter(q => q !== undefined && q !== null);
10
11
  return {
11
12
  query: queries.map(q => typeof q === 'string' ? q : q!.query).join(seperator ?? ''),
12
- params: queries.flatMap(q => typeof q === 'string' ? [] : q!.params)
13
- }
13
+ params: queries.flatMap(q => typeof q === 'string' ? [] : q!.params),
14
+ };
14
15
  }
15
16
 
16
17
  export function normalizeSQLQuery(q: SQLQuery): NormalizedSQLQuery {
17
18
  return {
18
19
  query: typeof q === 'string' ? q : q.query,
19
- params: typeof q === 'string' ? [] : q.params
20
- }
20
+ params: typeof q === 'string' ? [] : q.params,
21
+ };
21
22
  }
22
23
 
23
-
24
24
  export interface SQLExpression {
25
- getSQL(options?: SQLExpressionOptions): SQLQuery
25
+ getSQL(options?: SQLExpressionOptions): SQLQuery;
26
26
  }
27
27
 
28
28
  export function isSQLExpression(obj: unknown): obj is SQLExpression {
29
- return typeof obj === 'object' && obj !== null && !!(obj as any).getSQL && typeof (obj as any).getSQL === 'function'
29
+ return typeof obj === 'object' && obj !== null && !!(obj as any).getSQL && typeof (obj as any).getSQL === 'function';
30
30
  }
@@ -1,5 +1,6 @@
1
1
  import { Database } from '@simonbackx/simple-database';
2
2
  import { joinSQLQuery, SQLExpression, SQLExpressionOptions, SQLQuery } from './SQLExpression';
3
+ import { ParseWhereArguments, SQLEmptyWhere } from './SQLWhere';
3
4
 
4
5
  export type SQLScalarValue = string | number | boolean | Date;
5
6
  export type SQLDynamicExpression = SQLScalarValue | SQLScalarValue[] | null | SQLExpression;
@@ -329,23 +330,74 @@ export class SQLWildcardSelectExpression implements SQLExpression {
329
330
  }
330
331
  }
331
332
 
333
+ export class SQLNamespaceExpression implements SQLExpression {
334
+ namespace: string;
335
+
336
+ constructor(namespace: string) {
337
+ this.namespace = namespace;
338
+ }
339
+
340
+ getSQL(options?: SQLExpressionOptions): SQLQuery {
341
+ return Database.escapeId(this.namespace);
342
+ }
343
+ }
344
+
345
+ export const SQLDefaultNamespace: SQLExpression = {
346
+ getSQL(options?: SQLExpressionOptions): SQLQuery {
347
+ if (!options?.defaultNamespace) {
348
+ throw new Error('No default namespace provided');
349
+ }
350
+ return Database.escapeId(options.defaultNamespace);
351
+ },
352
+ };
353
+
354
+ /**
355
+ * Reference the table of the parent query
356
+ */
357
+ export const SQLParentNamespace: SQLExpression = {
358
+ getSQL(options?: SQLExpressionOptions): SQLQuery {
359
+ if (!options?.parentNamespace) {
360
+ throw new Error('No parent namespace provided');
361
+ }
362
+ return Database.escapeId(options.parentNamespace);
363
+ },
364
+ };
365
+
366
+ export type SQLColumnExpressionParams = [
367
+ namespace: string | SQLExpression,
368
+ column: string,
369
+ ] | [
370
+ column: string,
371
+ ];
372
+
332
373
  export class SQLColumnExpression implements SQLExpression {
333
- namespace?: string;
374
+ namespace: SQLExpression;
334
375
  column: string;
335
376
 
336
- constructor(namespace: string, column: string);
337
- constructor(column: string);
338
- constructor(namespaceOrColumn: string, column?: string) {
339
- if (column === undefined) {
340
- this.column = namespaceOrColumn;
377
+ constructor(...args: SQLColumnExpressionParams) {
378
+ if (args.length === 1) {
379
+ this.namespace = SQLDefaultNamespace;
380
+ this.column = args[0];
341
381
  return;
342
382
  }
343
- this.namespace = namespaceOrColumn;
383
+
384
+ const [namespace, column] = args;
385
+
386
+ if (typeof namespace === 'string') {
387
+ this.namespace = new SQLNamespaceExpression(namespace);
388
+ }
389
+ else {
390
+ this.namespace = namespace;
391
+ }
344
392
  this.column = column;
345
393
  }
346
394
 
347
395
  getSQL(options?: SQLExpressionOptions): SQLQuery {
348
- return Database.escapeId(this.namespace ?? options?.defaultNamespace ?? '') + '.' + Database.escapeId(this.column);
396
+ return joinSQLQuery([
397
+ this.namespace.getSQL(options),
398
+ '.',
399
+ Database.escapeId(this.column),
400
+ ]);
349
401
  }
350
402
  }
351
403
 
@@ -371,3 +423,35 @@ export class SQLTableExpression implements SQLExpression {
371
423
  return Database.escapeId(this.table) + ' ' + Database.escapeId(this.namespace);
372
424
  }
373
425
  }
426
+
427
+ export class SQLIf implements SQLExpression {
428
+ _if: SQLExpression;
429
+ _then: SQLExpression = new SQLNull();
430
+ _else: SQLExpression = new SQLNull();
431
+
432
+ constructor(...args: ParseWhereArguments) {
433
+ this._if = new SQLEmptyWhere().and(...args);
434
+ }
435
+
436
+ then(then: SQLDynamicExpression): SQLIf {
437
+ this._then = readDynamicSQLExpression(then);
438
+ return this;
439
+ }
440
+
441
+ else(_else: SQLDynamicExpression): SQLIf {
442
+ this._else = readDynamicSQLExpression(_else);
443
+ return this;
444
+ }
445
+
446
+ getSQL(options?: SQLExpressionOptions): SQLQuery {
447
+ return joinSQLQuery([
448
+ 'IF(',
449
+ this._if.getSQL(options),
450
+ ',',
451
+ this._then.getSQL(options),
452
+ ',',
453
+ this._else.getSQL(options),
454
+ ')',
455
+ ]);
456
+ }
457
+ }
package/src/SQLInsert.ts CHANGED
@@ -47,9 +47,15 @@ export class SQLInsert implements SQLExpression {
47
47
  }
48
48
 
49
49
  getSQL(options?: SQLExpressionOptions): SQLQuery {
50
- options = options ?? {};
50
+ // Create a clone since we are mutating the default namespaces
51
+ const parentOptions = options;
52
+ options = options ? { ...options } : {};
51
53
  options.defaultNamespace = (this._into as any).namespace ?? (this._into).table ?? undefined;
52
54
 
55
+ if (parentOptions?.defaultNamespace) {
56
+ options.parentNamespace = parentOptions.defaultNamespace;
57
+ }
58
+
53
59
  const query: SQLQuery[] = [
54
60
  'INSERT INTO',
55
61
  this._into.getSQL(options),
package/src/SQLJoin.ts CHANGED
@@ -1,16 +1,16 @@
1
- import { SQLExpression, SQLExpressionOptions, SQLQuery, joinSQLQuery } from "./SQLExpression";
2
- import { SQLWhere, Whereable } from "./SQLWhere";
1
+ import { SQLExpression, SQLExpressionOptions, SQLQuery, joinSQLQuery } from './SQLExpression';
2
+ import { SQLWhere, Whereable } from './SQLWhere';
3
3
 
4
4
  export enum SQLJoinType {
5
- Left = "Left",
6
- Right = "Right",
7
- Inner = "Inner",
8
- Outer = "Outer"
5
+ Left = 'Left',
6
+ Right = 'Right',
7
+ Inner = 'Inner',
8
+ Outer = 'Outer',
9
9
  }
10
10
 
11
11
  class EmptyClass {}
12
12
  export class SQLJoin extends Whereable(EmptyClass) implements SQLExpression {
13
- type = SQLJoinType.Left
13
+ type = SQLJoinType.Left;
14
14
  table: SQLExpression;
15
15
 
16
16
  constructor(type: SQLJoinType, table: SQLExpression) {
@@ -28,12 +28,12 @@ export class SQLJoin extends Whereable(EmptyClass) implements SQLExpression {
28
28
  }
29
29
  }
30
30
 
31
- getSQL(options?: SQLExpressionOptions | undefined): SQLQuery {
31
+ getSQL(options?: SQLExpressionOptions): SQLQuery {
32
32
  return joinSQLQuery([
33
33
  this.getJoinPrefix(),
34
- this.table?.getSQL(),
34
+ this.table?.getSQL(options),
35
35
  this._where ? 'ON' : undefined,
36
- this._where?.getSQL()
37
- ], ' ')
36
+ this._where?.getSQL(options),
37
+ ], ' ');
38
38
  }
39
39
  }
package/src/SQLOrderBy.ts CHANGED
@@ -1,50 +1,54 @@
1
- import { SQLExpression, SQLExpressionOptions, SQLQuery, joinSQLQuery } from "./SQLExpression";
1
+ import { SQLExpression, SQLExpressionOptions, SQLQuery, joinSQLQuery } from './SQLExpression';
2
+ import { SQLColumnExpression } from './SQLExpressions';
2
3
 
3
4
  type Constructor<T = {}> = new (...args: any[]) => T;
4
5
 
5
6
  export function Orderable<Sup extends Constructor<{}>>(Base: Sup) {
6
7
  return class extends Base {
7
- _orderBy: SQLOrderBy|null = null
8
+ _orderBy: SQLOrderBy | null = null;
8
9
 
9
- orderBy<T>(this: T, orderBy: SQLOrderBy): T
10
- orderBy<T>(this: T, column: SQLExpression, direction?: SQLOrderByDirection) : T
11
- orderBy<T>(this: T, columnOrOrderBy: SQLExpression, direction?: SQLOrderByDirection): T {
12
- let o = columnOrOrderBy as SQLOrderBy
10
+ orderBy<T>(this: T, orderBy: SQLOrderBy): T;
11
+ orderBy<T>(this: T, column: SQLExpression | string, direction?: SQLOrderByDirection): T;
12
+ orderBy<T>(this: T, columnOrOrderBy: SQLExpression | string, direction?: SQLOrderByDirection): T {
13
+ let o = columnOrOrderBy as SQLOrderBy;
13
14
  if (!(columnOrOrderBy instanceof SQLOrderBy)) {
14
- o = new SQLOrderBy({column: columnOrOrderBy, direction: direction ?? 'ASC'})
15
+ o = new SQLOrderBy({
16
+ column: typeof columnOrOrderBy === 'string' ? new SQLColumnExpression(columnOrOrderBy) : columnOrOrderBy,
17
+ direction: direction ?? 'ASC',
18
+ });
15
19
  }
16
20
 
17
21
  const me = this as any; // stupid typescript looses type information if we don't do the this: T dance
18
22
 
19
23
  if (me._orderBy) {
20
- me._orderBy.add(o)
21
- } else {
24
+ me._orderBy.add(o);
25
+ }
26
+ else {
22
27
  me._orderBy = o;
23
28
  }
24
29
 
25
30
  return me;
26
31
  }
27
- }
32
+ };
28
33
  }
29
34
 
30
-
31
35
  export type SQLOrderByDirection = 'ASC' | 'DESC';
32
36
  export class SQLOrderBy implements SQLExpression {
33
- orderBy: {column: SQLExpression, direction: SQLOrderByDirection}[] = [];
37
+ orderBy: { column: SQLExpression; direction: SQLOrderByDirection }[] = [];
34
38
 
35
- constructor(...orderBy: {column: SQLExpression, direction: SQLOrderByDirection}[]) {
36
- this.orderBy = orderBy
39
+ constructor(...orderBy: { column: SQLExpression; direction: SQLOrderByDirection }[]) {
40
+ this.orderBy = orderBy;
37
41
  }
38
42
 
39
43
  static combine(orderBy: SQLOrderBy[]) {
40
- return new SQLOrderBy(...orderBy.flatMap(o => o.orderBy))
44
+ return new SQLOrderBy(...orderBy.flatMap(o => o.orderBy));
41
45
  }
42
46
 
43
47
  add(orderBy: SQLOrderBy) {
44
- this.orderBy.push(...orderBy.orderBy)
48
+ this.orderBy.push(...orderBy.orderBy);
45
49
  }
46
50
 
47
- getSQL(options?: SQLExpressionOptions | undefined): SQLQuery {
51
+ getSQL(options?: SQLExpressionOptions): SQLQuery {
48
52
  if (this.orderBy.length === 0) {
49
53
  return '';
50
54
  }
@@ -52,14 +56,14 @@ export class SQLOrderBy implements SQLExpression {
52
56
  return joinSQLQuery([
53
57
  'ORDER BY ',
54
58
  joinSQLQuery(
55
- this.orderBy.map(o => {
59
+ this.orderBy.map((o) => {
56
60
  return joinSQLQuery([
57
61
  o.column.getSQL(options),
58
- o.direction
59
- ], ' ')
60
- }),
61
- ', '
62
- )
63
- ])
62
+ o.direction,
63
+ ], ' ');
64
+ }),
65
+ ', ',
66
+ ),
67
+ ]);
64
68
  }
65
69
  }
package/src/SQLSelect.ts CHANGED
@@ -3,7 +3,7 @@ import { SQLExpression, SQLExpressionOptions, SQLQuery, joinSQLQuery, normalizeS
3
3
  import { SQLAlias, SQLColumnExpression, SQLCount, SQLSelectAs, SQLSum, SQLTableExpression } from './SQLExpressions';
4
4
  import { SQLJoin } from './SQLJoin';
5
5
  import { Orderable } from './SQLOrderBy';
6
- import { Whereable } from './SQLWhere';
6
+ import { SQLWhereSign, Whereable } from './SQLWhere';
7
7
 
8
8
  class EmptyClass {}
9
9
 
@@ -19,7 +19,19 @@ export function parseTable(tableOrExpressiongOrNamespace: SQLExpression | string
19
19
  }
20
20
  }
21
21
 
22
- export class SQLSelect<T = SQLResultNamespacedRow> extends Whereable(Orderable(EmptyClass)) implements SQLExpression {
22
+ export type IterableSQLSelect<T extends object = SQLResultNamespacedRow> = AsyncIterableIterator<T, undefined> & {
23
+ isDone: boolean;
24
+ options: IterableSQLSelectOptions;
25
+ maxQueries(maxQueries: number): IterableSQLSelect<T>;
26
+ };
27
+ export type IterableSQLSelectOptions = {
28
+ /**
29
+ * The loop will cancel after this amount of queries - but you can continue to loop over the results when starting a new for loop.
30
+ */
31
+ maxQueries?: number;
32
+ };
33
+
34
+ export class SQLSelect<T extends object = SQLResultNamespacedRow> extends Whereable(Orderable(EmptyClass)) implements SQLExpression {
23
35
  _columns: SQLExpression[];
24
36
  _from: SQLExpression;
25
37
 
@@ -81,9 +93,15 @@ export class SQLSelect<T = SQLResultNamespacedRow> extends Whereable(Orderable(E
81
93
  query.push('/*+ MAX_EXECUTION_TIME(' + this._max_execution_time + ') */');
82
94
  }
83
95
 
84
- options = options ?? {};
96
+ // Create a clone since we are mutating the default namespaces
97
+ const parentOptions = options;
98
+ options = options ? { ...options } : {};
85
99
  options.defaultNamespace = (this._from as any).namespace ?? (this._from as any).table ?? undefined;
86
100
 
101
+ if (parentOptions?.defaultNamespace) {
102
+ options.parentNamespace = parentOptions.defaultNamespace;
103
+ }
104
+
87
105
  const columns = this._columns.map(c => c.getSQL(options));
88
106
  query.push(
89
107
  joinSQLQuery(columns, ', '),
@@ -136,7 +154,7 @@ export class SQLSelect<T = SQLResultNamespacedRow> extends Whereable(Orderable(E
136
154
  const { query, params } = normalizeSQLQuery(this.getSQL());
137
155
 
138
156
  // when debugging: log all queries
139
- // console.log(query, params);
157
+ console.log(query, params);
140
158
  const [rows] = await Database.select(query, params, { nestTables: true });
141
159
 
142
160
  // Now map aggregated queries to the correct namespace
@@ -238,4 +256,192 @@ export class SQLSelect<T = SQLResultNamespacedRow> extends Whereable(Orderable(E
238
256
  console.warn('Invalid sum SQL response', rows);
239
257
  return 0;
240
258
  }
259
+
260
+ all(options: IterableSQLSelectOptions = {}): T extends { id: string } ? IterableSQLSelect<T> : never {
261
+ if (this._orderBy) {
262
+ throw new Error('Cannot use async iterator with custom order by. Results should be ordered by ID');
263
+ }
264
+
265
+ if (this._offset !== null) {
266
+ throw new Error('Cannot use async iterator with offset');
267
+ }
268
+
269
+ if (!this._limit) {
270
+ this._limit = 100;
271
+ }
272
+
273
+ const limit = this._limit;
274
+ this.orderBy('id');
275
+
276
+ let next: this | null = this.clone();
277
+ const base = this;
278
+
279
+ let stack: T[] = [];
280
+ let stackIndex = 0;
281
+
282
+ return {
283
+ queryCount: 0,
284
+ options,
285
+ [Symbol.asyncIterator]() {
286
+ return {
287
+ ...this,
288
+
289
+ // Reset the iterator
290
+ queryCount: 0,
291
+ };
292
+ },
293
+ get isDone() {
294
+ return !next && (stackIndex + 1) >= stack.length;
295
+ },
296
+ async next(): Promise<IteratorResult<T, undefined>> {
297
+ stackIndex++;
298
+
299
+ if (stackIndex < stack.length) {
300
+ return {
301
+ done: false,
302
+ value: stack[stackIndex],
303
+ };
304
+ }
305
+
306
+ if (!next) {
307
+ stack = []; // Clean up memory
308
+ return {
309
+ done: true,
310
+ value: undefined,
311
+ };
312
+ }
313
+
314
+ if (this.options.maxQueries !== undefined && this.queryCount >= this.options.maxQueries) {
315
+ // Stopping early
316
+ return {
317
+ done: true,
318
+ value: undefined,
319
+ };
320
+ }
321
+
322
+ stack = await next.fetch();
323
+ next = null;
324
+ this.queryCount += 1;
325
+
326
+ if (stack.length === 0) {
327
+ return {
328
+ done: true,
329
+ value: undefined,
330
+ };
331
+ }
332
+ stackIndex = 0;
333
+
334
+ if (stack.length >= limit) {
335
+ next = base.clone();
336
+ const lastResult = stack[stack.length - 1]!;
337
+ if (!('id' in lastResult)) {
338
+ throw new Error('Cannot use async iterator without ID column');
339
+ }
340
+
341
+ const lastId = lastResult.id;
342
+ if (typeof lastId !== 'string') {
343
+ throw new Error('Cannot use async iterator without string ID column');
344
+ }
345
+
346
+ next.andWhere('id', '>', lastId);
347
+ }
348
+
349
+ return {
350
+ done: false,
351
+ value: stack[stackIndex],
352
+ };
353
+ },
354
+ maxQueries(maxQueries: number) {
355
+ this.options.maxQueries = maxQueries;
356
+ return this;
357
+ },
358
+ } as IterableSQLSelect<T> as any;
359
+ }
360
+
361
+ allBatched(options: IterableSQLSelectOptions = {}): T extends { id: string } ? IterableSQLSelect<T[]> : never {
362
+ if (this._orderBy) {
363
+ throw new Error('Cannot use async iterator with custom order by. Results should be ordered by ID');
364
+ }
365
+
366
+ if (this._offset !== null) {
367
+ throw new Error('Cannot use async iterator with offset');
368
+ }
369
+
370
+ if (!this._limit) {
371
+ this._limit = 100;
372
+ }
373
+
374
+ const limit = this._limit;
375
+ this.orderBy('id');
376
+
377
+ let next: this | null = this.clone();
378
+ const base = this;
379
+
380
+ return {
381
+ queryCount: 0,
382
+ options,
383
+ [Symbol.asyncIterator]() {
384
+ return {
385
+ ...this,
386
+
387
+ // Reset the iterator
388
+ queryCount: 0,
389
+ };
390
+ },
391
+ get isDone() {
392
+ return !next;
393
+ },
394
+ async next(): Promise<IteratorResult<T[], undefined>> {
395
+ if (!next) {
396
+ return {
397
+ done: true,
398
+ value: undefined,
399
+ };
400
+ }
401
+
402
+ if (this.options.maxQueries !== undefined && this.queryCount >= this.options.maxQueries) {
403
+ // Stopping early
404
+ return {
405
+ done: true,
406
+ value: undefined,
407
+ };
408
+ }
409
+
410
+ const stack = await next.fetch();
411
+ next = null;
412
+ this.queryCount += 1;
413
+
414
+ if (stack.length === 0) {
415
+ return {
416
+ done: true,
417
+ value: undefined,
418
+ };
419
+ }
420
+
421
+ if (stack.length >= limit) {
422
+ next = base.clone();
423
+ const lastResult = stack[stack.length - 1]!;
424
+ if (!('id' in lastResult)) {
425
+ throw new Error('Cannot use async iterator without ID column');
426
+ }
427
+
428
+ const lastId = lastResult.id;
429
+ if (typeof lastId !== 'string') {
430
+ throw new Error('Cannot use async iterator without string ID column');
431
+ }
432
+
433
+ next.andWhere('id', '>', lastId);
434
+ }
435
+
436
+ return {
437
+ done: false,
438
+ value: stack,
439
+ };
440
+ },
441
+ maxQueries(maxQueries: number) {
442
+ this.options.maxQueries = maxQueries;
443
+ return this;
444
+ },
445
+ } as IterableSQLSelect<T[]> as any;
446
+ }
241
447
  }
package/src/SQLUpdate.ts CHANGED
@@ -34,9 +34,15 @@ export class SQLUpdate extends Whereable(EmptyClass) implements SQLExpression {
34
34
  throw new Error('No assignments provided');
35
35
  }
36
36
 
37
- options = options ?? {};
37
+ // Create a clone since we are mutating the default namespaces
38
+ const parentOptions = options;
39
+ options = options ? { ...options } : {};
38
40
  options.defaultNamespace = (this._table as any).namespace ?? (this._table).table ?? undefined;
39
41
 
42
+ if (parentOptions?.defaultNamespace) {
43
+ options.parentNamespace = parentOptions.defaultNamespace;
44
+ }
45
+
40
46
  const query: SQLQuery[] = [
41
47
  'UPDATE ',
42
48
  this._table.getSQL(options),