@fragno-dev/db 0.1.13 → 0.1.15
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/.turbo/turbo-build.log +179 -132
- package/CHANGELOG.md +30 -0
- package/dist/adapters/adapters.d.ts +27 -1
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/adapters.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.d.ts +5 -1
- package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.js +15 -3
- package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-query.js +7 -5
- package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +0 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.js +76 -44
- package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-decoder.js +23 -16
- package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-executor.js +18 -7
- package/dist/adapters/drizzle/drizzle-uow-executor.js.map +1 -1
- package/dist/adapters/drizzle/generate.d.ts +4 -1
- package/dist/adapters/drizzle/generate.d.ts.map +1 -1
- package/dist/adapters/drizzle/generate.js +11 -18
- package/dist/adapters/drizzle/generate.js.map +1 -1
- package/dist/adapters/drizzle/shared.d.ts +14 -1
- package/dist/adapters/drizzle/shared.d.ts.map +1 -0
- package/dist/adapters/kysely/kysely-adapter.d.ts +5 -1
- package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
- package/dist/adapters/kysely/kysely-adapter.js +14 -3
- package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
- package/dist/adapters/kysely/kysely-query-builder.js +1 -1
- package/dist/adapters/kysely/kysely-query-compiler.js +3 -2
- package/dist/adapters/kysely/kysely-query-compiler.js.map +1 -1
- package/dist/adapters/kysely/kysely-query.d.ts +1 -0
- package/dist/adapters/kysely/kysely-query.d.ts.map +1 -1
- package/dist/adapters/kysely/kysely-query.js +28 -19
- package/dist/adapters/kysely/kysely-query.js.map +1 -1
- package/dist/adapters/kysely/kysely-shared.d.ts +14 -0
- package/dist/adapters/kysely/kysely-shared.d.ts.map +1 -0
- package/dist/adapters/kysely/kysely-shared.js +16 -1
- package/dist/adapters/kysely/kysely-shared.js.map +1 -1
- package/dist/adapters/kysely/kysely-uow-compiler.js +68 -16
- package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
- package/dist/adapters/kysely/kysely-uow-executor.js +8 -4
- package/dist/adapters/kysely/kysely-uow-executor.js.map +1 -1
- package/dist/adapters/kysely/migration/execute-base.js +1 -1
- package/dist/adapters/kysely/migration/execute-base.js.map +1 -1
- package/dist/db-fragment-definition-builder.d.ts +152 -0
- package/dist/db-fragment-definition-builder.d.ts.map +1 -0
- package/dist/db-fragment-definition-builder.js +137 -0
- package/dist/db-fragment-definition-builder.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +19 -0
- package/dist/fragments/internal-fragment.d.ts.map +1 -0
- package/dist/fragments/internal-fragment.js +39 -0
- package/dist/fragments/internal-fragment.js.map +1 -0
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js +35 -15
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/mod.d.ts +8 -18
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +7 -34
- package/dist/mod.js.map +1 -1
- package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js +165 -0
- package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js.map +1 -0
- package/dist/packages/fragno/dist/api/bind-services.js +20 -0
- package/dist/packages/fragno/dist/api/bind-services.js.map +1 -0
- package/dist/packages/fragno/dist/api/error.js +48 -0
- package/dist/packages/fragno/dist/api/error.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +320 -0
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +487 -0
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragno-response.js +73 -0
- package/dist/packages/fragno/dist/api/fragno-response.js.map +1 -0
- package/dist/packages/fragno/dist/api/internal/response-stream.js +81 -0
- package/dist/packages/fragno/dist/api/internal/response-stream.js.map +1 -0
- package/dist/packages/fragno/dist/api/internal/route.js +10 -0
- package/dist/packages/fragno/dist/api/internal/route.js.map +1 -0
- package/dist/packages/fragno/dist/api/mutable-request-state.js +97 -0
- package/dist/packages/fragno/dist/api/mutable-request-state.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-context-storage.js +43 -0
- package/dist/packages/fragno/dist/api/request-context-storage.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-input-context.js +118 -0
- package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-middleware.js +83 -0
- package/dist/packages/fragno/dist/api/request-middleware.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-output-context.js +119 -0
- package/dist/packages/fragno/dist/api/request-output-context.js.map +1 -0
- package/dist/packages/fragno/dist/api/route.js +17 -0
- package/dist/packages/fragno/dist/api/route.js.map +1 -0
- package/dist/packages/fragno/dist/internal/symbols.js +10 -0
- package/dist/packages/fragno/dist/internal/symbols.js.map +1 -0
- package/dist/query/cursor.d.ts +10 -2
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +11 -4
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/execute-unit-of-work.d.ts +123 -0
- package/dist/query/execute-unit-of-work.d.ts.map +1 -0
- package/dist/query/execute-unit-of-work.js +184 -0
- package/dist/query/execute-unit-of-work.js.map +1 -0
- package/dist/query/query.d.ts +3 -3
- package/dist/query/query.d.ts.map +1 -1
- package/dist/query/result-transform.js +4 -2
- package/dist/query/result-transform.js.map +1 -1
- package/dist/query/retry-policy.d.ts +88 -0
- package/dist/query/retry-policy.d.ts.map +1 -0
- package/dist/query/retry-policy.js +61 -0
- package/dist/query/retry-policy.js.map +1 -0
- package/dist/query/unit-of-work.d.ts +171 -32
- package/dist/query/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work.js +530 -133
- package/dist/query/unit-of-work.js.map +1 -1
- package/dist/schema/serialize.js +12 -7
- package/dist/schema/serialize.js.map +1 -1
- package/dist/with-database.d.ts +28 -0
- package/dist/with-database.d.ts.map +1 -0
- package/dist/with-database.js +34 -0
- package/dist/with-database.js.map +1 -0
- package/package.json +10 -3
- package/src/adapters/adapters.ts +30 -0
- package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +86 -17
- package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +291 -7
- package/src/adapters/drizzle/drizzle-adapter.test.ts +3 -51
- package/src/adapters/drizzle/drizzle-adapter.ts +35 -7
- package/src/adapters/drizzle/drizzle-query.ts +25 -15
- package/src/adapters/drizzle/drizzle-uow-compiler-mysql.test.ts +1442 -0
- package/src/adapters/drizzle/drizzle-uow-compiler-sqlite.test.ts +1414 -0
- package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +78 -61
- package/src/adapters/drizzle/drizzle-uow-compiler.ts +123 -42
- package/src/adapters/drizzle/drizzle-uow-decoder.ts +34 -27
- package/src/adapters/drizzle/drizzle-uow-executor.ts +41 -8
- package/src/adapters/drizzle/generate.test.ts +102 -269
- package/src/adapters/drizzle/generate.ts +12 -30
- package/src/adapters/drizzle/test-utils.ts +36 -5
- package/src/adapters/kysely/kysely-adapter-pglite.test.ts +66 -22
- package/src/adapters/kysely/kysely-adapter-sqlite.test.ts +156 -0
- package/src/adapters/kysely/kysely-adapter.ts +25 -2
- package/src/adapters/kysely/kysely-query-compiler.ts +3 -8
- package/src/adapters/kysely/kysely-query.ts +57 -37
- package/src/adapters/kysely/kysely-shared.ts +34 -0
- package/src/adapters/kysely/kysely-uow-compiler.test.ts +62 -74
- package/src/adapters/kysely/kysely-uow-compiler.ts +92 -24
- package/src/adapters/kysely/kysely-uow-executor.ts +26 -7
- package/src/adapters/kysely/kysely-uow-joins.test.ts +33 -50
- package/src/adapters/kysely/migration/execute-base.ts +1 -1
- package/src/db-fragment-definition-builder.test.ts +887 -0
- package/src/db-fragment-definition-builder.ts +506 -0
- package/src/db-fragment-instantiator.test.ts +467 -0
- package/src/db-fragment-integration.test.ts +408 -0
- package/src/fragments/internal-fragment.test.ts +160 -0
- package/src/fragments/internal-fragment.ts +85 -0
- package/src/migration-engine/generation-engine.test.ts +58 -15
- package/src/migration-engine/generation-engine.ts +78 -25
- package/src/mod.ts +35 -43
- package/src/query/cursor.test.ts +119 -0
- package/src/query/cursor.ts +17 -4
- package/src/query/execute-unit-of-work.test.ts +1310 -0
- package/src/query/execute-unit-of-work.ts +463 -0
- package/src/query/query.ts +4 -4
- package/src/query/result-transform.test.ts +129 -0
- package/src/query/result-transform.ts +4 -1
- package/src/query/retry-policy.test.ts +217 -0
- package/src/query/retry-policy.ts +141 -0
- package/src/query/unit-of-work-coordinator.test.ts +833 -0
- package/src/query/unit-of-work-types.test.ts +15 -2
- package/src/query/unit-of-work.test.ts +878 -200
- package/src/query/unit-of-work.ts +963 -321
- package/src/schema/serialize.ts +22 -11
- package/src/with-database.ts +140 -0
- package/tsdown.config.ts +1 -0
- package/dist/fragment.d.ts +0 -54
- package/dist/fragment.d.ts.map +0 -1
- package/dist/fragment.js +0 -92
- package/dist/fragment.js.map +0 -1
- package/dist/shared/settings-schema.js +0 -36
- package/dist/shared/settings-schema.js.map +0 -1
- package/src/fragment.test.ts +0 -341
- package/src/fragment.ts +0 -198
- package/src/shared/settings-schema.ts +0 -61
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type TableNameMapper,
|
|
15
15
|
parseDrizzle,
|
|
16
16
|
type DBType,
|
|
17
|
+
createTableNameMapper,
|
|
17
18
|
} from "./shared";
|
|
18
19
|
import { encodeValues, ReferenceSubquery } from "../../query/result-transform";
|
|
19
20
|
import { serialize } from "../../schema/serialize";
|
|
@@ -33,30 +34,42 @@ export type DrizzleCompiledQuery = {
|
|
|
33
34
|
* This compiler translates UOW operations into Drizzle query functions
|
|
34
35
|
* that can be executed as a batch/transaction.
|
|
35
36
|
*
|
|
36
|
-
* @param schema - The database schema
|
|
37
37
|
* @param pool - Connection pool for acquiring database connections
|
|
38
38
|
* @param provider - SQL provider (sqlite, mysql, postgresql)
|
|
39
|
-
* @param mapper - Optional table name mapper for namespace prefixing
|
|
39
|
+
* @param mapper - Optional table name mapper for namespace prefixing (fallback for operations without explicit namespace)
|
|
40
40
|
* @returns A UOWCompiler instance for Drizzle
|
|
41
41
|
*/
|
|
42
|
-
export function createDrizzleUOWCompiler
|
|
43
|
-
schema: TSchema,
|
|
42
|
+
export function createDrizzleUOWCompiler(
|
|
44
43
|
pool: ConnectionPool<DBType>,
|
|
45
44
|
provider: "sqlite" | "mysql" | "postgresql",
|
|
46
45
|
mapper?: TableNameMapper,
|
|
47
|
-
): UOWCompiler<
|
|
46
|
+
): UOWCompiler<DrizzleCompiledQuery> {
|
|
48
47
|
// Get db synchronously for compilation (doesn't execute, just builds SQL)
|
|
49
48
|
// TODO: We don't even need a Drizzle instance with a db client attached here. `drizzle({ schema })` is enough.
|
|
50
49
|
const dbRaw = pool.getDatabaseSync();
|
|
51
50
|
const [db, drizzleTables] = parseDrizzle(dbRaw);
|
|
52
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Get the mapper for a specific operation
|
|
54
|
+
* Uses operation's namespace if provided, otherwise falls back to the default mapper
|
|
55
|
+
*/
|
|
56
|
+
function getMapperForOperation(namespace: string | undefined): TableNameMapper | undefined {
|
|
57
|
+
if (namespace) {
|
|
58
|
+
return createTableNameMapper(namespace);
|
|
59
|
+
}
|
|
60
|
+
return mapper;
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
/**
|
|
54
64
|
* Convert a Fragno table to a Drizzle table
|
|
55
65
|
* @throws Error if table is not found in Drizzle schema
|
|
56
66
|
*/
|
|
57
|
-
function toDrizzleTable(table: AnyTable): TableType {
|
|
58
|
-
//
|
|
59
|
-
const
|
|
67
|
+
function toDrizzleTable(table: AnyTable, namespace: string | undefined): TableType {
|
|
68
|
+
// Get the mapper for this operation's namespace
|
|
69
|
+
const opMapper = getMapperForOperation(namespace);
|
|
70
|
+
|
|
71
|
+
// Map logical table name to physical table name using the operation-specific mapper
|
|
72
|
+
const physicalTableName = opMapper ? opMapper.toPhysical(table.ormName) : table.ormName;
|
|
60
73
|
const out = drizzleTables[physicalTableName];
|
|
61
74
|
if (out) {
|
|
62
75
|
return out;
|
|
@@ -71,13 +84,17 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
71
84
|
* Convert a Fragno column to a Drizzle column
|
|
72
85
|
* @throws Error if column is not found in Drizzle table
|
|
73
86
|
*/
|
|
74
|
-
function toDrizzleColumn(
|
|
87
|
+
function toDrizzleColumn(
|
|
88
|
+
schema: AnySchema,
|
|
89
|
+
namespace: string | undefined,
|
|
90
|
+
col: AnyColumn,
|
|
91
|
+
): ColumnType {
|
|
75
92
|
const fragnoTable = schema.tables[col.tableName];
|
|
76
93
|
if (!fragnoTable) {
|
|
77
94
|
throw new Error(`[Drizzle] Unknown table ${col.tableName} for column ${col.ormName}.`);
|
|
78
95
|
}
|
|
79
96
|
|
|
80
|
-
const table = toDrizzleTable(fragnoTable);
|
|
97
|
+
const table = toDrizzleTable(fragnoTable, namespace);
|
|
81
98
|
const out = table[col.ormName];
|
|
82
99
|
if (out) {
|
|
83
100
|
return out;
|
|
@@ -89,13 +106,17 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
89
106
|
/**
|
|
90
107
|
* Build a WHERE clause from a condition using Drizzle's query builder
|
|
91
108
|
*/
|
|
92
|
-
function buildWhere(
|
|
109
|
+
function buildWhere(
|
|
110
|
+
schema: AnySchema,
|
|
111
|
+
namespace: string | undefined,
|
|
112
|
+
condition: Condition,
|
|
113
|
+
): Drizzle.SQL | undefined {
|
|
93
114
|
if (condition.type === "compare") {
|
|
94
|
-
const left = toDrizzleColumn(condition.a);
|
|
115
|
+
const left = toDrizzleColumn(schema, namespace, condition.a);
|
|
95
116
|
const op = condition.operator;
|
|
96
117
|
let right = condition.b;
|
|
97
118
|
if (right instanceof Column) {
|
|
98
|
-
right = toDrizzleColumn(right);
|
|
119
|
+
right = toDrizzleColumn(schema, namespace, right);
|
|
99
120
|
} else {
|
|
100
121
|
// Handle string references - convert external ID to internal ID via subquery
|
|
101
122
|
if (condition.a.role === "reference" && typeof right === "string") {
|
|
@@ -181,11 +202,11 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
181
202
|
}
|
|
182
203
|
|
|
183
204
|
if (condition.type === "and") {
|
|
184
|
-
return Drizzle.and(...condition.items.map((item) => buildWhere(item)));
|
|
205
|
+
return Drizzle.and(...condition.items.map((item) => buildWhere(schema, namespace, item)));
|
|
185
206
|
}
|
|
186
207
|
|
|
187
208
|
if (condition.type === "not") {
|
|
188
|
-
const result = buildWhere(condition.item);
|
|
209
|
+
const result = buildWhere(schema, namespace, condition.item);
|
|
189
210
|
if (!result) {
|
|
190
211
|
return;
|
|
191
212
|
}
|
|
@@ -193,7 +214,7 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
193
214
|
return Drizzle.not(result);
|
|
194
215
|
}
|
|
195
216
|
|
|
196
|
-
return Drizzle.or(...condition.items.map((item) => buildWhere(item)));
|
|
217
|
+
return Drizzle.or(...condition.items.map((item) => buildWhere(schema, namespace, item)));
|
|
197
218
|
}
|
|
198
219
|
|
|
199
220
|
/**
|
|
@@ -229,7 +250,7 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
229
250
|
* Get table from schema by name
|
|
230
251
|
* @throws Error if table is not found in schema
|
|
231
252
|
*/
|
|
232
|
-
function getTable(name: unknown): AnyTable {
|
|
253
|
+
function getTable(schema: AnySchema, name: unknown): AnyTable {
|
|
233
254
|
const table = schema.tables[name as string];
|
|
234
255
|
if (!table) {
|
|
235
256
|
throw new Error(`Invalid table name ${name}.`);
|
|
@@ -260,6 +281,8 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
260
281
|
* Process joins recursively to support nested joins with orderBy and limit
|
|
261
282
|
*/
|
|
262
283
|
function processJoins(
|
|
284
|
+
schema: AnySchema,
|
|
285
|
+
namespace: string | undefined,
|
|
263
286
|
joins: CompiledJoin[],
|
|
264
287
|
): Record<string, Drizzle.DBQueryConfig<"many", boolean>> {
|
|
265
288
|
const result: Record<string, Drizzle.DBQueryConfig<"many", boolean>> = {};
|
|
@@ -286,7 +309,7 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
286
309
|
let joinOrderBy: Drizzle.SQL[] | undefined;
|
|
287
310
|
if (options.orderBy && options.orderBy.length > 0) {
|
|
288
311
|
joinOrderBy = options.orderBy.map(([col, direction]) => {
|
|
289
|
-
const drizzleCol = toDrizzleColumn(col);
|
|
312
|
+
const drizzleCol = toDrizzleColumn(schema, namespace, col);
|
|
290
313
|
return direction === "asc" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);
|
|
291
314
|
});
|
|
292
315
|
}
|
|
@@ -294,7 +317,7 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
294
317
|
// Build WHERE clause for this join if provided
|
|
295
318
|
let joinWhere: Drizzle.SQL | undefined;
|
|
296
319
|
if (options.where) {
|
|
297
|
-
joinWhere = buildWhere(options.where);
|
|
320
|
+
joinWhere = buildWhere(schema, namespace, options.where);
|
|
298
321
|
}
|
|
299
322
|
|
|
300
323
|
// Build the join config
|
|
@@ -307,7 +330,7 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
307
330
|
|
|
308
331
|
// Recursively process nested joins
|
|
309
332
|
if (options.join && options.join.length > 0) {
|
|
310
|
-
joinConfig.with = processJoins(options.join);
|
|
333
|
+
joinConfig.with = processJoins(schema, namespace, options.join);
|
|
311
334
|
}
|
|
312
335
|
|
|
313
336
|
result[joinName] = joinConfig;
|
|
@@ -317,7 +340,8 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
317
340
|
}
|
|
318
341
|
|
|
319
342
|
return {
|
|
320
|
-
compileRetrievalOperation(op: RetrievalOperation<
|
|
343
|
+
compileRetrievalOperation(op: RetrievalOperation<AnySchema>): DrizzleCompiledQuery | null {
|
|
344
|
+
const schema = op.schema;
|
|
321
345
|
switch (op.type) {
|
|
322
346
|
case "count": {
|
|
323
347
|
// Build WHERE clause
|
|
@@ -329,11 +353,11 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
329
353
|
return null;
|
|
330
354
|
}
|
|
331
355
|
if (condition !== true) {
|
|
332
|
-
whereClause = buildWhere(condition);
|
|
356
|
+
whereClause = buildWhere(schema, op.namespace, condition);
|
|
333
357
|
}
|
|
334
358
|
}
|
|
335
359
|
|
|
336
|
-
const drizzleTable = toDrizzleTable(op.table);
|
|
360
|
+
const drizzleTable = toDrizzleTable(op.table, op.namespace);
|
|
337
361
|
const query = db.select({ count: Drizzle.count() }).from(drizzleTable);
|
|
338
362
|
|
|
339
363
|
const compiledQuery = whereClause ? query.where(whereClause).toSQL() : query.toSQL();
|
|
@@ -377,7 +401,7 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
377
401
|
let orderBy: Drizzle.SQL[] | undefined;
|
|
378
402
|
if (indexColumns.length > 0) {
|
|
379
403
|
orderBy = indexColumns.map((col) => {
|
|
380
|
-
const drizzleCol = toDrizzleColumn(col);
|
|
404
|
+
const drizzleCol = toDrizzleColumn(schema, op.namespace, col);
|
|
381
405
|
return orderDirection === "asc" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);
|
|
382
406
|
});
|
|
383
407
|
}
|
|
@@ -413,7 +437,7 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
413
437
|
return null;
|
|
414
438
|
}
|
|
415
439
|
if (condition !== true) {
|
|
416
|
-
const clause = buildWhere(condition);
|
|
440
|
+
const clause = buildWhere(schema, op.namespace, condition);
|
|
417
441
|
if (clause) {
|
|
418
442
|
whereClauses.push(clause);
|
|
419
443
|
}
|
|
@@ -436,12 +460,12 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
436
460
|
|
|
437
461
|
if (indexColumns.length === 1) {
|
|
438
462
|
// Simple single-column case
|
|
439
|
-
const col = toDrizzleColumn(indexColumns[0]!);
|
|
463
|
+
const col = toDrizzleColumn(schema, op.namespace, indexColumns[0]!);
|
|
440
464
|
const val = serializedValues[indexColumns[0]!.ormName];
|
|
441
465
|
whereClauses.push(useGreaterThan ? Drizzle.gt(col, val) : Drizzle.lt(col, val));
|
|
442
466
|
} else {
|
|
443
467
|
// Multi-column tuple comparison using SQL
|
|
444
|
-
const drizzleCols = indexColumns.map((c) => toDrizzleColumn(c));
|
|
468
|
+
const drizzleCols = indexColumns.map((c) => toDrizzleColumn(schema, op.namespace, c));
|
|
445
469
|
const vals = indexColumns.map((c) => serializedValues[c.ormName]);
|
|
446
470
|
const operator = useGreaterThan ? ">" : "<";
|
|
447
471
|
// Safe cast: building a SQL comparison expression for cursor pagination
|
|
@@ -459,9 +483,13 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
459
483
|
|
|
460
484
|
const whereClause = whereClauses.length > 0 ? Drizzle.and(...whereClauses) : undefined;
|
|
461
485
|
|
|
486
|
+
// For cursor pagination, fetch one extra item to determine if there's a next page
|
|
487
|
+
// Only apply this when using the high-level findWithCursor() API (op.withCursor === true)
|
|
488
|
+
const effectiveLimit = pageSize && op.withCursor ? pageSize + 1 : pageSize;
|
|
489
|
+
|
|
462
490
|
const queryConfig: Drizzle.DBQueryConfig<"many", boolean> = {
|
|
463
491
|
columns,
|
|
464
|
-
limit:
|
|
492
|
+
limit: effectiveLimit,
|
|
465
493
|
where: whereClause,
|
|
466
494
|
orderBy,
|
|
467
495
|
with: {},
|
|
@@ -469,39 +497,54 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
469
497
|
|
|
470
498
|
// Process joins recursively to support nested joins
|
|
471
499
|
if (joins) {
|
|
472
|
-
queryConfig.with = processJoins(joins);
|
|
500
|
+
queryConfig.with = processJoins(schema, op.namespace, joins);
|
|
473
501
|
}
|
|
474
502
|
|
|
475
|
-
|
|
476
|
-
const
|
|
503
|
+
// For multi-schema support: get the mapper for the operation's namespace
|
|
504
|
+
const opMapper = getMapperForOperation(op.namespace);
|
|
505
|
+
const physicalTableName = opMapper
|
|
506
|
+
? opMapper.toPhysical(op.table.ormName)
|
|
507
|
+
: op.table.ormName;
|
|
508
|
+
const tableQuery = db.query[physicalTableName];
|
|
509
|
+
|
|
510
|
+
if (!tableQuery) {
|
|
511
|
+
throw new Error(
|
|
512
|
+
`[Drizzle] Table ${op.table.ormName} (physical: ${physicalTableName}) not found in db.query. ` +
|
|
513
|
+
`Available tables: ${Object.keys(db.query).join(", ")}`,
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const compiledQuery = tableQuery.findMany(queryConfig).toSQL();
|
|
477
518
|
return compiledQuery;
|
|
478
519
|
}
|
|
479
520
|
}
|
|
480
521
|
},
|
|
481
522
|
|
|
482
523
|
compileMutationOperation(
|
|
483
|
-
op: MutationOperation<
|
|
524
|
+
op: MutationOperation<AnySchema>,
|
|
484
525
|
): CompiledMutation<DrizzleCompiledQuery> | null {
|
|
526
|
+
const schema = op.schema;
|
|
485
527
|
switch (op.type) {
|
|
486
528
|
case "create": {
|
|
487
|
-
const table = getTable(op.table);
|
|
488
|
-
const drizzleTable = toDrizzleTable(table);
|
|
529
|
+
const table = getTable(schema, op.table);
|
|
530
|
+
const drizzleTable = toDrizzleTable(table, op.namespace);
|
|
489
531
|
// encodeValues now handles runtime defaults automatically
|
|
490
|
-
const encodedValues = encodeValues(op.values, table, true, provider);
|
|
532
|
+
const encodedValues = encodeValues(op.values, table, true, provider, true);
|
|
491
533
|
const values = processReferenceSubqueries(encodedValues);
|
|
492
534
|
|
|
493
535
|
const compiledQuery = db.insert(drizzleTable).values(values).toSQL();
|
|
494
536
|
return {
|
|
495
537
|
query: compiledQuery,
|
|
496
538
|
expectedAffectedRows: null, // creates don't need affected row checks
|
|
539
|
+
expectedReturnedRows: null,
|
|
497
540
|
};
|
|
498
541
|
}
|
|
499
542
|
|
|
500
543
|
case "update": {
|
|
501
|
-
const table = getTable(op.table);
|
|
544
|
+
const table = getTable(schema, op.table);
|
|
502
545
|
const idColumn = table.getIdColumn();
|
|
503
546
|
const versionColumn = table.getVersionColumn();
|
|
504
|
-
const drizzleTable = toDrizzleTable(table);
|
|
547
|
+
const drizzleTable = toDrizzleTable(table, op.namespace);
|
|
505
548
|
|
|
506
549
|
const externalId = typeof op.id === "string" ? op.id : op.id.externalId;
|
|
507
550
|
const versionToCheck = getVersionToCheck(op.id, op.checkVersion);
|
|
@@ -523,8 +566,9 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
523
566
|
return null;
|
|
524
567
|
}
|
|
525
568
|
|
|
526
|
-
const whereClause =
|
|
527
|
-
|
|
569
|
+
const whereClause =
|
|
570
|
+
condition === true ? undefined : buildWhere(schema, op.namespace, condition);
|
|
571
|
+
const encodedSetValues = encodeValues(op.set, table, false, provider, true);
|
|
528
572
|
const setValues = processReferenceSubqueries(encodedSetValues);
|
|
529
573
|
|
|
530
574
|
// Automatically increment _version for optimistic concurrency control
|
|
@@ -537,14 +581,15 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
537
581
|
return {
|
|
538
582
|
query: compiledQuery,
|
|
539
583
|
expectedAffectedRows: op.checkVersion ? 1 : null,
|
|
584
|
+
expectedReturnedRows: null,
|
|
540
585
|
};
|
|
541
586
|
}
|
|
542
587
|
|
|
543
588
|
case "delete": {
|
|
544
|
-
const table = getTable(op.table);
|
|
589
|
+
const table = getTable(schema, op.table);
|
|
545
590
|
const idColumn = table.getIdColumn();
|
|
546
591
|
const versionColumn = table.getVersionColumn();
|
|
547
|
-
const drizzleTable = toDrizzleTable(table);
|
|
592
|
+
const drizzleTable = toDrizzleTable(table, op.namespace);
|
|
548
593
|
|
|
549
594
|
if (!op.id) {
|
|
550
595
|
throw new Error(
|
|
@@ -582,12 +627,48 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
|
|
|
582
627
|
return null;
|
|
583
628
|
}
|
|
584
629
|
|
|
585
|
-
const whereClause =
|
|
630
|
+
const whereClause =
|
|
631
|
+
condition === true ? undefined : buildWhere(schema, op.namespace, condition);
|
|
586
632
|
|
|
587
633
|
const compiledQuery = db.delete(drizzleTable).where(whereClause).toSQL();
|
|
588
634
|
return {
|
|
589
635
|
query: compiledQuery,
|
|
590
636
|
expectedAffectedRows: op.checkVersion ? 1 : null,
|
|
637
|
+
expectedReturnedRows: null,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
case "check": {
|
|
642
|
+
const table = getTable(schema, op.table);
|
|
643
|
+
const idColumn = table.getIdColumn();
|
|
644
|
+
const versionColumn = table.getVersionColumn();
|
|
645
|
+
const drizzleTable = toDrizzleTable(table, op.namespace);
|
|
646
|
+
|
|
647
|
+
const externalId = op.id.externalId;
|
|
648
|
+
const version = op.id.version;
|
|
649
|
+
|
|
650
|
+
// Build WHERE clause that filters by ID and version
|
|
651
|
+
const condition = buildCondition(table.columns, (eb) =>
|
|
652
|
+
eb.and(eb(idColumn.ormName, "=", externalId), eb(versionColumn.ormName, "=", version)),
|
|
653
|
+
);
|
|
654
|
+
|
|
655
|
+
if (typeof condition === "boolean") {
|
|
656
|
+
throw new Error("Condition is a boolean, but should be a condition object.");
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Build a SELECT query to check if the row exists with the correct version
|
|
660
|
+
// Use sql`1` to select a constant with an alias
|
|
661
|
+
const compiledQuery = db
|
|
662
|
+
.select({ exists: Drizzle.sql<number>`1`.as("exists") })
|
|
663
|
+
.from(drizzleTable)
|
|
664
|
+
.where(buildWhere(schema, op.namespace, condition))
|
|
665
|
+
.limit(1)
|
|
666
|
+
.toSQL();
|
|
667
|
+
|
|
668
|
+
return {
|
|
669
|
+
query: compiledQuery,
|
|
670
|
+
expectedAffectedRows: null,
|
|
671
|
+
expectedReturnedRows: 1, // Check that exactly 1 row was returned
|
|
591
672
|
};
|
|
592
673
|
}
|
|
593
674
|
}
|
|
@@ -143,17 +143,14 @@ function transformJoinArraysToObjects(
|
|
|
143
143
|
return transformedRow;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
export function createDrizzleUOWDecoder
|
|
147
|
-
_schema: TSchema,
|
|
148
|
-
provider: SQLProvider,
|
|
149
|
-
): UOWDecoder<TSchema, DrizzleResult> {
|
|
146
|
+
export function createDrizzleUOWDecoder(provider: SQLProvider): UOWDecoder<DrizzleResult> {
|
|
150
147
|
return (rawResults, ops) => {
|
|
151
148
|
if (rawResults.length !== ops.length) {
|
|
152
149
|
throw new Error("rawResults and ops must have the same length");
|
|
153
150
|
}
|
|
154
151
|
|
|
155
152
|
return rawResults.map((result, index) => {
|
|
156
|
-
const op = ops[index] as RetrievalOperation<
|
|
153
|
+
const op = ops[index] as RetrievalOperation<AnySchema>;
|
|
157
154
|
if (!op) {
|
|
158
155
|
throw new Error("op must be defined");
|
|
159
156
|
}
|
|
@@ -182,35 +179,45 @@ export function createDrizzleUOWDecoder<TSchema extends AnySchema>(
|
|
|
182
179
|
// If cursor generation is requested, wrap in CursorResult
|
|
183
180
|
if (op.withCursor) {
|
|
184
181
|
let cursor: Cursor | undefined;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
182
|
+
let hasNextPage = false;
|
|
183
|
+
let items = decodedRows;
|
|
184
|
+
|
|
185
|
+
// Check if there are more results (we fetched pageSize + 1)
|
|
186
|
+
if (op.options.pageSize && decodedRows.length > op.options.pageSize) {
|
|
187
|
+
hasNextPage = true;
|
|
188
|
+
// Trim to requested pageSize
|
|
189
|
+
items = decodedRows.slice(0, op.options.pageSize);
|
|
190
|
+
|
|
191
|
+
// Generate cursor from the last item we're returning
|
|
192
|
+
if (op.options.orderByIndex) {
|
|
193
|
+
const lastItem = items[items.length - 1];
|
|
194
|
+
const indexName = op.options.orderByIndex.indexName;
|
|
195
|
+
|
|
196
|
+
// Get index columns
|
|
197
|
+
let indexColumns;
|
|
198
|
+
if (indexName === "_primary") {
|
|
199
|
+
indexColumns = [op.table.getIdColumn()];
|
|
200
|
+
} else {
|
|
201
|
+
const index = op.table.indexes[indexName];
|
|
202
|
+
if (index) {
|
|
203
|
+
indexColumns = index.columns;
|
|
204
|
+
}
|
|
199
205
|
}
|
|
200
|
-
}
|
|
201
206
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
207
|
+
if (indexColumns && lastItem) {
|
|
208
|
+
cursor = createCursorFromRecord(lastItem as Record<string, unknown>, indexColumns, {
|
|
209
|
+
indexName: op.options.orderByIndex.indexName,
|
|
210
|
+
orderDirection: op.options.orderByIndex.direction,
|
|
211
|
+
pageSize: op.options.pageSize,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
208
214
|
}
|
|
209
215
|
}
|
|
210
216
|
|
|
211
217
|
const cursorResult: CursorResult<unknown> = {
|
|
212
|
-
items
|
|
218
|
+
items,
|
|
213
219
|
cursor,
|
|
220
|
+
hasNextPage,
|
|
214
221
|
};
|
|
215
222
|
return cursorResult;
|
|
216
223
|
}
|
|
@@ -75,7 +75,6 @@ function toSQL(query: DrizzleCompiledQuery, provider: "sqlite" | "mysql" | "post
|
|
|
75
75
|
|
|
76
76
|
const queryChunks =
|
|
77
77
|
provider === "sqlite" ? sqliteToSQL(sqlString, params) : postgresToSQL(sqlString, params);
|
|
78
|
-
|
|
79
78
|
return new SQL(queryChunks);
|
|
80
79
|
}
|
|
81
80
|
|
|
@@ -160,7 +159,24 @@ function extractCreatedInternalId(result: unknown): bigint | null {
|
|
|
160
159
|
return null;
|
|
161
160
|
}
|
|
162
161
|
|
|
162
|
+
function validateReturnedRows(result: unknown, expected: number): void {
|
|
163
|
+
// For SELECT queries, check the number of rows returned
|
|
164
|
+
// Result can be an array (sync) or an object with rows property (async)
|
|
165
|
+
let rowCount = 0;
|
|
166
|
+
if (Array.isArray(result)) {
|
|
167
|
+
rowCount = result.length;
|
|
168
|
+
} else if (result && typeof result === "object" && "rows" in result) {
|
|
169
|
+
const rows = (result as { rows: unknown[] }).rows;
|
|
170
|
+
rowCount = Array.isArray(rows) ? rows.length : 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (rowCount !== expected) {
|
|
174
|
+
throw new Error(`Version conflict: expected ${expected} rows returned, but got ${rowCount}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
163
178
|
function validateAffectedRows(result: unknown, expected: number): void {
|
|
179
|
+
// For UPDATE/DELETE queries, check affected rows
|
|
164
180
|
const actual = getAffectedRows(result);
|
|
165
181
|
if (actual !== expected) {
|
|
166
182
|
throw new Error(`Version conflict: expected ${expected} rows affected, but got ${actual}`);
|
|
@@ -235,21 +251,32 @@ export async function executeDrizzleMutationPhase(
|
|
|
235
251
|
db,
|
|
236
252
|
provider,
|
|
237
253
|
(syncDb) => {
|
|
238
|
-
for (const { query, expectedAffectedRows } of mutationBatch) {
|
|
254
|
+
for (const { query, expectedAffectedRows, expectedReturnedRows } of mutationBatch) {
|
|
239
255
|
const sqlObj = toSQL(query, provider);
|
|
256
|
+
|
|
240
257
|
// Type assertion needed due to drizzle-orm version mismatch in dependencies
|
|
241
|
-
const result =
|
|
258
|
+
const result =
|
|
259
|
+
expectedReturnedRows !== null
|
|
260
|
+
? syncDb.all(sqlObj as never)
|
|
261
|
+
: syncDb.run(sqlObj as never);
|
|
242
262
|
|
|
243
|
-
if (expectedAffectedRows === null) {
|
|
263
|
+
if (expectedAffectedRows === null && expectedReturnedRows === null) {
|
|
244
264
|
createdInternalIds.push(extractCreatedInternalId(result));
|
|
245
|
-
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (expectedReturnedRows !== null) {
|
|
268
|
+
validateReturnedRows(result, expectedReturnedRows);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (expectedAffectedRows !== null) {
|
|
246
272
|
validateAffectedRows(result, expectedAffectedRows);
|
|
247
273
|
}
|
|
248
274
|
}
|
|
249
275
|
},
|
|
250
276
|
async (tx) => {
|
|
251
|
-
for (const { query, expectedAffectedRows } of mutationBatch) {
|
|
277
|
+
for (const { query, expectedAffectedRows, expectedReturnedRows } of mutationBatch) {
|
|
252
278
|
const sqlObj = toSQL(query, provider);
|
|
279
|
+
|
|
253
280
|
// Fallback to run when execute is not available (e.g., libsql)
|
|
254
281
|
const executeMethod = tx.execute ?? tx.run;
|
|
255
282
|
if (!executeMethod) {
|
|
@@ -257,9 +284,15 @@ export async function executeDrizzleMutationPhase(
|
|
|
257
284
|
}
|
|
258
285
|
const result = await executeMethod.call(tx, sqlObj);
|
|
259
286
|
|
|
260
|
-
if (expectedAffectedRows === null) {
|
|
287
|
+
if (expectedAffectedRows === null && expectedReturnedRows === null) {
|
|
261
288
|
createdInternalIds.push(extractCreatedInternalId(result));
|
|
262
|
-
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (expectedReturnedRows !== null) {
|
|
292
|
+
validateReturnedRows(result, expectedReturnedRows);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (expectedAffectedRows !== null) {
|
|
263
296
|
validateAffectedRows(result, expectedAffectedRows);
|
|
264
297
|
}
|
|
265
298
|
}
|