@nextlyhq/adapter-drizzle 0.0.2-alpha.6 → 0.0.2-alpha.60
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/{adapter-nvlxFkF-.d.cts → adapter-BG7MnOUw.d.cts} +194 -58
- package/dist/{adapter-BxJVtttb.d.ts → adapter-DjPoLjBK.d.ts} +194 -58
- package/dist/{error-um1d_3Uo.d.cts → error-BrdknH2s.d.cts} +18 -1
- package/dist/{error-um1d_3Uo.d.ts → error-BrdknH2s.d.ts} +18 -1
- package/dist/index.cjs +461 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.mjs +462 -41
- package/dist/index.mjs.map +1 -1
- package/dist/{migration-BbO5meEV.d.cts → migration-BUc56kip.d.cts} +78 -0
- package/dist/{migration-Qe70wDOC.d.ts → migration-Bj84e15B.d.ts} +78 -0
- package/dist/migrations.d.cts +5 -4
- package/dist/migrations.d.ts +5 -4
- package/dist/types/index.cjs +8 -0
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +3 -2
- package/dist/types/index.d.ts +3 -2
- package/dist/types/index.mjs +8 -1
- package/dist/types/index.mjs.map +1 -1
- package/dist/version-check.d.cts +1 -1
- package/dist/version-check.d.ts +1 -1
- package/package.json +10 -10
package/dist/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@ var drizzleOrm = require('drizzle-orm');
|
|
|
4
4
|
|
|
5
5
|
// src/adapter.ts
|
|
6
6
|
function buildDrizzleWhere(table, where) {
|
|
7
|
-
const columns = drizzleOrm.
|
|
7
|
+
const columns = drizzleOrm.getColumns(table);
|
|
8
8
|
return processWhereClause(columns, where);
|
|
9
9
|
}
|
|
10
10
|
function processWhereClause(columns, where) {
|
|
@@ -108,6 +108,51 @@ function createDatabaseError(options) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
// src/adapter.ts
|
|
111
|
+
var DATE_DATA_TYPE = /(?:^|\s)date$/;
|
|
112
|
+
var WALL_CLOCK_ALIAS_STEM = "__nx_wc";
|
|
113
|
+
var WALL_CLOCK_PATTERN = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?$/;
|
|
114
|
+
function parseWallClockAsUtc(text) {
|
|
115
|
+
const match = WALL_CLOCK_PATTERN.exec(text.trim());
|
|
116
|
+
if (!match) return void 0;
|
|
117
|
+
const [, year, month, day, hour, minute, second, fraction = ""] = match;
|
|
118
|
+
const millis = Number(`${fraction}000`.slice(0, 3));
|
|
119
|
+
const parsed = new Date(
|
|
120
|
+
Date.UTC(
|
|
121
|
+
Number(year),
|
|
122
|
+
Number(month) - 1,
|
|
123
|
+
Number(day),
|
|
124
|
+
Number(hour),
|
|
125
|
+
Number(minute),
|
|
126
|
+
Number(second),
|
|
127
|
+
millis
|
|
128
|
+
)
|
|
129
|
+
);
|
|
130
|
+
if (Number(year) < 100) parsed.setUTCFullYear(Number(year));
|
|
131
|
+
return parsed;
|
|
132
|
+
}
|
|
133
|
+
function isDateColumn(colDef) {
|
|
134
|
+
if (!colDef || typeof colDef !== "object") return false;
|
|
135
|
+
const candidate = colDef;
|
|
136
|
+
return typeof candidate.mapFromDriverValue === "function" && typeof candidate.mapToDriverValue === "function" && DATE_DATA_TYPE.test(String(candidate.dataType));
|
|
137
|
+
}
|
|
138
|
+
function carriesTimeZone(colDef) {
|
|
139
|
+
return colDef.withTimezone === true;
|
|
140
|
+
}
|
|
141
|
+
function affectedRowCount(result) {
|
|
142
|
+
if (Array.isArray(result)) {
|
|
143
|
+
const header = result[0];
|
|
144
|
+
if (header && typeof header.affectedRows === "number") {
|
|
145
|
+
return header.affectedRows;
|
|
146
|
+
}
|
|
147
|
+
return result.length;
|
|
148
|
+
}
|
|
149
|
+
const record = result;
|
|
150
|
+
const rowCount = record?.rowCount;
|
|
151
|
+
if (typeof rowCount === "number") return rowCount;
|
|
152
|
+
const changes = record?.changes;
|
|
153
|
+
if (typeof changes === "number") return changes;
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
111
156
|
var DrizzleAdapter = class {
|
|
112
157
|
// ============================================================
|
|
113
158
|
// Drizzle Query API Support
|
|
@@ -136,6 +181,45 @@ var DrizzleAdapter = class {
|
|
|
136
181
|
getTableObject(tableName) {
|
|
137
182
|
return this.tableResolver?.getTable(tableName) ?? null;
|
|
138
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Run a Drizzle-built statement and return its rows.
|
|
186
|
+
*
|
|
187
|
+
* @remarks
|
|
188
|
+
* The CRUD methods resolve their table through the schema registry and reject
|
|
189
|
+
* any name it does not declare, which leaves no way to read from a table the
|
|
190
|
+
* ORM does not know — one mid-rename, above all — except by assembling SQL and
|
|
191
|
+
* quoting identifiers by hand. This takes Drizzle's `sql` template instead, so
|
|
192
|
+
* the dialect in use decides the quoting and the parameter binding.
|
|
193
|
+
*
|
|
194
|
+
* Concrete rather than abstract so existing adapters keep working unchanged.
|
|
195
|
+
* The three drivers disagree about both the call and the result: node-postgres
|
|
196
|
+
* returns `{ rows }`, mysql2 a `[rows, fields]` tuple, and better-sqlite3 has
|
|
197
|
+
* no `execute` at all and answers `all`. Keeping that here rather than at each
|
|
198
|
+
* call site is the point — a caller reasoning about it would be reasoning
|
|
199
|
+
* about a driver it cannot see.
|
|
200
|
+
*
|
|
201
|
+
* @param statement - Drizzle `sql` template to run
|
|
202
|
+
* @returns Rows the statement produced
|
|
203
|
+
*/
|
|
204
|
+
async queryStatement(statement) {
|
|
205
|
+
if (this.getCapabilities().dialect === "sqlite") {
|
|
206
|
+
const db2 = this.getDrizzle();
|
|
207
|
+
return await db2.all(statement);
|
|
208
|
+
}
|
|
209
|
+
const db = this.getDrizzle();
|
|
210
|
+
const result = await db.execute(statement);
|
|
211
|
+
if (Array.isArray(result)) {
|
|
212
|
+
return Array.isArray(result[0]) ? result[0] : result;
|
|
213
|
+
}
|
|
214
|
+
if (typeof result === "object" && result !== null && Array.isArray(result.rows)) {
|
|
215
|
+
return result.rows;
|
|
216
|
+
}
|
|
217
|
+
throw this.createDatabaseError(
|
|
218
|
+
"query",
|
|
219
|
+
"Drizzle statement returned a result shape this adapter does not recognise; refusing to report it as an empty result.",
|
|
220
|
+
void 0
|
|
221
|
+
);
|
|
222
|
+
}
|
|
139
223
|
/**
|
|
140
224
|
* Map data keys from SQL column names (snake_case) to Drizzle JS property names (camelCase).
|
|
141
225
|
* Drizzle schemas define columns as e.g. `createdAt: timestamp("created_at")` — the JS
|
|
@@ -175,6 +259,281 @@ var DrizzleAdapter = class {
|
|
|
175
259
|
}
|
|
176
260
|
return mapped;
|
|
177
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Map data keys from Drizzle JS property names to SQL column names for the
|
|
264
|
+
* raw-SQL transaction insert path. The transaction context builds INSERT
|
|
265
|
+
* statements from Object.keys(data) used directly as column identifiers, so a
|
|
266
|
+
* table whose Drizzle property names differ from its SQL column names
|
|
267
|
+
* (camelCase core tables like nextly_versions) needs its keys translated
|
|
268
|
+
* first. For tables whose property names already equal their column names
|
|
269
|
+
* (the dynamic dc_/single_/comp_ tables) every lookup is identity, so
|
|
270
|
+
* existing callers are unaffected.
|
|
271
|
+
*/
|
|
272
|
+
mapKeysToSqlColumns(tableObj, data) {
|
|
273
|
+
if (!tableObj || typeof tableObj !== "object") return data;
|
|
274
|
+
const jsToSql = /* @__PURE__ */ new Map();
|
|
275
|
+
for (const [jsName, colDef] of Object.entries(
|
|
276
|
+
tableObj
|
|
277
|
+
)) {
|
|
278
|
+
if (colDef && typeof colDef === "object" && "name" in colDef && typeof colDef.name === "string") {
|
|
279
|
+
jsToSql.set(jsName, colDef.name);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (jsToSql.size === 0) return data;
|
|
283
|
+
const out = {};
|
|
284
|
+
for (const [key, value] of Object.entries(data)) {
|
|
285
|
+
out[jsToSql.get(key) ?? key] = value;
|
|
286
|
+
}
|
|
287
|
+
return out;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Map a list of column identifiers (Drizzle property names) to their SQL
|
|
291
|
+
* column names, for the raw-SQL transaction insert paths that build a
|
|
292
|
+
* RETURNING clause from `options.returning`. Same identity behavior as
|
|
293
|
+
* `mapKeysToSqlColumns`: names that are already SQL columns (the dynamic
|
|
294
|
+
* dc_/single_/comp_ tables) pass through unchanged.
|
|
295
|
+
*/
|
|
296
|
+
mapColumnNamesToSql(tableObj, names) {
|
|
297
|
+
if (!tableObj || typeof tableObj !== "object") return names;
|
|
298
|
+
const jsToSql = /* @__PURE__ */ new Map();
|
|
299
|
+
for (const [jsName, colDef] of Object.entries(
|
|
300
|
+
tableObj
|
|
301
|
+
)) {
|
|
302
|
+
if (colDef && typeof colDef === "object" && "name" in colDef && typeof colDef.name === "string") {
|
|
303
|
+
jsToSql.set(jsName, colDef.name);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (jsToSql.size === 0) return names;
|
|
307
|
+
return names.map((n) => jsToSql.get(n) ?? n);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Remap a raw-SQL result row's KEYS from SQL column names to Drizzle property
|
|
311
|
+
* names, so the raw-SQL transaction insert paths return the same key casing
|
|
312
|
+
* as the non-transactional (Drizzle) insert. Keys only - values are left
|
|
313
|
+
* untouched, so this does not change how JSON/date columns are decoded. For
|
|
314
|
+
* tables whose property names already equal their SQL columns (the dynamic
|
|
315
|
+
* dc_/single_/comp_ tables) every lookup is identity, so existing callers see
|
|
316
|
+
* no change.
|
|
317
|
+
*/
|
|
318
|
+
mapRowKeysToJs(tableObj, row) {
|
|
319
|
+
if (!tableObj || typeof tableObj !== "object" || !row || typeof row !== "object") {
|
|
320
|
+
return row;
|
|
321
|
+
}
|
|
322
|
+
const sqlToJs = /* @__PURE__ */ new Map();
|
|
323
|
+
for (const [jsName, colDef] of Object.entries(
|
|
324
|
+
tableObj
|
|
325
|
+
)) {
|
|
326
|
+
if (colDef && typeof colDef === "object" && "name" in colDef && typeof colDef.name === "string") {
|
|
327
|
+
sqlToJs.set(colDef.name, jsName);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (sqlToJs.size === 0) return row;
|
|
331
|
+
const out = {};
|
|
332
|
+
for (const [key, value] of Object.entries(row)) {
|
|
333
|
+
out[sqlToJs.get(key) ?? key] = value;
|
|
334
|
+
}
|
|
335
|
+
return out;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Decode a raw-SQL result row's DATE column values the way a Drizzle query
|
|
339
|
+
* would, so a write answers with the same representation a read of the same
|
|
340
|
+
* column gives.
|
|
341
|
+
*
|
|
342
|
+
* A Drizzle query path runs every value through its column definition; a
|
|
343
|
+
* raw-SQL path does not, so a dialect that stores a timestamp as a number
|
|
344
|
+
* answers a write with that number while every read answers a `Date`. Only
|
|
345
|
+
* date columns are touched, and a value that is already a `Date` is left
|
|
346
|
+
* alone, so a driver that decodes on its own is unaffected.
|
|
347
|
+
*
|
|
348
|
+
* Runs after {@link mapRowKeysToJs}: keys are Drizzle property names by then,
|
|
349
|
+
* which is what the table definition is keyed by.
|
|
350
|
+
*/
|
|
351
|
+
mapDateValuesFromDriver(tableObj, row) {
|
|
352
|
+
if (!tableObj || typeof tableObj !== "object" || !row || typeof row !== "object") {
|
|
353
|
+
return row;
|
|
354
|
+
}
|
|
355
|
+
let decoded;
|
|
356
|
+
for (const [jsName, colDef] of Object.entries(
|
|
357
|
+
tableObj
|
|
358
|
+
)) {
|
|
359
|
+
if (!isDateColumn(colDef)) continue;
|
|
360
|
+
const value = row[jsName];
|
|
361
|
+
if (value == null || value instanceof Date) continue;
|
|
362
|
+
decoded ??= { ...row };
|
|
363
|
+
decoded[jsName] = colDef.mapFromDriverValue(value);
|
|
364
|
+
}
|
|
365
|
+
return decoded ?? row;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Encode a row's DATE values the way a Drizzle query would before binding
|
|
369
|
+
* them, so what lands in the column does not depend on the server's timezone.
|
|
370
|
+
*
|
|
371
|
+
* A column declared without a time zone stores a wall clock and records
|
|
372
|
+
* nothing about which zone it belongs to, so both ends have to agree. Drizzle
|
|
373
|
+
* writes UTC and reads UTC. A driver handed a `Date` writes the LOCAL wall
|
|
374
|
+
* clock instead, and the same row then reads back shifted by the offset --
|
|
375
|
+
* on a UTC server the two agree and nothing is visibly wrong, which is why
|
|
376
|
+
* this survives CI.
|
|
377
|
+
*
|
|
378
|
+
* Only date columns are touched. A value that is not a `Date` is left as it
|
|
379
|
+
* is, so a caller that already encoded one is not encoded twice.
|
|
380
|
+
*
|
|
381
|
+
* Keys may be spelled either way at this point, so both the Drizzle property
|
|
382
|
+
* name and the SQL column name resolve.
|
|
383
|
+
*/
|
|
384
|
+
mapDateValuesToDriver(tableObj, data) {
|
|
385
|
+
if (!tableObj || typeof tableObj !== "object" || !data) return data;
|
|
386
|
+
const dateColumns = /* @__PURE__ */ new Map();
|
|
387
|
+
for (const [jsName, colDef] of Object.entries(
|
|
388
|
+
tableObj
|
|
389
|
+
)) {
|
|
390
|
+
if (!isDateColumn(colDef)) continue;
|
|
391
|
+
dateColumns.set(jsName, colDef);
|
|
392
|
+
const sqlName = colDef.name;
|
|
393
|
+
if (typeof sqlName === "string") dateColumns.set(sqlName, colDef);
|
|
394
|
+
}
|
|
395
|
+
if (dateColumns.size === 0) return data;
|
|
396
|
+
let encoded;
|
|
397
|
+
for (const [key, value] of Object.entries(data)) {
|
|
398
|
+
if (!(value instanceof Date)) continue;
|
|
399
|
+
const column = dateColumns.get(key);
|
|
400
|
+
if (!column) continue;
|
|
401
|
+
encoded ??= { ...data };
|
|
402
|
+
encoded[key] = column.mapToDriverValue(value);
|
|
403
|
+
}
|
|
404
|
+
return encoded ?? data;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Bring a row about to be written through a raw-SQL statement into the shape
|
|
408
|
+
* a Drizzle query would have bound: SQL column names for keys, encoded values
|
|
409
|
+
* for date columns.
|
|
410
|
+
*
|
|
411
|
+
* The mirror of {@link mapRowFromRawSql}. The raw-SQL write paths exist
|
|
412
|
+
* because a dialect needs SQL a Drizzle query cannot express, not because
|
|
413
|
+
* their callers want different values in the table.
|
|
414
|
+
*/
|
|
415
|
+
mapRowToRawSql(tableObj, data) {
|
|
416
|
+
return this.mapDateValuesToDriver(
|
|
417
|
+
tableObj,
|
|
418
|
+
this.mapKeysToSqlColumns(tableObj, data)
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Bring a raw-SQL result row into the shape a Drizzle query would have
|
|
423
|
+
* produced: Drizzle property names for keys, decoded values for date columns.
|
|
424
|
+
*
|
|
425
|
+
* The raw-SQL write paths exist because a dialect needs SQL a Drizzle query
|
|
426
|
+
* cannot express, not because their callers want a different row shape, so
|
|
427
|
+
* every one of them ends here.
|
|
428
|
+
*/
|
|
429
|
+
mapRowFromRawSql(tableObj, row, aliases = []) {
|
|
430
|
+
const mapped = this.mapDateValuesFromDriver(
|
|
431
|
+
tableObj,
|
|
432
|
+
this.mapRowKeysToJs(tableObj, row)
|
|
433
|
+
);
|
|
434
|
+
return this.applyWallClockAliases(mapped, aliases);
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* The date columns a statement is about to return, and the alias each one's
|
|
438
|
+
* wall clock is spelled out under.
|
|
439
|
+
*
|
|
440
|
+
* A driver turns a timestamp into a `Date` using ITS zone before any of this
|
|
441
|
+
* code runs, and that conversion is lossy: a wall clock inside a
|
|
442
|
+
* daylight-saving gap is a local time that does not exist, so the driver
|
|
443
|
+
* normalizes it and the original is gone. Asking the database for the wall
|
|
444
|
+
* clock as text alongside the row is the only way to read it exactly.
|
|
445
|
+
*
|
|
446
|
+
* `"*"` covers every date column; a projection covers only the ones it asked
|
|
447
|
+
* for, so a caller still gets back what it requested and nothing more.
|
|
448
|
+
*/
|
|
449
|
+
dateWallClockAliases(tableObj, returning) {
|
|
450
|
+
if (!tableObj || typeof tableObj !== "object" || returning === void 0) {
|
|
451
|
+
return [];
|
|
452
|
+
}
|
|
453
|
+
const requested = returning === "*" ? void 0 : new Set(returning.map(String));
|
|
454
|
+
const taken = /* @__PURE__ */ new Set();
|
|
455
|
+
for (const [jsName, colDef] of Object.entries(
|
|
456
|
+
tableObj
|
|
457
|
+
)) {
|
|
458
|
+
taken.add(jsName);
|
|
459
|
+
const columnName = colDef.name;
|
|
460
|
+
if (typeof columnName === "string") taken.add(columnName);
|
|
461
|
+
}
|
|
462
|
+
let next = 0;
|
|
463
|
+
const freeAlias = () => {
|
|
464
|
+
let candidate = `${WALL_CLOCK_ALIAS_STEM}${next++}`;
|
|
465
|
+
while (taken.has(candidate)) {
|
|
466
|
+
candidate = `${WALL_CLOCK_ALIAS_STEM}${next++}`;
|
|
467
|
+
}
|
|
468
|
+
taken.add(candidate);
|
|
469
|
+
return candidate;
|
|
470
|
+
};
|
|
471
|
+
const aliases = [];
|
|
472
|
+
for (const [jsName, colDef] of Object.entries(
|
|
473
|
+
tableObj
|
|
474
|
+
)) {
|
|
475
|
+
if (!isDateColumn(colDef)) continue;
|
|
476
|
+
if (carriesTimeZone(colDef)) continue;
|
|
477
|
+
const sqlName = colDef.name;
|
|
478
|
+
if (typeof sqlName !== "string") continue;
|
|
479
|
+
if (requested && !requested.has(sqlName) && !requested.has(jsName)) {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
aliases.push({ sqlName, alias: freeAlias(), jsName });
|
|
483
|
+
}
|
|
484
|
+
return aliases;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Replace each date in a row with the wall clock the database spelled out,
|
|
488
|
+
* read as UTC, and drop the aliases that carried it.
|
|
489
|
+
*
|
|
490
|
+
* The statement writes a UTC wall clock, so reading one back as UTC is what
|
|
491
|
+
* makes a write and a later read agree. Whatever the column actually stored
|
|
492
|
+
* is what arrives -- a column keeping only whole seconds reports whole
|
|
493
|
+
* seconds -- because this is the database's own text, not a value
|
|
494
|
+
* reconstructed from the one that was bound.
|
|
495
|
+
*/
|
|
496
|
+
applyWallClockAliases(row, aliases) {
|
|
497
|
+
if (!row || typeof row !== "object" || aliases.length === 0) return row;
|
|
498
|
+
const record = row;
|
|
499
|
+
let resolved;
|
|
500
|
+
for (const { alias, jsName } of aliases) {
|
|
501
|
+
if (!(alias in record)) continue;
|
|
502
|
+
resolved ??= { ...record };
|
|
503
|
+
const text = resolved[alias];
|
|
504
|
+
delete resolved[alias];
|
|
505
|
+
if (typeof text !== "string") continue;
|
|
506
|
+
const parsed = parseWallClockAsUtc(text);
|
|
507
|
+
if (parsed !== void 0) resolved[jsName] = parsed;
|
|
508
|
+
}
|
|
509
|
+
return resolved ?? row;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Build a Drizzle column projection object (`{ propertyName: column }`) from a
|
|
513
|
+
* requested column list, used by `select` (columns) and `insert` (returning).
|
|
514
|
+
* A requested name resolves against either the Drizzle property name
|
|
515
|
+
* (camelCase) or the SQL column name (snake_case); the projection is keyed by
|
|
516
|
+
* the property name so the row shape matches a full select. Returns undefined
|
|
517
|
+
* for `"*"` or when nothing resolves, so callers fall back to all columns.
|
|
518
|
+
*/
|
|
519
|
+
buildColumnProjection(tableObj, names) {
|
|
520
|
+
if (names == null || names === "*" || !tableObj || typeof tableObj !== "object") {
|
|
521
|
+
return void 0;
|
|
522
|
+
}
|
|
523
|
+
const cols = drizzleOrm.getColumns(tableObj);
|
|
524
|
+
const byAnyName = {};
|
|
525
|
+
for (const [jsName, col] of Object.entries(cols)) {
|
|
526
|
+
byAnyName[jsName] = { jsName, col };
|
|
527
|
+
const sqlName = col?.name;
|
|
528
|
+
if (typeof sqlName === "string") byAnyName[sqlName] = { jsName, col };
|
|
529
|
+
}
|
|
530
|
+
const projection = {};
|
|
531
|
+
for (const name of names) {
|
|
532
|
+
const hit = byAnyName[name];
|
|
533
|
+
if (hit) projection[hit.jsName] = hit.col;
|
|
534
|
+
}
|
|
535
|
+
return Object.keys(projection).length ? projection : void 0;
|
|
536
|
+
}
|
|
178
537
|
// ============================================================
|
|
179
538
|
// Connection Status (Default implementations, can override)
|
|
180
539
|
// ============================================================
|
|
@@ -328,12 +687,13 @@ var DrizzleAdapter = class {
|
|
|
328
687
|
* });
|
|
329
688
|
* ```
|
|
330
689
|
*/
|
|
331
|
-
async select(table, options) {
|
|
690
|
+
async select(table, options, executor) {
|
|
332
691
|
const tableObj = this.getTableObject(table);
|
|
333
692
|
if (tableObj) {
|
|
334
693
|
try {
|
|
335
|
-
const db = this.getDrizzle();
|
|
336
|
-
|
|
694
|
+
const db = executor ?? this.getDrizzle();
|
|
695
|
+
const projection = options?.columns?.length ? this.buildColumnProjection(tableObj, options.columns) : void 0;
|
|
696
|
+
let query = projection ? db.select(projection).from(tableObj) : db.select().from(tableObj);
|
|
337
697
|
if (options?.where) {
|
|
338
698
|
const whereCondition = buildDrizzleWhere(
|
|
339
699
|
tableObj,
|
|
@@ -344,7 +704,7 @@ var DrizzleAdapter = class {
|
|
|
344
704
|
}
|
|
345
705
|
}
|
|
346
706
|
if (options?.orderBy?.length) {
|
|
347
|
-
const columns = drizzleOrm.
|
|
707
|
+
const columns = drizzleOrm.getColumns(tableObj);
|
|
348
708
|
const orderClauses = options.orderBy.map((o) => {
|
|
349
709
|
const col = columns[o.column];
|
|
350
710
|
if (!col) return void 0;
|
|
@@ -360,6 +720,16 @@ var DrizzleAdapter = class {
|
|
|
360
720
|
if (options?.offset !== void 0) {
|
|
361
721
|
query = query.offset(options.offset);
|
|
362
722
|
}
|
|
723
|
+
if (options?.forUpdate && !executor) {
|
|
724
|
+
throw this.createDatabaseError(
|
|
725
|
+
"query",
|
|
726
|
+
"forUpdate requires a transaction executor: a lock request on the pooled connection takes no durable lock and cannot prevent a concurrent write.",
|
|
727
|
+
void 0
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
if (options?.forUpdate && this.dialect !== "sqlite") {
|
|
731
|
+
query = query.for("update");
|
|
732
|
+
}
|
|
363
733
|
return await query;
|
|
364
734
|
} catch (error) {
|
|
365
735
|
throw this.handleQueryError(error, "select", table);
|
|
@@ -391,8 +761,12 @@ var DrizzleAdapter = class {
|
|
|
391
761
|
* });
|
|
392
762
|
* ```
|
|
393
763
|
*/
|
|
394
|
-
async selectOne(table, options) {
|
|
395
|
-
const results = await this.select(
|
|
764
|
+
async selectOne(table, options, executor) {
|
|
765
|
+
const results = await this.select(
|
|
766
|
+
table,
|
|
767
|
+
{ ...options, limit: 1 },
|
|
768
|
+
executor
|
|
769
|
+
);
|
|
396
770
|
return results.length > 0 ? results[0] : null;
|
|
397
771
|
}
|
|
398
772
|
/**
|
|
@@ -424,19 +798,22 @@ var DrizzleAdapter = class {
|
|
|
424
798
|
const mappedData = this.mapDataToColumnNames(tableObj, data);
|
|
425
799
|
const db = this.getDrizzle();
|
|
426
800
|
const caps = this.getCapabilities();
|
|
427
|
-
|
|
428
|
-
|
|
801
|
+
const returning = options?.returning;
|
|
802
|
+
const wantsReturning = returning != null && !(Array.isArray(returning) && returning.length === 0);
|
|
803
|
+
if (caps.supportsReturning && wantsReturning) {
|
|
804
|
+
const projection = this.buildColumnProjection(tableObj, returning);
|
|
805
|
+
const insertQuery = db.insert(tableObj).values(mappedData);
|
|
806
|
+
const result2 = await (projection ? insertQuery.returning(projection) : insertQuery.returning());
|
|
429
807
|
return Array.isArray(result2) ? result2[0] : result2;
|
|
430
808
|
}
|
|
431
809
|
const result = await db.insert(tableObj).values(mappedData);
|
|
432
|
-
if (!caps.supportsReturning &&
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
810
|
+
if (!caps.supportsReturning && wantsReturning && data.id !== void 0) {
|
|
811
|
+
return await this.selectOne(table, {
|
|
812
|
+
columns: returning === "*" ? void 0 : returning,
|
|
813
|
+
where: {
|
|
814
|
+
and: [{ column: "id", op: "=", value: data.id }]
|
|
815
|
+
}
|
|
816
|
+
});
|
|
440
817
|
}
|
|
441
818
|
return Array.isArray(result) ? result[0] : result;
|
|
442
819
|
} catch (error) {
|
|
@@ -506,11 +883,11 @@ var DrizzleAdapter = class {
|
|
|
506
883
|
* );
|
|
507
884
|
* ```
|
|
508
885
|
*/
|
|
509
|
-
async update(table, data, where, options) {
|
|
886
|
+
async update(table, data, where, options, executor) {
|
|
510
887
|
const tableObj = this.getTableObject(table);
|
|
511
888
|
if (tableObj) {
|
|
512
889
|
try {
|
|
513
|
-
const db = this.getDrizzle();
|
|
890
|
+
const db = executor ?? this.getDrizzle();
|
|
514
891
|
const caps = this.getCapabilities();
|
|
515
892
|
const mappedData = this.mapDataToColumnNames(tableObj, data);
|
|
516
893
|
let query = db.update(tableObj).set(mappedData);
|
|
@@ -523,7 +900,7 @@ var DrizzleAdapter = class {
|
|
|
523
900
|
}
|
|
524
901
|
await query;
|
|
525
902
|
if (!caps.supportsReturning && options?.returning) {
|
|
526
|
-
return await this.select(table, { where });
|
|
903
|
+
return await this.select(table, { where }, executor);
|
|
527
904
|
}
|
|
528
905
|
return [];
|
|
529
906
|
} catch (error) {
|
|
@@ -536,6 +913,42 @@ var DrizzleAdapter = class {
|
|
|
536
913
|
void 0
|
|
537
914
|
);
|
|
538
915
|
}
|
|
916
|
+
/**
|
|
917
|
+
* Update records in a table and report how many rows the statement affected.
|
|
918
|
+
*
|
|
919
|
+
* The count is the whole return value, mirroring `delete`. `update` cannot answer this: without
|
|
920
|
+
* `returning` it discards the driver's count, and WITH `returning` on a dialect that lacks
|
|
921
|
+
* RETURNING it re-SELECTs using the same WHERE — so a conditional update that just changed a
|
|
922
|
+
* column named in that WHERE reads back zero rows and a write that landed reports as unmatched.
|
|
923
|
+
* Reading the driver's own count has no second query to disagree with the first.
|
|
924
|
+
*
|
|
925
|
+
* 🔴 MySQL reports CHANGED rows, not matched rows: an UPDATE that matches a row but writes values
|
|
926
|
+
* identical to what it holds counts zero. A caller using this as a compare-and-set must therefore
|
|
927
|
+
* include a column the write always moves — a version bump, a timestamp with enough resolution —
|
|
928
|
+
* so that matched implies changed. Postgres (`rowCount`) and SQLite (`changes`) count matched
|
|
929
|
+
* rows and do not need the precaution, which is exactly why it cannot be dropped: the dialect
|
|
930
|
+
* where the distinction exists is the one with no RETURNING to fall back on.
|
|
931
|
+
*/
|
|
932
|
+
async updateCount(table, data, where, executor) {
|
|
933
|
+
const tableObj = this.getTableObject(table);
|
|
934
|
+
if (tableObj) {
|
|
935
|
+
try {
|
|
936
|
+
const db = executor ?? this.getDrizzle();
|
|
937
|
+
const mappedData = this.mapDataToColumnNames(tableObj, data);
|
|
938
|
+
const statement = db.update(tableObj).set(mappedData);
|
|
939
|
+
const whereCondition = buildDrizzleWhere(tableObj, where);
|
|
940
|
+
const result = await (whereCondition ? statement.where(whereCondition) : statement);
|
|
941
|
+
return affectedRowCount(result);
|
|
942
|
+
} catch (error) {
|
|
943
|
+
throw this.handleQueryError(error, "update", table);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
throw this.createDatabaseError(
|
|
947
|
+
"query",
|
|
948
|
+
`Table "${table}" not found in schema registry. Ensure setTableResolver() has been called during boot.`,
|
|
949
|
+
void 0
|
|
950
|
+
);
|
|
951
|
+
}
|
|
539
952
|
/**
|
|
540
953
|
* Delete records from a table.
|
|
541
954
|
*
|
|
@@ -558,18 +971,18 @@ var DrizzleAdapter = class {
|
|
|
558
971
|
* console.log(`Deleted ${count} users`);
|
|
559
972
|
* ```
|
|
560
973
|
*/
|
|
561
|
-
async delete(table, where, _options) {
|
|
974
|
+
async delete(table, where, _options, executor) {
|
|
562
975
|
const tableObj = this.getTableObject(table);
|
|
563
976
|
if (tableObj) {
|
|
564
977
|
try {
|
|
565
|
-
const db = this.getDrizzle();
|
|
978
|
+
const db = executor ?? this.getDrizzle();
|
|
566
979
|
let query = db.delete(tableObj);
|
|
567
980
|
const whereCondition = buildDrizzleWhere(tableObj, where);
|
|
568
981
|
if (whereCondition) {
|
|
569
982
|
query = query.where(whereCondition);
|
|
570
983
|
}
|
|
571
984
|
const result = await query;
|
|
572
|
-
return
|
|
985
|
+
return affectedRowCount(result);
|
|
573
986
|
} catch (error) {
|
|
574
987
|
throw this.handleQueryError(error, "delete", table);
|
|
575
988
|
}
|
|
@@ -607,13 +1020,13 @@ var DrizzleAdapter = class {
|
|
|
607
1020
|
* });
|
|
608
1021
|
* ```
|
|
609
1022
|
*/
|
|
610
|
-
async upsert(table, data, options) {
|
|
1023
|
+
async upsert(table, data, options, executor) {
|
|
611
1024
|
const tableObj = this.getTableObject(table);
|
|
612
1025
|
if (tableObj) {
|
|
613
1026
|
try {
|
|
614
|
-
const db = this.getDrizzle();
|
|
1027
|
+
const db = executor ?? this.getDrizzle();
|
|
615
1028
|
const caps = this.getCapabilities();
|
|
616
|
-
const columns = drizzleOrm.
|
|
1029
|
+
const columns = drizzleOrm.getColumns(tableObj);
|
|
617
1030
|
const conflictTarget = options.conflictColumns.map((col) => columns[col]).filter(Boolean);
|
|
618
1031
|
const conflictSet = new Set(options.conflictColumns);
|
|
619
1032
|
const updateData = {};
|
|
@@ -638,17 +1051,21 @@ var DrizzleAdapter = class {
|
|
|
638
1051
|
}
|
|
639
1052
|
await query;
|
|
640
1053
|
if (options.conflictColumns.length && data[options.conflictColumns[0]] !== void 0) {
|
|
641
|
-
return await this.selectOne(
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
1054
|
+
return await this.selectOne(
|
|
1055
|
+
table,
|
|
1056
|
+
{
|
|
1057
|
+
where: {
|
|
1058
|
+
and: [
|
|
1059
|
+
{
|
|
1060
|
+
column: options.conflictColumns[0],
|
|
1061
|
+
op: "=",
|
|
1062
|
+
value: data[options.conflictColumns[0]]
|
|
1063
|
+
}
|
|
1064
|
+
]
|
|
1065
|
+
}
|
|
1066
|
+
},
|
|
1067
|
+
executor
|
|
1068
|
+
);
|
|
652
1069
|
}
|
|
653
1070
|
return data;
|
|
654
1071
|
} catch (error) {
|
|
@@ -895,11 +1312,15 @@ var DrizzleAdapter = class {
|
|
|
895
1312
|
sql = `
|
|
896
1313
|
SELECT EXISTS (
|
|
897
1314
|
SELECT FROM information_schema.tables
|
|
898
|
-
WHERE table_schema = $1
|
|
1315
|
+
WHERE table_schema = COALESCE($1, (
|
|
1316
|
+
SELECT n.nspname FROM pg_class c
|
|
1317
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
1318
|
+
WHERE c.oid = to_regclass($2)
|
|
1319
|
+
))
|
|
899
1320
|
AND table_name = $2
|
|
900
1321
|
) as exists
|
|
901
1322
|
`;
|
|
902
|
-
params.push(schema ??
|
|
1323
|
+
params.push(schema ?? null, tableName);
|
|
903
1324
|
break;
|
|
904
1325
|
case "mysql":
|
|
905
1326
|
sql = `
|
|
@@ -974,7 +1395,7 @@ var DrizzleAdapter = class {
|
|
|
974
1395
|
break;
|
|
975
1396
|
case "mysql":
|
|
976
1397
|
sql = `
|
|
977
|
-
SELECT table_name
|
|
1398
|
+
SELECT table_name AS table_name
|
|
978
1399
|
FROM information_schema.tables
|
|
979
1400
|
WHERE table_schema = DATABASE()
|
|
980
1401
|
AND table_type = 'BASE TABLE'
|