@atscript/db-mysql 0.1.146 → 0.1.148
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/dist/index.cjs +423 -66
- package/dist/index.d.cts +140 -7
- package/dist/index.d.mts +140 -7
- package/dist/index.mjs +425 -68
- package/package.json +10 -10
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TExistingTableOption, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TTableOptionDiff, TValueFormatterPair } from "@atscript/db";
|
|
1
|
+
import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TExistingTableOption, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TTableOptionDiff, TValueFormatterPair, TViewCapability } from "@atscript/db";
|
|
2
2
|
import { TMetadataMap } from "@atscript/typescript/utils";
|
|
3
3
|
import { FilterExpr as FilterExpr$1 } from "@uniqu/core";
|
|
4
|
-
import { TSqlFragment, TSqlFragment as TSqlFragment$1 } from "@atscript/db-sql-tools";
|
|
4
|
+
import { TFilterVisitorOptions, TSqlFragment, TSqlFragment as TSqlFragment$1 } from "@atscript/db-sql-tools";
|
|
5
5
|
|
|
6
6
|
//#region src/types.d.ts
|
|
7
7
|
/**
|
|
@@ -136,6 +136,15 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
136
136
|
* to `NO_ENGINE_SUBSTITUTION`) would turn `'abc'` into `0` silently.
|
|
137
137
|
*/
|
|
138
138
|
private _withStrictSession;
|
|
139
|
+
/**
|
|
140
|
+
* Relational predicates (`$some` / `$none`) render as correlated
|
|
141
|
+
* `[NOT] EXISTS` subqueries — in reads and in mutation filters alike.
|
|
142
|
+
* An UPDATE / DELETE whose predicate reads the mutated table itself is
|
|
143
|
+
* rewritten through a materialized derived table (MySQL error 1093).
|
|
144
|
+
*
|
|
145
|
+
* @since 0.1.147
|
|
146
|
+
*/
|
|
147
|
+
supportsRelationFilters(_mode: "read" | "write"): boolean;
|
|
139
148
|
/** MySQL InnoDB enforces FK constraints natively. */
|
|
140
149
|
supportsNativeForeignKeys(): boolean;
|
|
141
150
|
prepareId(id: unknown, _fieldType: unknown): unknown;
|
|
@@ -146,8 +155,12 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
146
155
|
* server's time zone tables (probed per zone in `aggregate()`).
|
|
147
156
|
*/
|
|
148
157
|
calendarBucketUnits(): ReadonlySet<BucketUnit>;
|
|
149
|
-
/** Every aggregate function
|
|
158
|
+
/** Every aggregate function: `countDistinct`, `first` and `last` included. */
|
|
150
159
|
aggregateFns(): ReadonlySet<AggregateFn>;
|
|
160
|
+
/** Arithmetic in an aggregate `$select` (`{ $expr }`, `{ $fn, $expr }`). */
|
|
161
|
+
supportsAggregateExpressions(): boolean;
|
|
162
|
+
/** Computed view columns and first-row joins. */
|
|
163
|
+
viewCapabilities(): ReadonlySet<TViewCapability>;
|
|
151
164
|
onBeforeFlatten(_type: unknown): void;
|
|
152
165
|
onFieldScanned(field: string, _type: unknown, metadata: TMetadataMap<AtscriptMetadata>): void;
|
|
153
166
|
getDesiredTableOptions(): TExistingTableOption[];
|
|
@@ -165,15 +178,89 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
165
178
|
*
|
|
166
179
|
* MySQL uses numeric error codes:
|
|
167
180
|
* - 1062 = ER_DUP_ENTRY (unique constraint violation)
|
|
181
|
+
* - 1586 = ER_DUP_ENTRY_WITH_KEY_NAME (same, for a multi-row INSERT)
|
|
168
182
|
* - 1451 = ER_ROW_IS_REFERENCED_2 (FK violation on delete)
|
|
169
183
|
* - 1452 = ER_NO_REFERENCED_ROW_2 (FK violation on insert/update)
|
|
170
184
|
*/
|
|
171
185
|
private _wrapConstraintError;
|
|
186
|
+
/** Rethrows `error` as a structured `DbError` when it is a unique / FK violation, else as is. */
|
|
187
|
+
private _mapConstraintError;
|
|
172
188
|
private _mapFkError;
|
|
173
189
|
insertOne(data: Record<string, unknown>): Promise<TDbInsertResult>;
|
|
174
190
|
insertMany(data: Array<Record<string, unknown>>): Promise<TDbInsertManyResult>;
|
|
191
|
+
/** Physical column of the single-column AUTO_INCREMENT primary key, if the table has one. */
|
|
192
|
+
private _autoIncrementPk;
|
|
193
|
+
/**
|
|
194
|
+
* Splits `rows` into runs that each map to ONE statement with a derivable
|
|
195
|
+
* id sequence. MySQL reports only the first GENERATED id of a statement, and
|
|
196
|
+
* an explicit auto-increment value above the counter bumps the counter, so a
|
|
197
|
+
* statement mixing explicit and generated PKs cannot be mapped by
|
|
198
|
+
* `insertId + i * step`: the chunk is cut into CONSECUTIVE runs of one kind,
|
|
199
|
+
* executed in input order (so "an earlier row wins" a unique collision, even
|
|
200
|
+
* under a case-insensitive collation). A table without an AUTO_INCREMENT PK,
|
|
201
|
+
* or a chunk of one kind, stays one group.
|
|
202
|
+
*/
|
|
203
|
+
private _idGroups;
|
|
204
|
+
/** The session `sql_mode` text on the current connection (`undefined` when the server returns no row). */
|
|
205
|
+
private _sessionSqlMode;
|
|
206
|
+
/** The {@link TZeroIsExplicit} of one `insertMany` / `insertManyIgnore` call. */
|
|
207
|
+
private _zeroIsExplicit;
|
|
208
|
+
/**
|
|
209
|
+
* Warns ONCE per driver when the session `sql_mode` is not strict: writes
|
|
210
|
+
* (insert-ignore included) assume `STRICT_TRANS_TABLES` / `STRICT_ALL_TABLES`
|
|
211
|
+
* (the MySQL 8 default); a non-strict mode coerces a NOT NULL violation to the
|
|
212
|
+
* column default instead of failing. Only probes when the adapter has a logger.
|
|
213
|
+
*/
|
|
214
|
+
private _warnNonStrictMode;
|
|
215
|
+
/** The {@link TIncrementStep} of one `insertMany` / `insertManyIgnore` call. */
|
|
216
|
+
private _incrementStep;
|
|
217
|
+
/**
|
|
218
|
+
* Ids of the rows of ONE successful multi-row INSERT of a homogeneous
|
|
219
|
+
* {@link _idGroups} group: generated rows get ids from `insertId` stepping by
|
|
220
|
+
* the session's `@@auto_increment_increment` (consecutive within one
|
|
221
|
+
* statement under `innodb_autoinc_lock_mode` 0 / 1, and — for a known row
|
|
222
|
+
* count — mode 2, the 8.0 default), explicit rows keep their own value.
|
|
223
|
+
*/
|
|
224
|
+
private _groupInsertedIds;
|
|
225
|
+
supportsInsertIgnore(): boolean;
|
|
226
|
+
/**
|
|
227
|
+
* Per chunk: ONE optimistic multi-row INSERT (an all-new batch costs a single
|
|
228
|
+
* statement). Only when it hits a duplicate key (errno 1062 / 1586) does ONE
|
|
229
|
+
* SELECT of the chunk's primary / unique key tuples find the stored rows
|
|
230
|
+
* (skipped as conflicts) and the survivors go in as one more multi-row
|
|
231
|
+
* INSERT — a dense-duplicate chunk is three statements, never O(rows). Only
|
|
232
|
+
* if that INSERT still collides (a concurrent writer raced in, or a
|
|
233
|
+
* collation-equal value the exact-match pre-check missed) are the survivors
|
|
234
|
+
* bisected: each half is retried, recursively, and a single row that still
|
|
235
|
+
* collides is skipped. A failed statement is rolled back by InnoDB alone, so
|
|
236
|
+
* the transaction stays usable. A chunk mixing explicit and generated
|
|
237
|
+
* auto-increment ids is processed as one such sequence per consecutive run of a kind. Deliberately
|
|
238
|
+
* NOT `INSERT IGNORE` (it would downgrade NOT NULL / FK / truncation errors
|
|
239
|
+
* to warnings) and not `ON DUPLICATE KEY UPDATE` (a no-op update is
|
|
240
|
+
* indistinguishable from an insert in the affected-rows count).
|
|
241
|
+
*/
|
|
242
|
+
insertManyIgnore(data: Array<Record<string, unknown>>): Promise<TDbInsertIgnoreSlot[]>;
|
|
243
|
+
/**
|
|
244
|
+
* Indices of `rows` whose primary / unique-index key tuple is already stored
|
|
245
|
+
* (a row with a null / missing key component never collides). One SELECT
|
|
246
|
+
* covers every key set; it is split only to stay under the parameter limit.
|
|
247
|
+
* Skipped entirely when no row carries a key value (generated PK, no unique
|
|
248
|
+
* index values).
|
|
249
|
+
*/
|
|
250
|
+
private _findStoredKeyConflicts;
|
|
251
|
+
/** Optimistic INSERT of a group, then pre-check + survivor INSERT (+ bisect on a race). */
|
|
252
|
+
private _insertIgnoringGroup;
|
|
253
|
+
/** Retries the halves of a group whose one INSERT is known to collide; a lone colliding row is skipped. */
|
|
254
|
+
private _bisectGroup;
|
|
255
|
+
/** ONE INSERT of a group's rows; `undefined` on a duplicate key (errno 1062 / 1586). */
|
|
256
|
+
private _tryInsertGroup;
|
|
175
257
|
findOne(query: DbQuery): Promise<Record<string, unknown> | null>;
|
|
176
258
|
findMany(query: DbQuery): Promise<Array<Record<string, unknown>>>;
|
|
259
|
+
/**
|
|
260
|
+
* `$skip` / `$limit` per partition in one statement: a `ROW_NUMBER()`
|
|
261
|
+
* window over `partitionBy` (the generic `$with` loader's per-parent page).
|
|
262
|
+
*/
|
|
263
|
+
findManyPerPartition(query: DbQuery, partitionBy: readonly string[]): Promise<Array<Record<string, unknown>>>;
|
|
177
264
|
count(query: DbQuery): Promise<number>;
|
|
178
265
|
aggregate(query: DbQuery): Promise<Array<Record<string, unknown>>>;
|
|
179
266
|
/**
|
|
@@ -186,6 +273,26 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
186
273
|
* expression skips `CONVERT_TZ`. Successes are cached per driver.
|
|
187
274
|
*/
|
|
188
275
|
private _ensureBucketZone;
|
|
276
|
+
/**
|
|
277
|
+
* The WHERE of an UPDATE / DELETE on this table. MySQL rejects a statement
|
|
278
|
+
* whose WHERE reads the mutated table in a subquery (error 1093,
|
|
279
|
+
* `ER_UPDATE_TABLE_USED`) — what a relational predicate (`$some` / `$none`)
|
|
280
|
+
* does when its target or junction table, at any nesting level, is this
|
|
281
|
+
* table (a self relation such as `parent`). Such a filter is re-keyed on the
|
|
282
|
+
* primary key through a materialized derived table:
|
|
283
|
+
*
|
|
284
|
+
* ```sql
|
|
285
|
+
* WHERE (<pk…>) IN (SELECT * FROM (SELECT DISTINCT <pk…> FROM t WHERE <where>) AS `_rfm`)
|
|
286
|
+
* ```
|
|
287
|
+
*
|
|
288
|
+
* `DISTINCT` keeps the optimizer from merging the derived table back into
|
|
289
|
+
* the statement. Every other filter renders as-is.
|
|
290
|
+
*
|
|
291
|
+
* @since 0.1.147
|
|
292
|
+
*/
|
|
293
|
+
private _mutationWhere;
|
|
294
|
+
/** `true` when a relational predicate of `filter` (nested ones included) reads this table. */
|
|
295
|
+
private _filterReadsOwnTable;
|
|
189
296
|
updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number): Promise<TDbUpdateResult>;
|
|
190
297
|
updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
|
|
191
298
|
replaceOne(filter: FilterExpr, data: Record<string, unknown>, expectedVersion?: number): Promise<TDbUpdateResult>;
|
|
@@ -284,6 +391,18 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
284
391
|
}
|
|
285
392
|
//#endregion
|
|
286
393
|
//#region src/mysql2-driver.d.ts
|
|
394
|
+
/** Options of {@link Mysql2Driver}. */
|
|
395
|
+
interface TMysql2DriverOptions {
|
|
396
|
+
/**
|
|
397
|
+
* Add `STRICT_TRANS_TABLES` to the session `sql_mode` of every new pool
|
|
398
|
+
* connection (default `true`, since 0.1.148). The server's own modes are
|
|
399
|
+
* kept — the mode is appended, never replaced. A non-strict server (Amazon
|
|
400
|
+
* RDS defaults to `NO_ENGINE_SUBSTITUTION`) otherwise coerces a `NOT NULL`,
|
|
401
|
+
* out-of-range or too-long value instead of failing the write. Pass `false`
|
|
402
|
+
* to keep the server's `sql_mode` untouched.
|
|
403
|
+
*/
|
|
404
|
+
strictMode?: boolean;
|
|
405
|
+
}
|
|
287
406
|
/**
|
|
288
407
|
* {@link TMysqlDriver} implementation backed by `mysql2/promise`.
|
|
289
408
|
*
|
|
@@ -311,6 +430,11 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
311
430
|
* const driver = new Mysql2Driver(pool)
|
|
312
431
|
* ```
|
|
313
432
|
*
|
|
433
|
+
* Every new pool connection gets `STRICT_TRANS_TABLES` appended to its session
|
|
434
|
+
* `sql_mode` (since 0.1.148); pass `{ strictMode: false }` as the second
|
|
435
|
+
* argument to opt out. A pre-created `Pool` is covered too: connections it
|
|
436
|
+
* opened earlier get the statement when they are first acquired through it.
|
|
437
|
+
*
|
|
314
438
|
* Requires `mysql2` to be installed:
|
|
315
439
|
* ```bash
|
|
316
440
|
* pnpm add mysql2
|
|
@@ -319,13 +443,15 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
319
443
|
declare class Mysql2Driver implements TMysqlDriver {
|
|
320
444
|
private pool;
|
|
321
445
|
private poolInit;
|
|
322
|
-
constructor(poolOrConfig: string | import("mysql2/promise").Pool | import("mysql2/promise").PoolOptions);
|
|
446
|
+
constructor(poolOrConfig: string | import("mysql2/promise").Pool | import("mysql2/promise").PoolOptions, options?: TMysql2DriverOptions);
|
|
323
447
|
private getPool;
|
|
324
448
|
run(sql: string, params?: unknown[]): Promise<TMysqlRunResult>;
|
|
325
449
|
all<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
326
450
|
get<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T | null>;
|
|
327
451
|
exec(sql: string): Promise<void>;
|
|
328
452
|
getConnection(): Promise<TMysqlConnection>;
|
|
453
|
+
private _closing?;
|
|
454
|
+
/** Idempotent: every call returns the first call's promise. */
|
|
329
455
|
close(): Promise<void>;
|
|
330
456
|
}
|
|
331
457
|
//#endregion
|
|
@@ -335,17 +461,24 @@ declare class Mysql2Driver implements TMysqlDriver {
|
|
|
335
461
|
*
|
|
336
462
|
* @returns `{ sql, params }` — the WHERE clause (without "WHERE") and bound params.
|
|
337
463
|
* Returns `{ sql: '1=1', params: [] }` for empty/null filters.
|
|
464
|
+
*
|
|
465
|
+
* Relational predicates (`$some` / `$none`) render as correlated `EXISTS`
|
|
466
|
+
* subqueries referencing the outer table by `opts.qualifier` (default: the
|
|
467
|
+
* source table) — a statement with an aliased FROM must pass its alias.
|
|
468
|
+
*
|
|
469
|
+
* @param opts - since 0.1.147
|
|
338
470
|
*/
|
|
339
|
-
declare function buildWhere(filter: FilterExpr$1): TSqlFragment$1;
|
|
471
|
+
declare function buildWhere(filter: FilterExpr$1, opts?: TFilterVisitorOptions): TSqlFragment$1;
|
|
340
472
|
//#endregion
|
|
341
473
|
//#region src/index.d.ts
|
|
342
474
|
/**
|
|
343
475
|
* Creates a {@link DbSpace} backed by a MySQL connection pool.
|
|
344
476
|
*
|
|
345
477
|
* @param uri - MySQL connection URI (e.g., `mysql://root@localhost:3306/mydb`)
|
|
346
|
-
* @param options - Additional pool options passed to mysql2.
|
|
478
|
+
* @param options - Additional pool options passed to mysql2. `strictMode: false`
|
|
479
|
+
* (not a pool option) opts out of the per-session `STRICT_TRANS_TABLES` ({@link Mysql2Driver}).
|
|
347
480
|
* @returns A `DbSpace` that creates `MysqlAdapter` instances per table.
|
|
348
481
|
*/
|
|
349
482
|
declare function createAdapter(uri: string, options?: Record<string, unknown>): DbSpace;
|
|
350
483
|
//#endregion
|
|
351
|
-
export { Mysql2Driver, MysqlAdapter, type TMysqlConnection, type TMysqlDriver, type TMysqlRunResult, type TSqlFragment, buildWhere, createAdapter };
|
|
484
|
+
export { Mysql2Driver, MysqlAdapter, type TMysql2DriverOptions, type TMysqlConnection, type TMysqlDriver, type TMysqlRunResult, type TSqlFragment, buildWhere, createAdapter };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TExistingTableOption, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TTableOptionDiff, TValueFormatterPair } from "@atscript/db";
|
|
2
|
-
import { TSqlFragment, TSqlFragment as TSqlFragment$1 } from "@atscript/db-sql-tools";
|
|
1
|
+
import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TExistingTableOption, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TTableOptionDiff, TValueFormatterPair, TViewCapability } from "@atscript/db";
|
|
2
|
+
import { TFilterVisitorOptions, TSqlFragment, TSqlFragment as TSqlFragment$1 } from "@atscript/db-sql-tools";
|
|
3
3
|
import { FilterExpr as FilterExpr$1 } from "@uniqu/core";
|
|
4
4
|
import { TMetadataMap } from "@atscript/typescript/utils";
|
|
5
5
|
|
|
@@ -136,6 +136,15 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
136
136
|
* to `NO_ENGINE_SUBSTITUTION`) would turn `'abc'` into `0` silently.
|
|
137
137
|
*/
|
|
138
138
|
private _withStrictSession;
|
|
139
|
+
/**
|
|
140
|
+
* Relational predicates (`$some` / `$none`) render as correlated
|
|
141
|
+
* `[NOT] EXISTS` subqueries — in reads and in mutation filters alike.
|
|
142
|
+
* An UPDATE / DELETE whose predicate reads the mutated table itself is
|
|
143
|
+
* rewritten through a materialized derived table (MySQL error 1093).
|
|
144
|
+
*
|
|
145
|
+
* @since 0.1.147
|
|
146
|
+
*/
|
|
147
|
+
supportsRelationFilters(_mode: "read" | "write"): boolean;
|
|
139
148
|
/** MySQL InnoDB enforces FK constraints natively. */
|
|
140
149
|
supportsNativeForeignKeys(): boolean;
|
|
141
150
|
prepareId(id: unknown, _fieldType: unknown): unknown;
|
|
@@ -146,8 +155,12 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
146
155
|
* server's time zone tables (probed per zone in `aggregate()`).
|
|
147
156
|
*/
|
|
148
157
|
calendarBucketUnits(): ReadonlySet<BucketUnit>;
|
|
149
|
-
/** Every aggregate function
|
|
158
|
+
/** Every aggregate function: `countDistinct`, `first` and `last` included. */
|
|
150
159
|
aggregateFns(): ReadonlySet<AggregateFn>;
|
|
160
|
+
/** Arithmetic in an aggregate `$select` (`{ $expr }`, `{ $fn, $expr }`). */
|
|
161
|
+
supportsAggregateExpressions(): boolean;
|
|
162
|
+
/** Computed view columns and first-row joins. */
|
|
163
|
+
viewCapabilities(): ReadonlySet<TViewCapability>;
|
|
151
164
|
onBeforeFlatten(_type: unknown): void;
|
|
152
165
|
onFieldScanned(field: string, _type: unknown, metadata: TMetadataMap<AtscriptMetadata>): void;
|
|
153
166
|
getDesiredTableOptions(): TExistingTableOption[];
|
|
@@ -165,15 +178,89 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
165
178
|
*
|
|
166
179
|
* MySQL uses numeric error codes:
|
|
167
180
|
* - 1062 = ER_DUP_ENTRY (unique constraint violation)
|
|
181
|
+
* - 1586 = ER_DUP_ENTRY_WITH_KEY_NAME (same, for a multi-row INSERT)
|
|
168
182
|
* - 1451 = ER_ROW_IS_REFERENCED_2 (FK violation on delete)
|
|
169
183
|
* - 1452 = ER_NO_REFERENCED_ROW_2 (FK violation on insert/update)
|
|
170
184
|
*/
|
|
171
185
|
private _wrapConstraintError;
|
|
186
|
+
/** Rethrows `error` as a structured `DbError` when it is a unique / FK violation, else as is. */
|
|
187
|
+
private _mapConstraintError;
|
|
172
188
|
private _mapFkError;
|
|
173
189
|
insertOne(data: Record<string, unknown>): Promise<TDbInsertResult>;
|
|
174
190
|
insertMany(data: Array<Record<string, unknown>>): Promise<TDbInsertManyResult>;
|
|
191
|
+
/** Physical column of the single-column AUTO_INCREMENT primary key, if the table has one. */
|
|
192
|
+
private _autoIncrementPk;
|
|
193
|
+
/**
|
|
194
|
+
* Splits `rows` into runs that each map to ONE statement with a derivable
|
|
195
|
+
* id sequence. MySQL reports only the first GENERATED id of a statement, and
|
|
196
|
+
* an explicit auto-increment value above the counter bumps the counter, so a
|
|
197
|
+
* statement mixing explicit and generated PKs cannot be mapped by
|
|
198
|
+
* `insertId + i * step`: the chunk is cut into CONSECUTIVE runs of one kind,
|
|
199
|
+
* executed in input order (so "an earlier row wins" a unique collision, even
|
|
200
|
+
* under a case-insensitive collation). A table without an AUTO_INCREMENT PK,
|
|
201
|
+
* or a chunk of one kind, stays one group.
|
|
202
|
+
*/
|
|
203
|
+
private _idGroups;
|
|
204
|
+
/** The session `sql_mode` text on the current connection (`undefined` when the server returns no row). */
|
|
205
|
+
private _sessionSqlMode;
|
|
206
|
+
/** The {@link TZeroIsExplicit} of one `insertMany` / `insertManyIgnore` call. */
|
|
207
|
+
private _zeroIsExplicit;
|
|
208
|
+
/**
|
|
209
|
+
* Warns ONCE per driver when the session `sql_mode` is not strict: writes
|
|
210
|
+
* (insert-ignore included) assume `STRICT_TRANS_TABLES` / `STRICT_ALL_TABLES`
|
|
211
|
+
* (the MySQL 8 default); a non-strict mode coerces a NOT NULL violation to the
|
|
212
|
+
* column default instead of failing. Only probes when the adapter has a logger.
|
|
213
|
+
*/
|
|
214
|
+
private _warnNonStrictMode;
|
|
215
|
+
/** The {@link TIncrementStep} of one `insertMany` / `insertManyIgnore` call. */
|
|
216
|
+
private _incrementStep;
|
|
217
|
+
/**
|
|
218
|
+
* Ids of the rows of ONE successful multi-row INSERT of a homogeneous
|
|
219
|
+
* {@link _idGroups} group: generated rows get ids from `insertId` stepping by
|
|
220
|
+
* the session's `@@auto_increment_increment` (consecutive within one
|
|
221
|
+
* statement under `innodb_autoinc_lock_mode` 0 / 1, and — for a known row
|
|
222
|
+
* count — mode 2, the 8.0 default), explicit rows keep their own value.
|
|
223
|
+
*/
|
|
224
|
+
private _groupInsertedIds;
|
|
225
|
+
supportsInsertIgnore(): boolean;
|
|
226
|
+
/**
|
|
227
|
+
* Per chunk: ONE optimistic multi-row INSERT (an all-new batch costs a single
|
|
228
|
+
* statement). Only when it hits a duplicate key (errno 1062 / 1586) does ONE
|
|
229
|
+
* SELECT of the chunk's primary / unique key tuples find the stored rows
|
|
230
|
+
* (skipped as conflicts) and the survivors go in as one more multi-row
|
|
231
|
+
* INSERT — a dense-duplicate chunk is three statements, never O(rows). Only
|
|
232
|
+
* if that INSERT still collides (a concurrent writer raced in, or a
|
|
233
|
+
* collation-equal value the exact-match pre-check missed) are the survivors
|
|
234
|
+
* bisected: each half is retried, recursively, and a single row that still
|
|
235
|
+
* collides is skipped. A failed statement is rolled back by InnoDB alone, so
|
|
236
|
+
* the transaction stays usable. A chunk mixing explicit and generated
|
|
237
|
+
* auto-increment ids is processed as one such sequence per consecutive run of a kind. Deliberately
|
|
238
|
+
* NOT `INSERT IGNORE` (it would downgrade NOT NULL / FK / truncation errors
|
|
239
|
+
* to warnings) and not `ON DUPLICATE KEY UPDATE` (a no-op update is
|
|
240
|
+
* indistinguishable from an insert in the affected-rows count).
|
|
241
|
+
*/
|
|
242
|
+
insertManyIgnore(data: Array<Record<string, unknown>>): Promise<TDbInsertIgnoreSlot[]>;
|
|
243
|
+
/**
|
|
244
|
+
* Indices of `rows` whose primary / unique-index key tuple is already stored
|
|
245
|
+
* (a row with a null / missing key component never collides). One SELECT
|
|
246
|
+
* covers every key set; it is split only to stay under the parameter limit.
|
|
247
|
+
* Skipped entirely when no row carries a key value (generated PK, no unique
|
|
248
|
+
* index values).
|
|
249
|
+
*/
|
|
250
|
+
private _findStoredKeyConflicts;
|
|
251
|
+
/** Optimistic INSERT of a group, then pre-check + survivor INSERT (+ bisect on a race). */
|
|
252
|
+
private _insertIgnoringGroup;
|
|
253
|
+
/** Retries the halves of a group whose one INSERT is known to collide; a lone colliding row is skipped. */
|
|
254
|
+
private _bisectGroup;
|
|
255
|
+
/** ONE INSERT of a group's rows; `undefined` on a duplicate key (errno 1062 / 1586). */
|
|
256
|
+
private _tryInsertGroup;
|
|
175
257
|
findOne(query: DbQuery): Promise<Record<string, unknown> | null>;
|
|
176
258
|
findMany(query: DbQuery): Promise<Array<Record<string, unknown>>>;
|
|
259
|
+
/**
|
|
260
|
+
* `$skip` / `$limit` per partition in one statement: a `ROW_NUMBER()`
|
|
261
|
+
* window over `partitionBy` (the generic `$with` loader's per-parent page).
|
|
262
|
+
*/
|
|
263
|
+
findManyPerPartition(query: DbQuery, partitionBy: readonly string[]): Promise<Array<Record<string, unknown>>>;
|
|
177
264
|
count(query: DbQuery): Promise<number>;
|
|
178
265
|
aggregate(query: DbQuery): Promise<Array<Record<string, unknown>>>;
|
|
179
266
|
/**
|
|
@@ -186,6 +273,26 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
186
273
|
* expression skips `CONVERT_TZ`. Successes are cached per driver.
|
|
187
274
|
*/
|
|
188
275
|
private _ensureBucketZone;
|
|
276
|
+
/**
|
|
277
|
+
* The WHERE of an UPDATE / DELETE on this table. MySQL rejects a statement
|
|
278
|
+
* whose WHERE reads the mutated table in a subquery (error 1093,
|
|
279
|
+
* `ER_UPDATE_TABLE_USED`) — what a relational predicate (`$some` / `$none`)
|
|
280
|
+
* does when its target or junction table, at any nesting level, is this
|
|
281
|
+
* table (a self relation such as `parent`). Such a filter is re-keyed on the
|
|
282
|
+
* primary key through a materialized derived table:
|
|
283
|
+
*
|
|
284
|
+
* ```sql
|
|
285
|
+
* WHERE (<pk…>) IN (SELECT * FROM (SELECT DISTINCT <pk…> FROM t WHERE <where>) AS `_rfm`)
|
|
286
|
+
* ```
|
|
287
|
+
*
|
|
288
|
+
* `DISTINCT` keeps the optimizer from merging the derived table back into
|
|
289
|
+
* the statement. Every other filter renders as-is.
|
|
290
|
+
*
|
|
291
|
+
* @since 0.1.147
|
|
292
|
+
*/
|
|
293
|
+
private _mutationWhere;
|
|
294
|
+
/** `true` when a relational predicate of `filter` (nested ones included) reads this table. */
|
|
295
|
+
private _filterReadsOwnTable;
|
|
189
296
|
updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number): Promise<TDbUpdateResult>;
|
|
190
297
|
updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
|
|
191
298
|
replaceOne(filter: FilterExpr, data: Record<string, unknown>, expectedVersion?: number): Promise<TDbUpdateResult>;
|
|
@@ -284,6 +391,18 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
284
391
|
}
|
|
285
392
|
//#endregion
|
|
286
393
|
//#region src/mysql2-driver.d.ts
|
|
394
|
+
/** Options of {@link Mysql2Driver}. */
|
|
395
|
+
interface TMysql2DriverOptions {
|
|
396
|
+
/**
|
|
397
|
+
* Add `STRICT_TRANS_TABLES` to the session `sql_mode` of every new pool
|
|
398
|
+
* connection (default `true`, since 0.1.148). The server's own modes are
|
|
399
|
+
* kept — the mode is appended, never replaced. A non-strict server (Amazon
|
|
400
|
+
* RDS defaults to `NO_ENGINE_SUBSTITUTION`) otherwise coerces a `NOT NULL`,
|
|
401
|
+
* out-of-range or too-long value instead of failing the write. Pass `false`
|
|
402
|
+
* to keep the server's `sql_mode` untouched.
|
|
403
|
+
*/
|
|
404
|
+
strictMode?: boolean;
|
|
405
|
+
}
|
|
287
406
|
/**
|
|
288
407
|
* {@link TMysqlDriver} implementation backed by `mysql2/promise`.
|
|
289
408
|
*
|
|
@@ -311,6 +430,11 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
311
430
|
* const driver = new Mysql2Driver(pool)
|
|
312
431
|
* ```
|
|
313
432
|
*
|
|
433
|
+
* Every new pool connection gets `STRICT_TRANS_TABLES` appended to its session
|
|
434
|
+
* `sql_mode` (since 0.1.148); pass `{ strictMode: false }` as the second
|
|
435
|
+
* argument to opt out. A pre-created `Pool` is covered too: connections it
|
|
436
|
+
* opened earlier get the statement when they are first acquired through it.
|
|
437
|
+
*
|
|
314
438
|
* Requires `mysql2` to be installed:
|
|
315
439
|
* ```bash
|
|
316
440
|
* pnpm add mysql2
|
|
@@ -319,13 +443,15 @@ declare class MysqlAdapter extends BaseDbAdapter {
|
|
|
319
443
|
declare class Mysql2Driver implements TMysqlDriver {
|
|
320
444
|
private pool;
|
|
321
445
|
private poolInit;
|
|
322
|
-
constructor(poolOrConfig: string | import("mysql2/promise").Pool | import("mysql2/promise").PoolOptions);
|
|
446
|
+
constructor(poolOrConfig: string | import("mysql2/promise").Pool | import("mysql2/promise").PoolOptions, options?: TMysql2DriverOptions);
|
|
323
447
|
private getPool;
|
|
324
448
|
run(sql: string, params?: unknown[]): Promise<TMysqlRunResult>;
|
|
325
449
|
all<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
326
450
|
get<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T | null>;
|
|
327
451
|
exec(sql: string): Promise<void>;
|
|
328
452
|
getConnection(): Promise<TMysqlConnection>;
|
|
453
|
+
private _closing?;
|
|
454
|
+
/** Idempotent: every call returns the first call's promise. */
|
|
329
455
|
close(): Promise<void>;
|
|
330
456
|
}
|
|
331
457
|
//#endregion
|
|
@@ -335,17 +461,24 @@ declare class Mysql2Driver implements TMysqlDriver {
|
|
|
335
461
|
*
|
|
336
462
|
* @returns `{ sql, params }` — the WHERE clause (without "WHERE") and bound params.
|
|
337
463
|
* Returns `{ sql: '1=1', params: [] }` for empty/null filters.
|
|
464
|
+
*
|
|
465
|
+
* Relational predicates (`$some` / `$none`) render as correlated `EXISTS`
|
|
466
|
+
* subqueries referencing the outer table by `opts.qualifier` (default: the
|
|
467
|
+
* source table) — a statement with an aliased FROM must pass its alias.
|
|
468
|
+
*
|
|
469
|
+
* @param opts - since 0.1.147
|
|
338
470
|
*/
|
|
339
|
-
declare function buildWhere(filter: FilterExpr$1): TSqlFragment$1;
|
|
471
|
+
declare function buildWhere(filter: FilterExpr$1, opts?: TFilterVisitorOptions): TSqlFragment$1;
|
|
340
472
|
//#endregion
|
|
341
473
|
//#region src/index.d.ts
|
|
342
474
|
/**
|
|
343
475
|
* Creates a {@link DbSpace} backed by a MySQL connection pool.
|
|
344
476
|
*
|
|
345
477
|
* @param uri - MySQL connection URI (e.g., `mysql://root@localhost:3306/mydb`)
|
|
346
|
-
* @param options - Additional pool options passed to mysql2.
|
|
478
|
+
* @param options - Additional pool options passed to mysql2. `strictMode: false`
|
|
479
|
+
* (not a pool option) opts out of the per-session `STRICT_TRANS_TABLES` ({@link Mysql2Driver}).
|
|
347
480
|
* @returns A `DbSpace` that creates `MysqlAdapter` instances per table.
|
|
348
481
|
*/
|
|
349
482
|
declare function createAdapter(uri: string, options?: Record<string, unknown>): DbSpace;
|
|
350
483
|
//#endregion
|
|
351
|
-
export { Mysql2Driver, MysqlAdapter, type TMysqlConnection, type TMysqlDriver, type TMysqlRunResult, type TSqlFragment, buildWhere, createAdapter };
|
|
484
|
+
export { Mysql2Driver, MysqlAdapter, type TMysql2DriverOptions, type TMysqlConnection, type TMysqlDriver, type TMysqlRunResult, type TSqlFragment, buildWhere, createAdapter };
|