@fragno-dev/db 0.1.1 → 0.1.2
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 +61 -53
- package/CHANGELOG.md +12 -0
- package/dist/adapters/adapters.d.ts +11 -1
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.d.ts +9 -2
- package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.js +21 -39
- package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-query.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-query.js +3 -2
- package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.js +8 -6
- package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-executor.js.map +1 -1
- package/dist/adapters/drizzle/generate.js +107 -34
- package/dist/adapters/drizzle/generate.js.map +1 -1
- package/dist/adapters/drizzle/shared.js +14 -1
- package/dist/adapters/drizzle/shared.js.map +1 -1
- package/dist/adapters/kysely/kysely-adapter.d.ts +2 -1
- package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
- package/dist/adapters/kysely/kysely-adapter.js +25 -30
- package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
- package/dist/adapters/kysely/kysely-query-builder.js +48 -44
- package/dist/adapters/kysely/kysely-query-builder.js.map +1 -1
- package/dist/adapters/kysely/kysely-query-compiler.js +2 -2
- package/dist/adapters/kysely/kysely-query-compiler.js.map +1 -1
- package/dist/adapters/kysely/kysely-query.js +3 -2
- package/dist/adapters/kysely/kysely-query.js.map +1 -1
- package/dist/adapters/kysely/kysely-shared.js +18 -0
- package/dist/adapters/kysely/kysely-shared.js.map +1 -0
- package/dist/adapters/kysely/kysely-uow-compiler.js +4 -3
- package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
- package/dist/adapters/kysely/migration/execute.js +15 -12
- package/dist/adapters/kysely/migration/execute.js.map +1 -1
- package/dist/migration-engine/auto-from-schema.js +2 -8
- package/dist/migration-engine/auto-from-schema.js.map +1 -1
- package/dist/migration-engine/create.d.ts +1 -5
- package/dist/migration-engine/create.js +1 -1
- package/dist/migration-engine/create.js.map +1 -1
- package/dist/migration-engine/generation-engine.d.ts +51 -0
- package/dist/migration-engine/generation-engine.d.ts.map +1 -0
- package/dist/migration-engine/generation-engine.js +165 -0
- package/dist/migration-engine/generation-engine.js.map +1 -0
- package/dist/migration-engine/shared.d.ts +5 -2
- package/dist/migration-engine/shared.d.ts.map +1 -1
- package/dist/migration-engine/shared.js.map +1 -1
- package/dist/mod.d.ts +0 -8
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +0 -32
- package/dist/mod.js.map +1 -1
- package/dist/query/condition-builder.js.map +1 -1
- package/dist/query/result-transform.js +2 -1
- package/dist/query/result-transform.js.map +1 -1
- package/dist/schema/create.d.ts +74 -16
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +76 -11
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/serialize.js.map +1 -1
- package/dist/shared/settings-schema.js +36 -0
- package/dist/shared/settings-schema.js.map +1 -0
- package/dist/util/import-generator.js.map +1 -1
- package/dist/util/parse.js.map +1 -1
- package/package.json +8 -2
- package/src/adapters/adapters.ts +10 -3
- package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +11 -7
- package/src/adapters/drizzle/drizzle-adapter.test.ts +77 -29
- package/src/adapters/drizzle/drizzle-adapter.ts +31 -78
- package/src/adapters/drizzle/drizzle-query.ts +4 -7
- package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +9 -3
- package/src/adapters/drizzle/drizzle-uow-compiler.ts +12 -6
- package/src/adapters/drizzle/drizzle-uow-decoder.ts +1 -1
- package/src/adapters/drizzle/drizzle-uow-executor.ts +1 -1
- package/src/adapters/drizzle/generate.test.ts +573 -150
- package/src/adapters/drizzle/generate.ts +187 -36
- package/src/adapters/drizzle/migrate-drizzle.test.ts +30 -6
- package/src/adapters/drizzle/shared.ts +31 -1
- package/src/adapters/drizzle/test-utils.ts +3 -1
- package/src/adapters/kysely/kysely-adapter-pglite.test.ts +25 -27
- package/src/adapters/kysely/kysely-adapter.ts +35 -58
- package/src/adapters/kysely/kysely-query-builder.ts +75 -44
- package/src/adapters/kysely/kysely-query-compiler.ts +3 -1
- package/src/adapters/kysely/kysely-query.ts +8 -2
- package/src/adapters/kysely/kysely-shared.ts +23 -0
- package/src/adapters/kysely/kysely-uow-compiler.ts +5 -2
- package/src/adapters/kysely/migration/execute-mysql.test.ts +2 -2
- package/src/adapters/kysely/migration/execute-postgres.test.ts +19 -19
- package/src/adapters/kysely/migration/execute.ts +48 -17
- package/src/adapters/kysely/migration/kysely-migrator.test.ts +19 -37
- package/src/fragment.test.ts +1 -0
- package/src/migration-engine/auto-from-schema.ts +14 -18
- package/src/migration-engine/create.ts +1 -6
- package/src/migration-engine/generation-engine.test.ts +597 -0
- package/src/migration-engine/generation-engine.ts +356 -0
- package/src/migration-engine/shared.ts +1 -4
- package/src/mod.ts +0 -66
- package/src/query/condition-builder.ts +24 -8
- package/src/query/result-transform.ts +7 -1
- package/src/schema/create.test.ts +4 -1
- package/src/schema/create.ts +132 -24
- package/src/schema/serialize.ts +21 -7
- package/src/shared/settings-schema.ts +61 -0
- package/src/util/deep-equal.ts +21 -7
- package/src/util/import-generator.ts +3 -1
- package/src/util/parse.ts +3 -1
- package/tsdown.config.ts +1 -0
- package/.turbo/turbo-test.log +0 -37
- package/.turbo/turbo-types$colon$check.log +0 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { sql, type Kysely } from "kysely";
|
|
2
2
|
import type { SQLProvider } from "../../shared/providers";
|
|
3
3
|
import type { DatabaseAdapter } from "../adapters";
|
|
4
|
-
import { schemaToDBType } from "../../schema/serialize";
|
|
5
4
|
import { createMigrator, type Migrator } from "../../migration-engine/create";
|
|
6
5
|
import type { AnySchema } from "../../schema/create";
|
|
7
6
|
import type { CustomOperation, MigrationOperation } from "../../migration-engine/shared";
|
|
8
7
|
import { execute } from "./migration/execute";
|
|
9
8
|
import type { AbstractQuery } from "../../query/query";
|
|
10
9
|
import { fromKysely } from "./kysely-query";
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
import { createTableNameMapper } from "./kysely-shared";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { SETTINGS_TABLE_NAME } from "../../shared/settings-schema";
|
|
13
13
|
|
|
14
14
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
15
|
type KyselyAny = Kysely<any>;
|
|
@@ -26,17 +26,26 @@ export class KyselyAdapter implements DatabaseAdapter {
|
|
|
26
26
|
this.#kyselyConfig = config;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
createQueryEngine<T extends AnySchema>(schema: T,
|
|
30
|
-
|
|
29
|
+
createQueryEngine<T extends AnySchema>(schema: T, namespace: string): AbstractQuery<T> {
|
|
30
|
+
// Only create mapper if namespace is non-empty
|
|
31
|
+
const mapper = namespace ? createTableNameMapper(namespace) : undefined;
|
|
32
|
+
return fromKysely(schema, this.#kyselyConfig, mapper);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async isConnectionHealthy(): Promise<boolean> {
|
|
36
|
+
try {
|
|
37
|
+
const result = await this.#kyselyConfig.db.executeQuery(
|
|
38
|
+
sql`SELECT 1 as healthy`.compile(this.#kyselyConfig.db),
|
|
39
|
+
);
|
|
40
|
+
return (result.rows[0] as Record<string, unknown>)["healthy"] === 1;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
createMigrationEngine(schema: AnySchema, namespace: string): Migrator {
|
|
34
|
-
const manager = createSettingsManager(
|
|
35
|
-
|
|
36
|
-
this.#kyselyConfig.provider,
|
|
37
|
-
SETTINGS_TABLE_NAME,
|
|
38
|
-
namespace,
|
|
39
|
-
);
|
|
47
|
+
const manager = createSettingsManager(this.#kyselyConfig.db, namespace);
|
|
48
|
+
const mapper = namespace ? createTableNameMapper(namespace) : undefined;
|
|
40
49
|
|
|
41
50
|
const onCustomNode = (node: CustomOperation, db: KyselyAny) => {
|
|
42
51
|
const statement = sql.raw(node["sql"] as string);
|
|
@@ -63,7 +72,7 @@ export class KyselyAdapter implements DatabaseAdapter {
|
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
return operations.flatMap((op) =>
|
|
66
|
-
execute(op, this.#kyselyConfig, (node) => onCustomNode(node, db)),
|
|
75
|
+
execute(op, this.#kyselyConfig, (node) => onCustomNode(node, db), mapper),
|
|
67
76
|
);
|
|
68
77
|
};
|
|
69
78
|
|
|
@@ -97,69 +106,38 @@ export class KyselyAdapter implements DatabaseAdapter {
|
|
|
97
106
|
return v ? parseInt(v) : 0;
|
|
98
107
|
},
|
|
99
108
|
updateSettingsInMigration: async (fromVersion, toVersion) => {
|
|
100
|
-
// Only create the settings table if we're migrating from version 0
|
|
101
|
-
if (fromVersion === 0) {
|
|
102
|
-
return [
|
|
103
|
-
{ type: "custom", sql: manager.initTable() },
|
|
104
|
-
{
|
|
105
|
-
type: "custom",
|
|
106
|
-
sql: manager.insert(`schema_version`, toVersion.toString()),
|
|
107
|
-
},
|
|
108
|
-
];
|
|
109
|
-
}
|
|
110
|
-
|
|
111
109
|
return [
|
|
112
110
|
{
|
|
113
111
|
type: "custom",
|
|
114
|
-
sql:
|
|
112
|
+
sql:
|
|
113
|
+
fromVersion === 0
|
|
114
|
+
? manager.insert(`schema_version`, toVersion.toString())
|
|
115
|
+
: manager.update(`schema_version`, toVersion.toString()),
|
|
115
116
|
},
|
|
116
117
|
];
|
|
117
118
|
},
|
|
118
119
|
},
|
|
119
120
|
});
|
|
120
121
|
|
|
121
|
-
// Add getDefaultFileName implementation
|
|
122
|
-
migrator.getDefaultFileName = (namespace: string, fromVersion: number, toVersion: number) => {
|
|
123
|
-
const date = new Date().toISOString().split("T")[0].replace(/-/g, "");
|
|
124
|
-
const safeName = namespace.replace(/[^a-z0-9-]/gi, "_");
|
|
125
|
-
return `${date}_${safeName}_migration_${fromVersion}_to_${toVersion}.sql`;
|
|
126
|
-
};
|
|
127
|
-
|
|
128
122
|
return migrator;
|
|
129
123
|
}
|
|
130
124
|
|
|
131
125
|
getSchemaVersion(namespace: string) {
|
|
132
|
-
const manager = createSettingsManager(
|
|
133
|
-
this.#kyselyConfig.db,
|
|
134
|
-
this.#kyselyConfig.provider,
|
|
135
|
-
SETTINGS_TABLE_NAME,
|
|
136
|
-
namespace,
|
|
137
|
-
);
|
|
126
|
+
const manager = createSettingsManager(this.#kyselyConfig.db, namespace);
|
|
138
127
|
|
|
139
128
|
return manager.get(`schema_version`);
|
|
140
129
|
}
|
|
141
130
|
}
|
|
142
131
|
|
|
143
|
-
function createSettingsManager(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
settingsTableName: string,
|
|
147
|
-
namespace: string,
|
|
148
|
-
) {
|
|
149
|
-
function initTable() {
|
|
150
|
-
return db.schema
|
|
151
|
-
.createTable(settingsTableName)
|
|
152
|
-
.addColumn("key", provider === "sqlite" ? "text" : "varchar(255)", (col) => col.primaryKey())
|
|
153
|
-
.addColumn("value", sql.raw(schemaToDBType({ type: "string" }, provider)), (col) =>
|
|
154
|
-
col.notNull(),
|
|
155
|
-
);
|
|
156
|
-
}
|
|
132
|
+
function createSettingsManager(db: KyselyAny, namespace: string) {
|
|
133
|
+
// Settings table is never namespaced, but keys include namespace prefix
|
|
134
|
+
const tableName = SETTINGS_TABLE_NAME;
|
|
157
135
|
|
|
158
136
|
return {
|
|
159
137
|
async get(key: string): Promise<string | undefined> {
|
|
160
138
|
try {
|
|
161
139
|
const result = await db
|
|
162
|
-
.selectFrom(
|
|
140
|
+
.selectFrom(tableName)
|
|
163
141
|
.where("key", "=", sql.lit(`${namespace}.${key}`))
|
|
164
142
|
.select(["value"])
|
|
165
143
|
.executeTakeFirstOrThrow();
|
|
@@ -169,14 +147,13 @@ function createSettingsManager(
|
|
|
169
147
|
}
|
|
170
148
|
},
|
|
171
149
|
|
|
172
|
-
initTable() {
|
|
173
|
-
return initTable().compile().sql;
|
|
174
|
-
},
|
|
175
|
-
|
|
176
150
|
insert(key: string, value: string) {
|
|
177
151
|
return db
|
|
178
|
-
.insertInto(
|
|
152
|
+
.insertInto(tableName)
|
|
179
153
|
.values({
|
|
154
|
+
id: sql.lit(
|
|
155
|
+
createHash("md5").update(`${namespace}.${key}`).digest("base64url").replace(/=/g, ""),
|
|
156
|
+
),
|
|
180
157
|
key: sql.lit(`${namespace}.${key}`),
|
|
181
158
|
value: sql.lit(value),
|
|
182
159
|
})
|
|
@@ -185,7 +162,7 @@ function createSettingsManager(
|
|
|
185
162
|
|
|
186
163
|
update(key: string, value: string) {
|
|
187
164
|
return db
|
|
188
|
-
.updateTable(
|
|
165
|
+
.updateTable(tableName)
|
|
189
166
|
.set({
|
|
190
167
|
value: sql.lit(value),
|
|
191
168
|
})
|
|
@@ -14,11 +14,13 @@ import type { Condition } from "../../query/condition-builder";
|
|
|
14
14
|
import { serialize } from "../../schema/serialize";
|
|
15
15
|
import type { CompiledJoin, SimplifyFindOptions } from "../../query/orm/orm";
|
|
16
16
|
import { decodeResult, encodeValues, ReferenceSubquery } from "../../query/result-transform";
|
|
17
|
+
import type { TableNameMapper } from "./kysely-shared";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Returns the fully qualified SQL name for a column (table.column).
|
|
20
21
|
*
|
|
21
22
|
* @param column - The column to get the full name for
|
|
23
|
+
* @param mapper - Optional table name mapper for namespace prefixing
|
|
22
24
|
* @returns The fully qualified SQL name in the format "tableName.columnName"
|
|
23
25
|
* @internal
|
|
24
26
|
*
|
|
@@ -28,8 +30,9 @@ import { decodeResult, encodeValues, ReferenceSubquery } from "../../query/resul
|
|
|
28
30
|
* // Returns: "users.email"
|
|
29
31
|
* ```
|
|
30
32
|
*/
|
|
31
|
-
export function fullSQLName(column: AnyColumn) {
|
|
32
|
-
|
|
33
|
+
export function fullSQLName(column: AnyColumn, mapper?: TableNameMapper) {
|
|
34
|
+
const tableName = mapper ? mapper.toPhysical(column.tableName) : column.tableName;
|
|
35
|
+
return `${tableName}.${column.name}`;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
/**
|
|
@@ -60,6 +63,7 @@ export function buildWhere(
|
|
|
60
63
|
condition: Condition,
|
|
61
64
|
eb: ExpressionBuilder<any, any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
62
65
|
provider: SQLProvider,
|
|
66
|
+
mapper?: TableNameMapper,
|
|
63
67
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
68
|
): ExpressionWrapper<any, any, SqlBool> {
|
|
65
69
|
if (condition.type === "compare") {
|
|
@@ -78,47 +82,55 @@ export function buildWhere(
|
|
|
78
82
|
case "contains":
|
|
79
83
|
v = "like";
|
|
80
84
|
rhs =
|
|
81
|
-
val instanceof Column
|
|
85
|
+
val instanceof Column
|
|
86
|
+
? sql`concat('%', ${eb.ref(fullSQLName(val, mapper))}, '%')`
|
|
87
|
+
: `%${val}%`;
|
|
82
88
|
break;
|
|
83
89
|
case "not contains":
|
|
84
90
|
v = "not like";
|
|
85
91
|
rhs =
|
|
86
|
-
val instanceof Column
|
|
92
|
+
val instanceof Column
|
|
93
|
+
? sql`concat('%', ${eb.ref(fullSQLName(val, mapper))}, '%')`
|
|
94
|
+
: `%${val}%`;
|
|
87
95
|
break;
|
|
88
96
|
case "starts with":
|
|
89
97
|
v = "like";
|
|
90
|
-
rhs =
|
|
98
|
+
rhs =
|
|
99
|
+
val instanceof Column ? sql`concat(${eb.ref(fullSQLName(val, mapper))}, '%')` : `${val}%`;
|
|
91
100
|
break;
|
|
92
101
|
case "not starts with":
|
|
93
102
|
v = "not like";
|
|
94
|
-
rhs =
|
|
103
|
+
rhs =
|
|
104
|
+
val instanceof Column ? sql`concat(${eb.ref(fullSQLName(val, mapper))}, '%')` : `${val}%`;
|
|
95
105
|
break;
|
|
96
106
|
case "ends with":
|
|
97
107
|
v = "like";
|
|
98
|
-
rhs =
|
|
108
|
+
rhs =
|
|
109
|
+
val instanceof Column ? sql`concat('%', ${eb.ref(fullSQLName(val, mapper))})` : `%${val}`;
|
|
99
110
|
break;
|
|
100
111
|
case "not ends with":
|
|
101
112
|
v = "not like";
|
|
102
|
-
rhs =
|
|
113
|
+
rhs =
|
|
114
|
+
val instanceof Column ? sql`concat('%', ${eb.ref(fullSQLName(val, mapper))})` : `%${val}`;
|
|
103
115
|
break;
|
|
104
116
|
default:
|
|
105
117
|
v = op;
|
|
106
|
-
rhs = val instanceof Column ? eb.ref(fullSQLName(val)) : val;
|
|
118
|
+
rhs = val instanceof Column ? eb.ref(fullSQLName(val, mapper)) : val;
|
|
107
119
|
}
|
|
108
120
|
|
|
109
|
-
return eb(fullSQLName(left), v, rhs);
|
|
121
|
+
return eb(fullSQLName(left, mapper), v, rhs);
|
|
110
122
|
}
|
|
111
123
|
|
|
112
124
|
// Nested conditions
|
|
113
125
|
if (condition.type === "and") {
|
|
114
|
-
return eb.and(condition.items.map((v) => buildWhere(v, eb, provider)));
|
|
126
|
+
return eb.and(condition.items.map((v) => buildWhere(v, eb, provider, mapper)));
|
|
115
127
|
}
|
|
116
128
|
|
|
117
129
|
if (condition.type === "not") {
|
|
118
|
-
return eb.not(buildWhere(condition.item, eb, provider));
|
|
130
|
+
return eb.not(buildWhere(condition.item, eb, provider, mapper));
|
|
119
131
|
}
|
|
120
132
|
|
|
121
|
-
return eb.or(condition.items.map((v) => buildWhere(v, eb, provider)));
|
|
133
|
+
return eb.or(condition.items.map((v) => buildWhere(v, eb, provider, mapper)));
|
|
122
134
|
}
|
|
123
135
|
|
|
124
136
|
/**
|
|
@@ -353,14 +365,17 @@ export async function findMany(
|
|
|
353
365
|
*
|
|
354
366
|
* @param values - The encoded values that may contain ReferenceSubquery objects
|
|
355
367
|
* @param kysely - The Kysely database instance for building subqueries
|
|
368
|
+
* @param mapper - Optional table name mapper for namespace prefixing
|
|
356
369
|
* @returns Processed values with subqueries in place of ReferenceSubquery markers
|
|
357
370
|
* @internal
|
|
358
371
|
*/
|
|
359
372
|
function processReferenceSubqueries(
|
|
360
373
|
values: Record<string, unknown>,
|
|
361
374
|
kysely: Kysely<any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
375
|
+
mapper?: TableNameMapper,
|
|
362
376
|
): Record<string, unknown> {
|
|
363
377
|
const processed: Record<string, unknown> = {};
|
|
378
|
+
const getTableName = (table: AnyTable) => (mapper ? mapper.toPhysical(table.name) : table.name);
|
|
364
379
|
|
|
365
380
|
for (const [key, value] of Object.entries(values)) {
|
|
366
381
|
if (value instanceof ReferenceSubquery) {
|
|
@@ -369,7 +384,7 @@ function processReferenceSubqueries(
|
|
|
369
384
|
|
|
370
385
|
// Build a subquery: SELECT _internal_id FROM referenced_table WHERE id = external_id LIMIT 1
|
|
371
386
|
processed[key] = kysely
|
|
372
|
-
.selectFrom(refTable
|
|
387
|
+
.selectFrom(getTableName(refTable))
|
|
373
388
|
.select(refTable.getInternalIdColumn().name)
|
|
374
389
|
.where(refTable.getIdColumn().name, "=", externalId)
|
|
375
390
|
.limit(1);
|
|
@@ -399,21 +414,27 @@ function processReferenceSubqueries(
|
|
|
399
414
|
* const result = await kysely.executeQuery(query);
|
|
400
415
|
* ```
|
|
401
416
|
*/
|
|
402
|
-
|
|
403
|
-
|
|
417
|
+
export function createKyselyQueryBuilder(
|
|
418
|
+
kysely: Kysely<any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
419
|
+
provider: SQLProvider,
|
|
420
|
+
mapper?: TableNameMapper,
|
|
421
|
+
) {
|
|
422
|
+
// Helper to get the physical table name (with namespace suffix if mapper is provided)
|
|
423
|
+
const getTableName = (table: AnyTable) => (mapper ? mapper.toPhysical(table.name) : table.name);
|
|
424
|
+
|
|
404
425
|
return {
|
|
405
426
|
count(table: AnyTable, { where }: { where?: Condition }): CompiledQuery {
|
|
406
|
-
let query = kysely.selectFrom(table
|
|
427
|
+
let query = kysely.selectFrom(getTableName(table)).select(kysely.fn.countAll().as("count"));
|
|
407
428
|
if (where) {
|
|
408
|
-
query = query.where((b) => buildWhere(where, b, provider));
|
|
429
|
+
query = query.where((b) => buildWhere(where, b, provider, mapper));
|
|
409
430
|
}
|
|
410
431
|
return query.compile();
|
|
411
432
|
},
|
|
412
433
|
|
|
413
434
|
create(table: AnyTable, values: Record<string, unknown>): CompiledQuery {
|
|
414
435
|
const encodedValues = encodeValues(values, table, true, provider);
|
|
415
|
-
const processedValues = processReferenceSubqueries(encodedValues, kysely);
|
|
416
|
-
const insert = kysely.insertInto(table
|
|
436
|
+
const processedValues = processReferenceSubqueries(encodedValues, kysely, mapper);
|
|
437
|
+
const insert = kysely.insertInto(getTableName(table)).values(processedValues);
|
|
417
438
|
|
|
418
439
|
if (provider === "mssql") {
|
|
419
440
|
return (
|
|
@@ -425,7 +446,9 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
|
|
|
425
446
|
}
|
|
426
447
|
|
|
427
448
|
if (provider === "postgresql" || provider === "sqlite") {
|
|
428
|
-
return insert
|
|
449
|
+
return insert
|
|
450
|
+
.returning(mapSelect(true, table, { tableName: getTableName(table) }))
|
|
451
|
+
.compile();
|
|
429
452
|
}
|
|
430
453
|
|
|
431
454
|
// For MySQL/other providers, return the insert query
|
|
@@ -436,11 +459,11 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
|
|
|
436
459
|
table: T,
|
|
437
460
|
v: SimplifyFindOptions<FindManyOptions<T>>,
|
|
438
461
|
): CompiledQuery {
|
|
439
|
-
let query = kysely.selectFrom(table
|
|
462
|
+
let query = kysely.selectFrom(getTableName(table));
|
|
440
463
|
|
|
441
464
|
const where = v.where;
|
|
442
465
|
if (where) {
|
|
443
|
-
query = query.where((eb) => buildWhere(where, eb, provider));
|
|
466
|
+
query = query.where((eb) => buildWhere(where, eb, provider, mapper));
|
|
444
467
|
}
|
|
445
468
|
|
|
446
469
|
if (v.offset !== undefined) {
|
|
@@ -453,7 +476,7 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
|
|
|
453
476
|
|
|
454
477
|
if (v.orderBy) {
|
|
455
478
|
for (const [col, mode] of v.orderBy) {
|
|
456
|
-
query = query.orderBy(fullSQLName(col), mode);
|
|
479
|
+
query = query.orderBy(fullSQLName(col, mapper), mode);
|
|
457
480
|
}
|
|
458
481
|
}
|
|
459
482
|
|
|
@@ -488,7 +511,7 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
|
|
|
488
511
|
}),
|
|
489
512
|
);
|
|
490
513
|
|
|
491
|
-
query = query.leftJoin(`${targetTable
|
|
514
|
+
query = query.leftJoin(`${getTableName(targetTable)} as ${joinName}`, (b) =>
|
|
492
515
|
b.on((eb) => {
|
|
493
516
|
const conditions = [];
|
|
494
517
|
for (const [left, right] of relation.on) {
|
|
@@ -507,7 +530,7 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
|
|
|
507
530
|
}
|
|
508
531
|
|
|
509
532
|
if (joinOptions.where) {
|
|
510
|
-
conditions.push(buildWhere(joinOptions.where, eb, provider));
|
|
533
|
+
conditions.push(buildWhere(joinOptions.where, eb, provider, mapper));
|
|
511
534
|
}
|
|
512
535
|
|
|
513
536
|
return eb.and(conditions);
|
|
@@ -519,10 +542,12 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
|
|
|
519
542
|
}
|
|
520
543
|
};
|
|
521
544
|
|
|
522
|
-
processJoins(v.join, table, table
|
|
545
|
+
processJoins(v.join, table, getTableName(table));
|
|
523
546
|
|
|
524
547
|
const compiledSelect = selectBuilder.compile();
|
|
525
|
-
mappedSelect.push(
|
|
548
|
+
mappedSelect.push(
|
|
549
|
+
...mapSelect(compiledSelect.result, table, { tableName: getTableName(table) }),
|
|
550
|
+
);
|
|
526
551
|
|
|
527
552
|
return query.select(mappedSelect).compile();
|
|
528
553
|
},
|
|
@@ -535,26 +560,26 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
|
|
|
535
560
|
},
|
|
536
561
|
): CompiledQuery {
|
|
537
562
|
const encoded = encodeValues(v.set, table, false, provider);
|
|
538
|
-
const processed = processReferenceSubqueries(encoded, kysely);
|
|
563
|
+
const processed = processReferenceSubqueries(encoded, kysely, mapper);
|
|
539
564
|
|
|
540
565
|
// Automatically increment _version for optimistic concurrency control
|
|
541
566
|
const versionCol = table.getVersionColumn();
|
|
542
567
|
// Safe cast: we're building a SQL expression for incrementing the version
|
|
543
568
|
processed[versionCol.name] = sql.raw(`COALESCE(${versionCol.name}, 0) + 1`) as unknown;
|
|
544
569
|
|
|
545
|
-
let query = kysely.updateTable(table
|
|
570
|
+
let query = kysely.updateTable(getTableName(table)).set(processed);
|
|
546
571
|
const { where } = v;
|
|
547
572
|
if (where) {
|
|
548
|
-
query = query.where((eb) => buildWhere(where, eb, provider));
|
|
573
|
+
query = query.where((eb) => buildWhere(where, eb, provider, mapper));
|
|
549
574
|
}
|
|
550
575
|
return query.compile();
|
|
551
576
|
},
|
|
552
577
|
|
|
553
578
|
upsertCheck(table: AnyTable, where: Condition | undefined): CompiledQuery {
|
|
554
579
|
const idColumn = table.getIdColumn();
|
|
555
|
-
let query = kysely.selectFrom(table
|
|
580
|
+
let query = kysely.selectFrom(getTableName(table)).select([`${idColumn.name} as id`]);
|
|
556
581
|
if (where) {
|
|
557
|
-
query = query.where((b) => buildWhere(where, b, provider));
|
|
582
|
+
query = query.where((b) => buildWhere(where, b, provider, mapper));
|
|
558
583
|
}
|
|
559
584
|
return query.limit(1).compile();
|
|
560
585
|
},
|
|
@@ -566,13 +591,13 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
|
|
|
566
591
|
top?: boolean,
|
|
567
592
|
): CompiledQuery {
|
|
568
593
|
const encoded = encodeValues(update, table, false, provider);
|
|
569
|
-
const processed = processReferenceSubqueries(encoded, kysely);
|
|
570
|
-
let query = kysely.updateTable(table
|
|
594
|
+
const processed = processReferenceSubqueries(encoded, kysely, mapper);
|
|
595
|
+
let query = kysely.updateTable(getTableName(table)).set(processed);
|
|
571
596
|
if (top) {
|
|
572
597
|
query = query.top(1);
|
|
573
598
|
}
|
|
574
599
|
if (where) {
|
|
575
|
-
query = query.where((b) => buildWhere(where, b, provider));
|
|
600
|
+
query = query.where((b) => buildWhere(where, b, provider, mapper));
|
|
576
601
|
}
|
|
577
602
|
return query.compile();
|
|
578
603
|
},
|
|
@@ -580,20 +605,26 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
|
|
|
580
605
|
upsertUpdateById(table: AnyTable, update: Record<string, unknown>, id: unknown): CompiledQuery {
|
|
581
606
|
const idColumn = table.getIdColumn();
|
|
582
607
|
const encoded = encodeValues(update, table, false, provider);
|
|
583
|
-
const processed = processReferenceSubqueries(encoded, kysely);
|
|
584
|
-
return kysely
|
|
608
|
+
const processed = processReferenceSubqueries(encoded, kysely, mapper);
|
|
609
|
+
return kysely
|
|
610
|
+
.updateTable(getTableName(table))
|
|
611
|
+
.set(processed)
|
|
612
|
+
.where(idColumn.name, "=", id)
|
|
613
|
+
.compile();
|
|
585
614
|
},
|
|
586
615
|
|
|
587
616
|
createMany(table: AnyTable, values: Record<string, unknown>[]): CompiledQuery {
|
|
588
617
|
const encodedValues = values.map((v) => encodeValues(v, table, true, provider));
|
|
589
|
-
const processedValues = encodedValues.map((v) =>
|
|
590
|
-
|
|
618
|
+
const processedValues = encodedValues.map((v) =>
|
|
619
|
+
processReferenceSubqueries(v, kysely, mapper),
|
|
620
|
+
);
|
|
621
|
+
return kysely.insertInto(getTableName(table)).values(processedValues).compile();
|
|
591
622
|
},
|
|
592
623
|
|
|
593
624
|
deleteMany(table: AnyTable, { where }: { where?: Condition }): CompiledQuery {
|
|
594
|
-
let query = kysely.deleteFrom(table
|
|
625
|
+
let query = kysely.deleteFrom(getTableName(table));
|
|
595
626
|
if (where) {
|
|
596
|
-
query = query.where((eb) => buildWhere(where, eb, provider));
|
|
627
|
+
query = query.where((eb) => buildWhere(where, eb, provider, mapper));
|
|
597
628
|
}
|
|
598
629
|
return query.compile();
|
|
599
630
|
},
|
|
@@ -601,8 +632,8 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
|
|
|
601
632
|
findById(table: AnyTable, idValue: unknown): CompiledQuery {
|
|
602
633
|
const idColumn = table.getIdColumn();
|
|
603
634
|
return kysely
|
|
604
|
-
.selectFrom(table
|
|
605
|
-
.select(mapSelect(true, table))
|
|
635
|
+
.selectFrom(getTableName(table))
|
|
636
|
+
.select(mapSelect(true, table, { tableName: getTableName(table) }))
|
|
606
637
|
.where(idColumn.name, "=", idValue)
|
|
607
638
|
.limit(1)
|
|
608
639
|
.compile();
|
|
@@ -5,6 +5,7 @@ import { buildFindOptions } from "../../query/orm/orm";
|
|
|
5
5
|
import type { KyselyConfig } from "./kysely-adapter";
|
|
6
6
|
import { createKyselyQueryBuilder } from "./kysely-query-builder";
|
|
7
7
|
import type { ConditionBuilder, Condition } from "../../query/condition-builder";
|
|
8
|
+
import type { TableNameMapper } from "./kysely-shared";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Internal query compiler interface for Kysely
|
|
@@ -27,9 +28,10 @@ export interface KyselyQueryCompiler {
|
|
|
27
28
|
export function createKyselyQueryCompiler<T extends AnySchema>(
|
|
28
29
|
schema: T,
|
|
29
30
|
config: KyselyConfig,
|
|
31
|
+
mapper?: TableNameMapper,
|
|
30
32
|
): KyselyQueryCompiler {
|
|
31
33
|
const { db: kysely, provider } = config;
|
|
32
|
-
const queryBuilder = createKyselyQueryBuilder(kysely, provider);
|
|
34
|
+
const queryBuilder = createKyselyQueryBuilder(kysely, provider, mapper);
|
|
33
35
|
|
|
34
36
|
function toTable(name: unknown): AnyTable {
|
|
35
37
|
const table = schema.tables[name as string];
|
|
@@ -7,6 +7,7 @@ import { createKyselyUOWCompiler } from "./kysely-uow-compiler";
|
|
|
7
7
|
import { executeKyselyRetrievalPhase, executeKyselyMutationPhase } from "./kysely-uow-executor";
|
|
8
8
|
import { UnitOfWork } from "../../query/unit-of-work";
|
|
9
9
|
import type { CompiledQuery } from "kysely";
|
|
10
|
+
import type { TableNameMapper } from "./kysely-shared";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Creates a Kysely-based query engine for the given schema.
|
|
@@ -17,6 +18,7 @@ import type { CompiledQuery } from "kysely";
|
|
|
17
18
|
*
|
|
18
19
|
* @param schema - The database schema definition
|
|
19
20
|
* @param config - Kysely configuration containing the database instance and provider
|
|
21
|
+
* @param mapper - Optional table name mapper for namespace prefixing
|
|
20
22
|
* @returns An AbstractQuery instance for performing database operations
|
|
21
23
|
*
|
|
22
24
|
* @example
|
|
@@ -32,9 +34,13 @@ import type { CompiledQuery } from "kysely";
|
|
|
32
34
|
* });
|
|
33
35
|
* ```
|
|
34
36
|
*/
|
|
35
|
-
export function fromKysely<T extends AnySchema>(
|
|
37
|
+
export function fromKysely<T extends AnySchema>(
|
|
38
|
+
schema: T,
|
|
39
|
+
config: KyselyConfig,
|
|
40
|
+
mapper?: TableNameMapper,
|
|
41
|
+
): AbstractQuery<T> {
|
|
36
42
|
const { db: kysely, provider } = config;
|
|
37
|
-
const uowCompiler = createKyselyUOWCompiler(schema, config);
|
|
43
|
+
const uowCompiler = createKyselyUOWCompiler(schema, config, mapper);
|
|
38
44
|
|
|
39
45
|
function createUOW(name?: string): UnitOfWork<T, []> {
|
|
40
46
|
const executor: UOWExecutor<CompiledQuery, unknown> = {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps logical table names (used by fragment authors) to physical table names (with namespace suffix)
|
|
3
|
+
*/
|
|
4
|
+
export interface TableNameMapper {
|
|
5
|
+
toPhysical(logicalName: string): string;
|
|
6
|
+
toLogical(physicalName: string): string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Creates a table name mapper for a given namespace.
|
|
11
|
+
* Physical names have format: {logicalName}_{namespace}
|
|
12
|
+
*/
|
|
13
|
+
export function createTableNameMapper(namespace: string): TableNameMapper {
|
|
14
|
+
return {
|
|
15
|
+
toPhysical: (logicalName: string) => `${logicalName}_${namespace}`,
|
|
16
|
+
toLogical: (physicalName: string) => {
|
|
17
|
+
if (physicalName.endsWith(`_${namespace}`)) {
|
|
18
|
+
return physicalName.slice(0, -(namespace.length + 1));
|
|
19
|
+
}
|
|
20
|
+
return physicalName;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -12,6 +12,7 @@ import { createKyselyQueryBuilder } from "./kysely-query-builder";
|
|
|
12
12
|
import { buildCondition, type Condition } from "../../query/condition-builder";
|
|
13
13
|
import { decodeCursor, serializeCursorValues } from "../../query/cursor";
|
|
14
14
|
import type { AnySelectClause } from "../../query/query";
|
|
15
|
+
import type { TableNameMapper } from "./kysely-shared";
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Create a Kysely-specific Unit of Work compiler
|
|
@@ -21,14 +22,16 @@ import type { AnySelectClause } from "../../query/query";
|
|
|
21
22
|
*
|
|
22
23
|
* @param schema - The database schema
|
|
23
24
|
* @param config - Kysely configuration
|
|
25
|
+
* @param mapper - Optional table name mapper for namespace prefixing
|
|
24
26
|
* @returns A UOWCompiler instance for Kysely
|
|
25
27
|
*/
|
|
26
28
|
export function createKyselyUOWCompiler<TSchema extends AnySchema>(
|
|
27
29
|
schema: TSchema,
|
|
28
30
|
config: KyselyConfig,
|
|
31
|
+
mapper?: TableNameMapper,
|
|
29
32
|
): UOWCompiler<TSchema, CompiledQuery> {
|
|
30
|
-
const queryCompiler = createKyselyQueryCompiler(schema, config);
|
|
31
|
-
const queryBuilder = createKyselyQueryBuilder(config.db, config.provider);
|
|
33
|
+
const queryCompiler = createKyselyQueryCompiler(schema, config, mapper);
|
|
34
|
+
const queryBuilder = createKyselyQueryBuilder(config.db, config.provider, mapper);
|
|
32
35
|
const { provider } = config;
|
|
33
36
|
|
|
34
37
|
function toTable(name: unknown) {
|
|
@@ -168,7 +168,7 @@ describe("execute() - MySQL", () => {
|
|
|
168
168
|
type: "timestamp",
|
|
169
169
|
isNullable: false,
|
|
170
170
|
role: "regular",
|
|
171
|
-
default: {
|
|
171
|
+
default: { dbSpecial: "now" },
|
|
172
172
|
},
|
|
173
173
|
],
|
|
174
174
|
};
|
|
@@ -679,7 +679,7 @@ describe("execute() - MySQL", () => {
|
|
|
679
679
|
type: "timestamp",
|
|
680
680
|
isNullable: false,
|
|
681
681
|
role: "regular",
|
|
682
|
-
default: {
|
|
682
|
+
default: { dbSpecial: "now" },
|
|
683
683
|
},
|
|
684
684
|
updateDataType: false,
|
|
685
685
|
updateNullable: false,
|