@vsrepo/drizzle-adapter 0.1.0 → 0.1.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.
- package/dist/index.cjs +112 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -1
- package/dist/index.d.ts +34 -1
- package/dist/index.js +111 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -86,6 +86,7 @@ declare class DrizzleAdapter<T, K extends DrizzleDbLike = DrizzleDbLike> extends
|
|
|
86
86
|
private readonly pk;
|
|
87
87
|
private readonly queryKey;
|
|
88
88
|
private readonly relations?;
|
|
89
|
+
private readonly relationsKeysSet?;
|
|
89
90
|
constructor(db: K, config: DrizzleAdapterConfig<T, K>);
|
|
90
91
|
/**
|
|
91
92
|
* Returns `db.query[queryKey]` (the relational query builder entry for
|
|
@@ -130,6 +131,16 @@ declare class DrizzleAdapter<T, K extends DrizzleDbLike = DrizzleDbLike> extends
|
|
|
130
131
|
* when they're actually used.
|
|
131
132
|
*/
|
|
132
133
|
private resolveFindWhere;
|
|
134
|
+
/**
|
|
135
|
+
* Fetches the current row matching `where` (or `null` when none exists),
|
|
136
|
+
* using the same where-resolution path as `update` — the single place
|
|
137
|
+
* "find the row behind a where" is materialized.
|
|
138
|
+
*
|
|
139
|
+
* Internal callers that already fetched a row in their own transaction
|
|
140
|
+
* (`save`, `upsert`) use this to detect/create, then hand that same row
|
|
141
|
+
* to `updateCore` as `current`, so the row is only ever read once.
|
|
142
|
+
*/
|
|
143
|
+
private findCurrentByWhere;
|
|
133
144
|
/**
|
|
134
145
|
* Strips relation fields from a payload — used by `createMany`/`updateMany`/
|
|
135
146
|
* `updateManyReturning`, since batch statements only accept flat column
|
|
@@ -161,6 +172,20 @@ declare class DrizzleAdapter<T, K extends DrizzleDbLike = DrizzleDbLike> extends
|
|
|
161
172
|
deleteMany(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<CountResult>;
|
|
162
173
|
deleteManyReturning(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<T[]>;
|
|
163
174
|
update(where: VSRepoWhere<T>, obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<T>;
|
|
175
|
+
/**
|
|
176
|
+
* Shared implementation behind `update` — also reached from `save` and
|
|
177
|
+
* `upsert`, which pass in `current` (the row they already fetched in the
|
|
178
|
+
* same transaction) so the row is read only once instead of once per
|
|
179
|
+
* method. The `'update' found no record...` contract applies whenever it
|
|
180
|
+
* runs without a preloaded row (i.e. when called through the public
|
|
181
|
+
* `update`), and always as a safety net.
|
|
182
|
+
*
|
|
183
|
+
* Invariant: when `current` is provided, it must be the full row read
|
|
184
|
+
* from the exact `tx` referenced by `options.db` (never from outside a
|
|
185
|
+
* transaction, and never a pk-only projection — `resolveFkHereFields`
|
|
186
|
+
* reads the current FK values off it).
|
|
187
|
+
*/
|
|
188
|
+
private updateCore;
|
|
164
189
|
updateMany(where: VSRepoWhere<T>, obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<CountResult>;
|
|
165
190
|
updateManyReturning(where: VSRepoWhere<T>, obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<T[]>;
|
|
166
191
|
count(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<number>;
|
|
@@ -181,4 +206,12 @@ declare class DrizzleAdapter<T, K extends DrizzleDbLike = DrizzleDbLike> extends
|
|
|
181
206
|
max(field: NumericKeys<T>, where?: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<number | null>;
|
|
182
207
|
}
|
|
183
208
|
|
|
184
|
-
|
|
209
|
+
/**
|
|
210
|
+
* @publicApi
|
|
211
|
+
*/
|
|
212
|
+
type DrizzleOrmTypes<T extends DrizzleDbLike> = {
|
|
213
|
+
dbClient: T;
|
|
214
|
+
dbTransaction: Parameters<Parameters<T["transaction"]>[0]>[0];
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export { type AdapterRelation, type AdapterRelations, DrizzleAdapter, type DrizzleAdapterConfig, type DrizzleDbLike, type DrizzleOrmTypes, type DrizzleTransactionLike, type SupportedDialects, DrizzleAdapter as VSRepoDrizzleAdapter };
|
package/dist/index.d.ts
CHANGED
|
@@ -86,6 +86,7 @@ declare class DrizzleAdapter<T, K extends DrizzleDbLike = DrizzleDbLike> extends
|
|
|
86
86
|
private readonly pk;
|
|
87
87
|
private readonly queryKey;
|
|
88
88
|
private readonly relations?;
|
|
89
|
+
private readonly relationsKeysSet?;
|
|
89
90
|
constructor(db: K, config: DrizzleAdapterConfig<T, K>);
|
|
90
91
|
/**
|
|
91
92
|
* Returns `db.query[queryKey]` (the relational query builder entry for
|
|
@@ -130,6 +131,16 @@ declare class DrizzleAdapter<T, K extends DrizzleDbLike = DrizzleDbLike> extends
|
|
|
130
131
|
* when they're actually used.
|
|
131
132
|
*/
|
|
132
133
|
private resolveFindWhere;
|
|
134
|
+
/**
|
|
135
|
+
* Fetches the current row matching `where` (or `null` when none exists),
|
|
136
|
+
* using the same where-resolution path as `update` — the single place
|
|
137
|
+
* "find the row behind a where" is materialized.
|
|
138
|
+
*
|
|
139
|
+
* Internal callers that already fetched a row in their own transaction
|
|
140
|
+
* (`save`, `upsert`) use this to detect/create, then hand that same row
|
|
141
|
+
* to `updateCore` as `current`, so the row is only ever read once.
|
|
142
|
+
*/
|
|
143
|
+
private findCurrentByWhere;
|
|
133
144
|
/**
|
|
134
145
|
* Strips relation fields from a payload — used by `createMany`/`updateMany`/
|
|
135
146
|
* `updateManyReturning`, since batch statements only accept flat column
|
|
@@ -161,6 +172,20 @@ declare class DrizzleAdapter<T, K extends DrizzleDbLike = DrizzleDbLike> extends
|
|
|
161
172
|
deleteMany(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<CountResult>;
|
|
162
173
|
deleteManyReturning(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<T[]>;
|
|
163
174
|
update(where: VSRepoWhere<T>, obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<T>;
|
|
175
|
+
/**
|
|
176
|
+
* Shared implementation behind `update` — also reached from `save` and
|
|
177
|
+
* `upsert`, which pass in `current` (the row they already fetched in the
|
|
178
|
+
* same transaction) so the row is read only once instead of once per
|
|
179
|
+
* method. The `'update' found no record...` contract applies whenever it
|
|
180
|
+
* runs without a preloaded row (i.e. when called through the public
|
|
181
|
+
* `update`), and always as a safety net.
|
|
182
|
+
*
|
|
183
|
+
* Invariant: when `current` is provided, it must be the full row read
|
|
184
|
+
* from the exact `tx` referenced by `options.db` (never from outside a
|
|
185
|
+
* transaction, and never a pk-only projection — `resolveFkHereFields`
|
|
186
|
+
* reads the current FK values off it).
|
|
187
|
+
*/
|
|
188
|
+
private updateCore;
|
|
164
189
|
updateMany(where: VSRepoWhere<T>, obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<CountResult>;
|
|
165
190
|
updateManyReturning(where: VSRepoWhere<T>, obj: DeepPartial<T>, options?: AdapterMethodOptions<T>): Promise<T[]>;
|
|
166
191
|
count(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<number>;
|
|
@@ -181,4 +206,12 @@ declare class DrizzleAdapter<T, K extends DrizzleDbLike = DrizzleDbLike> extends
|
|
|
181
206
|
max(field: NumericKeys<T>, where?: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<number | null>;
|
|
182
207
|
}
|
|
183
208
|
|
|
184
|
-
|
|
209
|
+
/**
|
|
210
|
+
* @publicApi
|
|
211
|
+
*/
|
|
212
|
+
type DrizzleOrmTypes<T extends DrizzleDbLike> = {
|
|
213
|
+
dbClient: T;
|
|
214
|
+
dbTransaction: Parameters<Parameters<T["transaction"]>[0]>[0];
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export { type AdapterRelation, type AdapterRelations, DrizzleAdapter, type DrizzleAdapterConfig, type DrizzleDbLike, type DrizzleOrmTypes, type DrizzleTransactionLike, type SupportedDialects, DrizzleAdapter as VSRepoDrizzleAdapter };
|
package/dist/index.js
CHANGED
|
@@ -309,16 +309,21 @@ function validateRelations(table, dialect, relations) {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
// src/parsers/columns.parser.ts
|
|
312
|
-
function parseColumns(select) {
|
|
312
|
+
function parseColumns(select, relationsKeysSet) {
|
|
313
313
|
const columns = {};
|
|
314
314
|
let withResult;
|
|
315
315
|
for (const [key, value] of Object.entries(select)) {
|
|
316
316
|
if (value === void 0) continue;
|
|
317
|
-
if (
|
|
317
|
+
if (typeof value !== "boolean") {
|
|
318
318
|
withResult ??= {};
|
|
319
319
|
const nested = parseColumns(value);
|
|
320
320
|
withResult[key] = nested.with ? { columns: nested.columns, with: nested.with } : { columns: nested.columns };
|
|
321
321
|
} else {
|
|
322
|
+
if (relationsKeysSet?.has(key)) {
|
|
323
|
+
withResult ??= {};
|
|
324
|
+
withResult[key] = value;
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
322
327
|
columns[key] = value;
|
|
323
328
|
}
|
|
324
329
|
}
|
|
@@ -845,7 +850,7 @@ function mergeEntities(result, obj, relations) {
|
|
|
845
850
|
}
|
|
846
851
|
|
|
847
852
|
// src/resolvers/relation-writes.resolver.ts
|
|
848
|
-
import { and as and2, eq as eq2,
|
|
853
|
+
import { and as and2, eq as eq2, notInArray as notInArray2 } from "drizzle-orm";
|
|
849
854
|
import { AdapterErrorCode as AdapterErrorCode9, VSRepoAdapterError as VSRepoAdapterError9 } from "vsrepo";
|
|
850
855
|
var SKIP = /* @__PURE__ */ Symbol("skip");
|
|
851
856
|
function omitKey(item, key) {
|
|
@@ -888,7 +893,7 @@ async function resolveFkHereField(tx, relation, field, currentFkValue) {
|
|
|
888
893
|
}
|
|
889
894
|
const pkValue = field[relation.relatedPk];
|
|
890
895
|
if (pkValue === void 0) {
|
|
891
|
-
const [created] = await tx.insert(relation.table).values(field).returning();
|
|
896
|
+
const [created] = await tx.insert(relation.table).values(field).returning({ [relation.relatedPk]: relation.table[relation.relatedPk] });
|
|
892
897
|
return created[relation.relatedPk];
|
|
893
898
|
}
|
|
894
899
|
const existing = await tx.select({ pk: pkColumn }).from(relation.table).where(eq2(pkColumn, pkValue)).limit(1);
|
|
@@ -926,7 +931,7 @@ async function resolveOtmField(tx, relation, items, ownPkValue) {
|
|
|
926
931
|
const withPk = items.filter((item) => item[relation.relatedPk] !== void 0);
|
|
927
932
|
const connectedIds = [];
|
|
928
933
|
for (const item of withoutPk) {
|
|
929
|
-
const [insertedRow] = await tx.insert(relation.table).values({ ...item, [relation.fkThere]: ownPkValue }).returning();
|
|
934
|
+
const [insertedRow] = await tx.insert(relation.table).values({ ...item, [relation.fkThere]: ownPkValue }).returning({ [relation.relatedPk]: relation.table[relation.relatedPk] });
|
|
930
935
|
if (insertedRow?.[relation.relatedPk] !== void 0) {
|
|
931
936
|
connectedIds.push(insertedRow[relation.relatedPk]);
|
|
932
937
|
}
|
|
@@ -958,22 +963,25 @@ async function resolveOtoFkThereField(tx, relation, field, ownPkValue) {
|
|
|
958
963
|
return;
|
|
959
964
|
}
|
|
960
965
|
const pkValue = field[relation.relatedPk];
|
|
961
|
-
|
|
966
|
+
const resolveSetData = async () => {
|
|
967
|
+
const setData = relation.restriction === "set" ? { ...omitKey(field, relation.relatedPk), [relation.fkThere]: ownPkValue } : { [relation.fkThere]: ownPkValue };
|
|
968
|
+
await tx.update(relation.table).set(setData).where(eq2(pkColumn, pkValue));
|
|
969
|
+
};
|
|
962
970
|
if (pkValue === void 0) {
|
|
963
|
-
const [
|
|
964
|
-
|
|
971
|
+
const [otoRel] = await tx.select({ pk: pkColumn }).from(relation.table).where(eq2(fkColumn, ownPkValue)).limit(1);
|
|
972
|
+
if (!otoRel) {
|
|
973
|
+
await tx.insert(relation.table).values({ ...field, [relation.fkThere]: ownPkValue });
|
|
974
|
+
} else {
|
|
975
|
+
await resolveSetData();
|
|
976
|
+
}
|
|
965
977
|
} else {
|
|
966
978
|
const existing = await tx.select({ pk: pkColumn }).from(relation.table).where(eq2(pkColumn, pkValue)).limit(1);
|
|
967
979
|
if (existing.length === 0) {
|
|
968
980
|
await tx.insert(relation.table).values({ ...field, [relation.fkThere]: ownPkValue });
|
|
969
981
|
} else {
|
|
970
|
-
|
|
971
|
-
await tx.update(relation.table).set(setData).where(eq2(pkColumn, pkValue));
|
|
982
|
+
await resolveSetData();
|
|
972
983
|
}
|
|
973
984
|
}
|
|
974
|
-
if (relation.restriction === "set" && keepPk !== void 0) {
|
|
975
|
-
await tx.delete(relation.table).where(and2(eq2(fkColumn, ownPkValue), ne(pkColumn, keepPk)));
|
|
976
|
-
}
|
|
977
985
|
}
|
|
978
986
|
async function resolveFkThereFields(tx, entries, ownPkValue) {
|
|
979
987
|
for (const [key, relation, value] of entries) {
|
|
@@ -1007,6 +1015,7 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1007
1015
|
pk;
|
|
1008
1016
|
queryKey;
|
|
1009
1017
|
relations;
|
|
1018
|
+
relationsKeysSet;
|
|
1010
1019
|
constructor(db, config) {
|
|
1011
1020
|
super();
|
|
1012
1021
|
const validated = validateDrizzleAdapterConfig(db, config);
|
|
@@ -1017,6 +1026,9 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1017
1026
|
const fieldsConfig = resolveFieldsConfig(this.table);
|
|
1018
1027
|
this.pk = fieldsConfig.pk;
|
|
1019
1028
|
this.relations = validateRelations(this.table, this.dialect, validated.config.relations);
|
|
1029
|
+
if (this.relations) {
|
|
1030
|
+
this.relationsKeysSet = new Set(Object.keys(this.relations));
|
|
1031
|
+
}
|
|
1020
1032
|
}
|
|
1021
1033
|
/**
|
|
1022
1034
|
* Returns `db.query[queryKey]` (the relational query builder entry for
|
|
@@ -1039,7 +1051,7 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1039
1051
|
let columns;
|
|
1040
1052
|
let withArg;
|
|
1041
1053
|
if (options.select) {
|
|
1042
|
-
const parsedSelect = parseColumns(options.select);
|
|
1054
|
+
const parsedSelect = parseColumns(options.select, this.relationsKeysSet);
|
|
1043
1055
|
columns = parsedSelect.columns;
|
|
1044
1056
|
withArg = parsedSelect.with;
|
|
1045
1057
|
} else if (options.relations) {
|
|
@@ -1118,6 +1130,21 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1118
1130
|
paginationApplied: opts?.limit !== void 0 || opts?.offset !== void 0
|
|
1119
1131
|
};
|
|
1120
1132
|
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Fetches the current row matching `where` (or `null` when none exists),
|
|
1135
|
+
* using the same where-resolution path as `update` — the single place
|
|
1136
|
+
* "find the row behind a where" is materialized.
|
|
1137
|
+
*
|
|
1138
|
+
* Internal callers that already fetched a row in their own transaction
|
|
1139
|
+
* (`save`, `upsert`) use this to detect/create, then hand that same row
|
|
1140
|
+
* to `updateCore` as `current`, so the row is only ever read once.
|
|
1141
|
+
*/
|
|
1142
|
+
async findCurrentByWhere(where, opts) {
|
|
1143
|
+
const arg = {
|
|
1144
|
+
where: (await this.resolveFindWhere(where, { db: opts?.db, limit: 1 })).where
|
|
1145
|
+
};
|
|
1146
|
+
return await this.getQueryBuilder(opts?.db).findFirst(arg) ?? null;
|
|
1147
|
+
}
|
|
1121
1148
|
/**
|
|
1122
1149
|
* Strips relation fields from a payload — used by `createMany`/`updateMany`/
|
|
1123
1150
|
* `updateManyReturning`, since batch statements only accept flat column
|
|
@@ -1227,12 +1254,18 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1227
1254
|
return await this.create(obj, options);
|
|
1228
1255
|
}
|
|
1229
1256
|
return await this.runTransactional(options?.db, async (tx) => {
|
|
1230
|
-
const
|
|
1231
|
-
|
|
1232
|
-
|
|
1257
|
+
const current = await this.findCurrentByWhere({ [this.pk]: pkValue }, {
|
|
1258
|
+
db: tx
|
|
1259
|
+
});
|
|
1260
|
+
if (current === null) {
|
|
1233
1261
|
return this.create(obj, { ...options, db: tx });
|
|
1234
1262
|
}
|
|
1235
|
-
return this.
|
|
1263
|
+
return this.updateCore(
|
|
1264
|
+
{ [this.pk]: pkValue },
|
|
1265
|
+
obj,
|
|
1266
|
+
{ ...options, db: tx },
|
|
1267
|
+
current
|
|
1268
|
+
);
|
|
1236
1269
|
});
|
|
1237
1270
|
} catch (error) {
|
|
1238
1271
|
throw mapDrizzleError(error, "save", this.dialect);
|
|
@@ -1259,7 +1292,7 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1259
1292
|
false
|
|
1260
1293
|
);
|
|
1261
1294
|
await resolveFkHereFields(tx, fkHereEntries, scalarFields, void 0);
|
|
1262
|
-
const [created] = await tx.insert(this.table).values(scalarFields).returning();
|
|
1295
|
+
const [created] = await tx.insert(this.table).values(scalarFields).returning({ [this.pk]: this.table[this.pk] });
|
|
1263
1296
|
const ownPkValue = created[this.pk];
|
|
1264
1297
|
await resolveFkThereFields(tx, fkThereEntries, ownPkValue);
|
|
1265
1298
|
const readArg = await this.resolveReadArgs(
|
|
@@ -1267,7 +1300,7 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1267
1300
|
options
|
|
1268
1301
|
);
|
|
1269
1302
|
const result = await this.getQueryBuilder(tx).findFirst(readArg);
|
|
1270
|
-
return result
|
|
1303
|
+
return result;
|
|
1271
1304
|
});
|
|
1272
1305
|
} catch (error) {
|
|
1273
1306
|
throw mapDrizzleError(error, "create", this.dialect);
|
|
@@ -1281,7 +1314,7 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1281
1314
|
if (options?.ignoreConflicts) qb = qb.onConflictDoNothing();
|
|
1282
1315
|
const result = await qb;
|
|
1283
1316
|
const affected = resolveRawResult(this.dialect, result, true);
|
|
1284
|
-
return { count: affected
|
|
1317
|
+
return { count: affected };
|
|
1285
1318
|
} catch (error) {
|
|
1286
1319
|
throw mapDrizzleError(error, "createMany", this.dialect);
|
|
1287
1320
|
}
|
|
@@ -1339,42 +1372,53 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1339
1372
|
}
|
|
1340
1373
|
async update(where, obj, options) {
|
|
1341
1374
|
try {
|
|
1342
|
-
return await this.
|
|
1343
|
-
const current = await this.getQueryBuilder(tx).findFirst({
|
|
1344
|
-
where: (await this.resolveFindWhere(where, { db: tx, limit: 1 })).where
|
|
1345
|
-
});
|
|
1346
|
-
if (!current) {
|
|
1347
|
-
throw new VSRepoAdapterError10(
|
|
1348
|
-
"'update' found no record matching the given 'where'.",
|
|
1349
|
-
AdapterErrorCode10.NOT_FOUND,
|
|
1350
|
-
null
|
|
1351
|
-
);
|
|
1352
|
-
}
|
|
1353
|
-
const ownPkValue = current[this.pk];
|
|
1354
|
-
const objAny = obj;
|
|
1355
|
-
const { scalarFields, fkHereEntries, fkThereEntries } = splitWritePayload(
|
|
1356
|
-
objAny,
|
|
1357
|
-
this.relations,
|
|
1358
|
-
this.pk,
|
|
1359
|
-
true
|
|
1360
|
-
);
|
|
1361
|
-
await resolveFkHereFields(tx, fkHereEntries, scalarFields, current);
|
|
1362
|
-
if (Object.keys(scalarFields).length > 0) {
|
|
1363
|
-
const pkColumn = this.table[this.pk];
|
|
1364
|
-
await tx.update(this.table).set(scalarFields).where(eq3(pkColumn, ownPkValue));
|
|
1365
|
-
}
|
|
1366
|
-
await resolveFkThereFields(tx, fkThereEntries, ownPkValue);
|
|
1367
|
-
const readArg = await this.resolveReadArgs(
|
|
1368
|
-
{ [this.pk]: ownPkValue },
|
|
1369
|
-
options
|
|
1370
|
-
);
|
|
1371
|
-
const result = await this.getQueryBuilder(tx).findFirst(readArg);
|
|
1372
|
-
return result ?? current;
|
|
1373
|
-
});
|
|
1375
|
+
return await this.updateCore(where, obj, options);
|
|
1374
1376
|
} catch (error) {
|
|
1375
1377
|
throw mapDrizzleError(error, "update", this.dialect);
|
|
1376
1378
|
}
|
|
1377
1379
|
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Shared implementation behind `update` — also reached from `save` and
|
|
1382
|
+
* `upsert`, which pass in `current` (the row they already fetched in the
|
|
1383
|
+
* same transaction) so the row is read only once instead of once per
|
|
1384
|
+
* method. The `'update' found no record...` contract applies whenever it
|
|
1385
|
+
* runs without a preloaded row (i.e. when called through the public
|
|
1386
|
+
* `update`), and always as a safety net.
|
|
1387
|
+
*
|
|
1388
|
+
* Invariant: when `current` is provided, it must be the full row read
|
|
1389
|
+
* from the exact `tx` referenced by `options.db` (never from outside a
|
|
1390
|
+
* transaction, and never a pk-only projection — `resolveFkHereFields`
|
|
1391
|
+
* reads the current FK values off it).
|
|
1392
|
+
*/
|
|
1393
|
+
async updateCore(where, obj, options, current) {
|
|
1394
|
+
return this.runTransactional(options?.db, async (tx) => {
|
|
1395
|
+
const resolved = current ?? await this.findCurrentByWhere(where, { db: tx });
|
|
1396
|
+
if (!resolved) {
|
|
1397
|
+
throw new VSRepoAdapterError10(
|
|
1398
|
+
"'update' found no record matching the given 'where'.",
|
|
1399
|
+
AdapterErrorCode10.NOT_FOUND,
|
|
1400
|
+
null
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
const ownPkValue = resolved[this.pk];
|
|
1404
|
+
const objAny = obj;
|
|
1405
|
+
const { scalarFields, fkHereEntries, fkThereEntries } = splitWritePayload(
|
|
1406
|
+
objAny,
|
|
1407
|
+
this.relations,
|
|
1408
|
+
this.pk,
|
|
1409
|
+
true
|
|
1410
|
+
);
|
|
1411
|
+
await resolveFkHereFields(tx, fkHereEntries, scalarFields, resolved);
|
|
1412
|
+
if (Object.keys(scalarFields).length > 0) {
|
|
1413
|
+
const pkColumn = this.table[this.pk];
|
|
1414
|
+
await tx.update(this.table).set(scalarFields).where(eq3(pkColumn, ownPkValue));
|
|
1415
|
+
}
|
|
1416
|
+
await resolveFkThereFields(tx, fkThereEntries, ownPkValue);
|
|
1417
|
+
const readArg = await this.resolveReadArgs({ [this.pk]: ownPkValue }, options);
|
|
1418
|
+
const result = await this.getQueryBuilder(tx).findFirst(readArg);
|
|
1419
|
+
return result;
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1378
1422
|
async updateMany(where, obj, options) {
|
|
1379
1423
|
try {
|
|
1380
1424
|
const executor = options?.db ?? this.db;
|
|
@@ -1436,15 +1480,15 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1436
1480
|
async upsert(where, create, update, options) {
|
|
1437
1481
|
try {
|
|
1438
1482
|
return await this.runTransactional(options?.db, async (tx) => {
|
|
1439
|
-
const current = await this.
|
|
1440
|
-
where: (await this.resolveFindWhere(where, { db: tx, limit: 1 })).where
|
|
1441
|
-
});
|
|
1483
|
+
const current = await this.findCurrentByWhere(where, { db: tx });
|
|
1442
1484
|
if (current) {
|
|
1443
1485
|
const ownPkValue = current[this.pk];
|
|
1444
|
-
return this.
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1486
|
+
return this.updateCore(
|
|
1487
|
+
{ [this.pk]: ownPkValue },
|
|
1488
|
+
update,
|
|
1489
|
+
{ ...options, db: tx },
|
|
1490
|
+
current
|
|
1491
|
+
);
|
|
1448
1492
|
}
|
|
1449
1493
|
return this.create(create, { ...options, db: tx });
|
|
1450
1494
|
});
|
|
@@ -1457,7 +1501,11 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1457
1501
|
try {
|
|
1458
1502
|
return await this.runTransactional(options?.db, async (tx) => {
|
|
1459
1503
|
const current = await this.getQueryBuilder(tx).findFirst({
|
|
1460
|
-
where: (await this.resolveFindWhere(where, { db: tx, limit: 1 })).where
|
|
1504
|
+
where: (await this.resolveFindWhere(where, { db: tx, limit: 1 })).where,
|
|
1505
|
+
// Only the pk is needed: atomic updates touch a single numeric
|
|
1506
|
+
// column and never resolve relations — a full-row read here
|
|
1507
|
+
// would ship the whole entity back to the client for nothing.
|
|
1508
|
+
columns: { [this.pk]: true }
|
|
1461
1509
|
});
|
|
1462
1510
|
if (!current) {
|
|
1463
1511
|
throw new VSRepoAdapterError10(
|
|
@@ -1520,6 +1568,7 @@ var DrizzleAdapter = class extends VSRepoAdapter {
|
|
|
1520
1568
|
}
|
|
1521
1569
|
};
|
|
1522
1570
|
export {
|
|
1523
|
-
DrizzleAdapter
|
|
1571
|
+
DrizzleAdapter,
|
|
1572
|
+
DrizzleAdapter as VSRepoDrizzleAdapter
|
|
1524
1573
|
};
|
|
1525
1574
|
//# sourceMappingURL=index.js.map
|