@simplysm/orm-common 13.0.75 → 13.0.77
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/README.md +575 -50
- package/dist/create-db-context.d.ts +1 -1
- package/dist/create-db-context.d.ts.map +1 -1
- package/dist/create-db-context.js +34 -27
- package/dist/create-db-context.js.map +1 -1
- package/dist/ddl/initialize.js +4 -4
- package/dist/ddl/initialize.js.map +1 -1
- package/dist/ddl/relation-ddl.d.ts +7 -7
- package/dist/ddl/relation-ddl.d.ts.map +1 -1
- package/dist/ddl/relation-ddl.js +18 -18
- package/dist/ddl/relation-ddl.js.map +1 -1
- package/dist/ddl/schema-ddl.d.ts +1 -1
- package/dist/ddl/schema-ddl.d.ts.map +1 -1
- package/dist/ddl/schema-ddl.js +2 -2
- package/dist/ddl/schema-ddl.js.map +1 -1
- package/dist/ddl/table-ddl.js +2 -2
- package/dist/ddl/table-ddl.js.map +1 -1
- package/dist/exec/queryable.d.ts +24 -24
- package/dist/exec/queryable.d.ts.map +1 -1
- package/dist/exec/queryable.js +37 -37
- package/dist/exec/queryable.js.map +1 -1
- package/dist/expr/expr-unit.js +1 -1
- package/dist/expr/expr-unit.js.map +1 -1
- package/dist/expr/expr.d.ts +9 -9
- package/dist/expr/expr.d.ts.map +1 -1
- package/dist/expr/expr.js +10 -10
- package/dist/expr/expr.js.map +1 -1
- package/dist/query-builder/base/expr-renderer-base.d.ts +2 -2
- package/dist/query-builder/base/expr-renderer-base.d.ts.map +1 -1
- package/dist/query-builder/base/query-builder-base.d.ts +7 -15
- package/dist/query-builder/base/query-builder-base.d.ts.map +1 -1
- package/dist/query-builder/base/query-builder-base.js +2 -2
- package/dist/query-builder/base/query-builder-base.js.map +1 -1
- package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/mssql/mssql-expr-renderer.js +8 -8
- package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -1
- package/dist/query-builder/mssql/mssql-query-builder.d.ts +7 -7
- package/dist/query-builder/mssql/mssql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/mssql/mssql-query-builder.js +7 -7
- package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -1
- package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/mysql/mysql-expr-renderer.js +9 -9
- package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -1
- package/dist/query-builder/mysql/mysql-query-builder.d.ts +7 -7
- package/dist/query-builder/mysql/mysql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/mysql/mysql-query-builder.js +11 -11
- package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -1
- package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +4 -4
- package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
- package/dist/query-builder/postgresql/postgresql-expr-renderer.js +8 -8
- package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -1
- package/dist/query-builder/postgresql/postgresql-query-builder.d.ts +7 -7
- package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
- package/dist/query-builder/postgresql/postgresql-query-builder.js +7 -7
- package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -1
- package/dist/schema/procedure-builder.d.ts +1 -1
- package/dist/schema/table-builder.d.ts +1 -1
- package/dist/schema/table-builder.d.ts.map +1 -1
- package/dist/schema/table-builder.js +1 -1
- package/dist/schema/view-builder.d.ts +1 -1
- package/dist/schema/view-builder.d.ts.map +1 -1
- package/dist/schema/view-builder.js +1 -1
- package/dist/types/db-context-def.d.ts +18 -18
- package/dist/types/db-context-def.d.ts.map +1 -1
- package/dist/types/expr.d.ts +6 -6
- package/dist/types/expr.d.ts.map +1 -1
- package/dist/types/query-def.d.ts +15 -15
- package/dist/types/query-def.d.ts.map +1 -1
- package/dist/types/query-def.js +6 -6
- package/dist/utils/result-parser.d.ts.map +1 -1
- package/dist/utils/result-parser.js +44 -16
- package/dist/utils/result-parser.js.map +1 -1
- package/package.json +2 -2
- package/src/create-db-context.ts +36 -29
- package/src/ddl/initialize.ts +4 -4
- package/src/ddl/relation-ddl.ts +16 -16
- package/src/ddl/schema-ddl.ts +2 -2
- package/src/ddl/table-ddl.ts +2 -2
- package/src/exec/queryable.ts +58 -58
- package/src/expr/expr-unit.ts +1 -1
- package/src/expr/expr.ts +13 -13
- package/src/query-builder/base/expr-renderer-base.ts +2 -2
- package/src/query-builder/base/query-builder-base.ts +18 -14
- package/src/query-builder/mssql/mssql-expr-renderer.ts +11 -10
- package/src/query-builder/mssql/mssql-query-builder.ts +13 -13
- package/src/query-builder/mysql/mysql-expr-renderer.ts +12 -11
- package/src/query-builder/mysql/mysql-query-builder.ts +17 -17
- package/src/query-builder/postgresql/postgresql-expr-renderer.ts +11 -10
- package/src/query-builder/postgresql/postgresql-query-builder.ts +13 -13
- package/src/schema/procedure-builder.ts +1 -1
- package/src/schema/table-builder.ts +1 -1
- package/src/schema/view-builder.ts +1 -1
- package/src/types/db-context-def.ts +18 -18
- package/src/types/expr.ts +6 -6
- package/src/types/query-def.ts +31 -31
- package/src/utils/result-parser.ts +60 -16
- package/tests/db-context/create-db-context.spec.ts +6 -37
- package/tests/db-context/define-db-context.spec.ts +0 -51
- package/tests/ddl/basic.expected.ts +8 -8
- package/tests/ddl/basic.spec.ts +24 -181
- package/tests/ddl/column-builder.spec.ts +0 -112
- package/tests/ddl/index-builder.spec.ts +10 -64
- package/tests/ddl/procedure-builder.spec.ts +0 -106
- package/tests/ddl/relation-builder.spec.ts +4 -205
- package/tests/ddl/table-builder.spec.ts +0 -34
- package/tests/ddl/view-builder.spec.ts +0 -60
- package/tests/dml/delete.spec.ts +0 -33
- package/tests/dml/insert.spec.ts +0 -78
- package/tests/dml/update.spec.ts +2 -98
- package/tests/dml/upsert.spec.ts +0 -52
- package/tests/errors/queryable-errors.spec.ts +0 -51
- package/tests/escape.spec.ts +0 -41
- package/tests/examples/pivot.spec.ts +0 -333
- package/tests/examples/sampling.spec.ts +0 -63
- package/tests/examples/unpivot.spec.ts +0 -65
- package/tests/exec/search-parser.spec.ts +0 -16
- package/tests/expr/comparison.spec.ts +0 -66
- package/tests/expr/conditional.expected.ts +2 -2
- package/tests/expr/conditional.spec.ts +8 -35
- package/tests/expr/date.spec.ts +5 -72
- package/tests/expr/math.spec.ts +0 -47
- package/tests/expr/string.spec.ts +0 -56
- package/tests/expr/utility.spec.ts +0 -27
- package/tests/select/basic.spec.ts +5 -74
- package/tests/select/filter.spec.ts +0 -114
- package/tests/select/group.spec.ts +0 -85
- package/tests/select/join.spec.ts +0 -113
- package/tests/select/order.spec.ts +0 -49
- package/tests/select/subquery.spec.ts +0 -96
- package/tests/select/window.spec.ts +0 -185
- package/tests/types/nullable-queryable-record.spec.ts +0 -48
- package/tests/utils/result-parser-perf.spec.ts +0 -67
- package/tests/utils/result-parser.spec.ts +4 -38
package/src/types/query-def.ts
CHANGED
|
@@ -174,7 +174,7 @@ export interface UpsertQueryDef {
|
|
|
174
174
|
export interface SwitchFkQueryDef {
|
|
175
175
|
type: "switchFk";
|
|
176
176
|
table: QueryDefObjectName;
|
|
177
|
-
|
|
177
|
+
enabled: boolean;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
//#endregion
|
|
@@ -275,21 +275,21 @@ export interface RenameColumnQueryDef {
|
|
|
275
275
|
//#region ========== DDL - PK/FK/Index ==========
|
|
276
276
|
|
|
277
277
|
/** DROP PRIMARY KEY */
|
|
278
|
-
export interface
|
|
279
|
-
type: "
|
|
278
|
+
export interface DropPrimaryKeyQueryDef {
|
|
279
|
+
type: "dropPrimaryKey";
|
|
280
280
|
table: QueryDefObjectName;
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
/** ADD PRIMARY KEY */
|
|
284
|
-
export interface
|
|
285
|
-
type: "
|
|
284
|
+
export interface AddPrimaryKeyQueryDef {
|
|
285
|
+
type: "addPrimaryKey";
|
|
286
286
|
table: QueryDefObjectName;
|
|
287
287
|
columns: string[];
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
/** ADD FOREIGN KEY */
|
|
291
|
-
export interface
|
|
292
|
-
type: "
|
|
291
|
+
export interface AddForeignKeyQueryDef {
|
|
292
|
+
type: "addForeignKey";
|
|
293
293
|
table: QueryDefObjectName;
|
|
294
294
|
foreignKey: {
|
|
295
295
|
name: string;
|
|
@@ -300,15 +300,15 @@ export interface AddFkQueryDef {
|
|
|
300
300
|
}
|
|
301
301
|
|
|
302
302
|
/** DROP FOREIGN KEY */
|
|
303
|
-
export interface
|
|
304
|
-
type: "
|
|
303
|
+
export interface DropForeignKeyQueryDef {
|
|
304
|
+
type: "dropForeignKey";
|
|
305
305
|
table: QueryDefObjectName;
|
|
306
306
|
foreignKey: string;
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
/** CREATE INDEX */
|
|
310
|
-
export interface
|
|
311
|
-
type: "
|
|
310
|
+
export interface AddIndexQueryDef {
|
|
311
|
+
type: "addIndex";
|
|
312
312
|
table: QueryDefObjectName;
|
|
313
313
|
index: {
|
|
314
314
|
name: string;
|
|
@@ -318,8 +318,8 @@ export interface AddIdxQueryDef {
|
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
/** DROP INDEX */
|
|
321
|
-
export interface
|
|
322
|
-
type: "
|
|
321
|
+
export interface DropIndexQueryDef {
|
|
322
|
+
type: "dropIndex";
|
|
323
323
|
table: QueryDefObjectName;
|
|
324
324
|
index: string;
|
|
325
325
|
}
|
|
@@ -403,12 +403,12 @@ type DdlQueryDef =
|
|
|
403
403
|
| DropColumnQueryDef
|
|
404
404
|
| ModifyColumnQueryDef
|
|
405
405
|
| RenameColumnQueryDef
|
|
406
|
-
|
|
|
407
|
-
|
|
|
408
|
-
|
|
|
409
|
-
|
|
|
410
|
-
|
|
|
411
|
-
|
|
|
406
|
+
| DropPrimaryKeyQueryDef
|
|
407
|
+
| AddPrimaryKeyQueryDef
|
|
408
|
+
| AddForeignKeyQueryDef
|
|
409
|
+
| DropForeignKeyQueryDef
|
|
410
|
+
| AddIndexQueryDef
|
|
411
|
+
| DropIndexQueryDef
|
|
412
412
|
| CreateViewQueryDef
|
|
413
413
|
| DropViewQueryDef
|
|
414
414
|
| CreateProcQueryDef
|
|
@@ -433,12 +433,12 @@ export const DDL_TYPES = [
|
|
|
433
433
|
"dropColumn",
|
|
434
434
|
"modifyColumn",
|
|
435
435
|
"renameColumn",
|
|
436
|
-
"
|
|
437
|
-
"
|
|
438
|
-
"
|
|
439
|
-
"
|
|
440
|
-
"
|
|
441
|
-
"
|
|
436
|
+
"dropPrimaryKey",
|
|
437
|
+
"addPrimaryKey",
|
|
438
|
+
"addForeignKey",
|
|
439
|
+
"dropForeignKey",
|
|
440
|
+
"addIndex",
|
|
441
|
+
"dropIndex",
|
|
442
442
|
"createView",
|
|
443
443
|
"dropView",
|
|
444
444
|
"createProc",
|
|
@@ -490,12 +490,12 @@ export type QueryDef =
|
|
|
490
490
|
| RenameColumnQueryDef
|
|
491
491
|
|
|
492
492
|
// DDL - PK/FK/Index
|
|
493
|
-
|
|
|
494
|
-
|
|
|
495
|
-
|
|
|
496
|
-
|
|
|
497
|
-
|
|
|
498
|
-
|
|
|
493
|
+
| DropPrimaryKeyQueryDef
|
|
494
|
+
| AddPrimaryKeyQueryDef
|
|
495
|
+
| AddForeignKeyQueryDef
|
|
496
|
+
| DropForeignKeyQueryDef
|
|
497
|
+
| AddIndexQueryDef
|
|
498
|
+
| DropIndexQueryDef
|
|
499
499
|
|
|
500
500
|
// DDL - View/Procedure
|
|
501
501
|
| CreateViewQueryDef
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { bytes, obj, DateOnly, DateTime, Time, Uuid } from "@simplysm/core-common";
|
|
2
2
|
import type { ColumnPrimitiveStr } from "../types/column";
|
|
3
3
|
import type { ResultMeta } from "../types/db";
|
|
4
4
|
|
|
@@ -55,7 +55,7 @@ function parseValue(value: unknown, type: ColumnPrimitiveStr): unknown {
|
|
|
55
55
|
|
|
56
56
|
case "Bytes":
|
|
57
57
|
if (value instanceof Uint8Array) return value;
|
|
58
|
-
if (typeof value === "string") return
|
|
58
|
+
if (typeof value === "string") return bytes.fromHex(value);
|
|
59
59
|
throw new Error(`Failed to parse Bytes: ${typeof value}`);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -64,6 +64,22 @@ function parseValue(value: unknown, type: ColumnPrimitiveStr): unknown {
|
|
|
64
64
|
// Grouping Utilities
|
|
65
65
|
// ============================================
|
|
66
66
|
|
|
67
|
+
/** Precomputed column metadata for flatToNested */
|
|
68
|
+
interface ColumnInfo {
|
|
69
|
+
key: string;
|
|
70
|
+
type: ColumnPrimitiveStr;
|
|
71
|
+
parts: string[] | undefined; // undefined for simple keys, string[] for nested keys
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Precompute column info once per unique columns object */
|
|
75
|
+
function buildColumnInfos(columns: Record<string, ColumnPrimitiveStr>): ColumnInfo[] {
|
|
76
|
+
return Object.entries(columns).map(([key, type]) => ({
|
|
77
|
+
key,
|
|
78
|
+
type,
|
|
79
|
+
parts: key.includes(".") ? key.split(".") : undefined,
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
|
|
67
83
|
/**
|
|
68
84
|
* Transform flat record to nested object
|
|
69
85
|
*
|
|
@@ -72,20 +88,19 @@ function parseValue(value: unknown, type: ColumnPrimitiveStr): unknown {
|
|
|
72
88
|
*/
|
|
73
89
|
function flatToNested(
|
|
74
90
|
record: Record<string, unknown>,
|
|
75
|
-
|
|
91
|
+
columnInfos: ColumnInfo[],
|
|
76
92
|
): Record<string, unknown> {
|
|
77
93
|
const result: Record<string, unknown> = {};
|
|
78
94
|
|
|
79
|
-
for (const
|
|
95
|
+
for (const { key, type, parts } of columnInfos) {
|
|
80
96
|
const rawValue = record[key];
|
|
81
97
|
const parsedValue = parseValue(rawValue, type);
|
|
82
98
|
|
|
83
99
|
// undefined values are not added as keys
|
|
84
100
|
if (parsedValue === undefined) continue;
|
|
85
101
|
|
|
86
|
-
if (
|
|
102
|
+
if (parts != null) {
|
|
87
103
|
// Nested key: "posts.id" → { posts: { id: ... } }
|
|
88
|
-
const parts = key.split(".");
|
|
89
104
|
let current = result;
|
|
90
105
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
91
106
|
const part = parts[i];
|
|
@@ -107,8 +122,8 @@ function flatToNested(
|
|
|
107
122
|
/**
|
|
108
123
|
* Check if object is empty (all values are undefined)
|
|
109
124
|
*/
|
|
110
|
-
function isEmptyObject(
|
|
111
|
-
return Object.keys(
|
|
125
|
+
function isEmptyObject(record: Record<string, unknown>): boolean {
|
|
126
|
+
return Object.keys(record).length === 0;
|
|
112
127
|
}
|
|
113
128
|
|
|
114
129
|
// ============================================
|
|
@@ -186,6 +201,7 @@ async function parseSimpleRecords<TRecord>(
|
|
|
186
201
|
rawResults: Record<string, unknown>[],
|
|
187
202
|
columns: Record<string, ColumnPrimitiveStr>,
|
|
188
203
|
): Promise<TRecord[] | undefined> {
|
|
204
|
+
const columnInfos = buildColumnInfos(columns);
|
|
189
205
|
const results: Record<string, unknown>[] = [];
|
|
190
206
|
|
|
191
207
|
for (let i = 0; i < rawResults.length; i++) {
|
|
@@ -194,7 +210,7 @@ async function parseSimpleRecords<TRecord>(
|
|
|
194
210
|
await yieldToEventLoop();
|
|
195
211
|
}
|
|
196
212
|
|
|
197
|
-
const parsed = flatToNested(rawResults[i],
|
|
213
|
+
const parsed = flatToNested(rawResults[i], columnInfos);
|
|
198
214
|
|
|
199
215
|
// Exclude empty objects
|
|
200
216
|
if (!isEmptyObject(parsed)) {
|
|
@@ -226,12 +242,13 @@ async function parseJoinedRecords<TRecord>(
|
|
|
226
242
|
meta: ResultMeta,
|
|
227
243
|
): Promise<TRecord[] | undefined> {
|
|
228
244
|
// 1. Transform all records to nested structure
|
|
245
|
+
const columnInfos = buildColumnInfos(meta.columns);
|
|
229
246
|
const nestedRecords: Record<string, unknown>[] = [];
|
|
230
247
|
for (let i = 0; i < rawResults.length; i++) {
|
|
231
248
|
if (i > 0 && i % YIELD_INTERVAL === 0) {
|
|
232
249
|
await yieldToEventLoop();
|
|
233
250
|
}
|
|
234
|
-
nestedRecords.push(flatToNested(rawResults[i],
|
|
251
|
+
nestedRecords.push(flatToNested(rawResults[i], columnInfos));
|
|
235
252
|
}
|
|
236
253
|
|
|
237
254
|
// 2. Sort JOIN keys by depth (shallower ones first)
|
|
@@ -253,7 +270,15 @@ async function parseJoinedRecords<TRecord>(
|
|
|
253
270
|
*/
|
|
254
271
|
function serializeGroupKey(groupKey: Record<string, unknown>, cachedKeyOrder?: string[]): string {
|
|
255
272
|
const keys = cachedKeyOrder ?? Object.keys(groupKey).sort((a, b) => a.localeCompare(b));
|
|
256
|
-
|
|
273
|
+
let result = "";
|
|
274
|
+
for (let i = 0; i < keys.length; i++) {
|
|
275
|
+
if (i > 0) result += "|";
|
|
276
|
+
const v = groupKey[keys[i]];
|
|
277
|
+
result += keys[i];
|
|
278
|
+
result += ":";
|
|
279
|
+
result += v === null ? "null" : String(v);
|
|
280
|
+
}
|
|
281
|
+
return result;
|
|
257
282
|
}
|
|
258
283
|
|
|
259
284
|
/**
|
|
@@ -295,12 +320,15 @@ function groupRecordsRecursively(
|
|
|
295
320
|
// Map-based grouping (O(n) complexity)
|
|
296
321
|
const groupMap = new Map<string, Record<string, unknown>>();
|
|
297
322
|
|
|
323
|
+
// Precompute join key exclusion set for O(1) lookup
|
|
324
|
+
const joinKeyExclusions = buildJoinKeyExclusionSet(childJoinKeys);
|
|
325
|
+
|
|
298
326
|
// Key order caching (determined from first record and reused)
|
|
299
327
|
let groupKeyOrder: string[] | undefined;
|
|
300
328
|
|
|
301
329
|
for (const record of records) {
|
|
302
330
|
// Extract and serialize group key (excluding JOIN keys)
|
|
303
|
-
const groupKey = extractGroupKey(record,
|
|
331
|
+
const groupKey = extractGroupKey(record, joinKeyExclusions);
|
|
304
332
|
if (groupKeyOrder == null) {
|
|
305
333
|
groupKeyOrder = Object.keys(groupKey).sort((a, b) => a.localeCompare(b));
|
|
306
334
|
}
|
|
@@ -382,17 +410,33 @@ function groupRecordsRecursively(
|
|
|
382
410
|
return grouped;
|
|
383
411
|
}
|
|
384
412
|
|
|
413
|
+
/**
|
|
414
|
+
* Build a Set of keys that should be excluded from group key (join keys and their prefixes)
|
|
415
|
+
*/
|
|
416
|
+
function buildJoinKeyExclusionSet(joinKeys: string[]): Set<string> {
|
|
417
|
+
const exclusions = new Set<string>();
|
|
418
|
+
for (const jk of joinKeys) {
|
|
419
|
+
exclusions.add(jk);
|
|
420
|
+
// Also exclude parent paths (e.g., "posts" for join key "posts.comments")
|
|
421
|
+
const parts = jk.split(".");
|
|
422
|
+
for (let i = 1; i < parts.length; i++) {
|
|
423
|
+
exclusions.add(parts.slice(0, i).join("."));
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return exclusions;
|
|
427
|
+
}
|
|
428
|
+
|
|
385
429
|
/**
|
|
386
430
|
* Extract group key from record excluding JOIN keys
|
|
387
431
|
*/
|
|
388
432
|
function extractGroupKey(
|
|
389
433
|
record: Record<string, unknown>,
|
|
390
|
-
|
|
434
|
+
joinKeyExclusions: Set<string>,
|
|
391
435
|
): Record<string, unknown> {
|
|
392
436
|
const result: Record<string, unknown> = {};
|
|
393
437
|
for (const [key, value] of Object.entries(record)) {
|
|
394
438
|
// Only include non-JOIN keys
|
|
395
|
-
if (!
|
|
439
|
+
if (!joinKeyExclusions.has(key)) {
|
|
396
440
|
// Only use primitive values (not object/array) as group key
|
|
397
441
|
if (value == null || typeof value !== "object") {
|
|
398
442
|
result[key] = value;
|
|
@@ -422,7 +466,7 @@ function mergeJoinData(
|
|
|
422
466
|
if (isSingle) {
|
|
423
467
|
// isSingle: true - error if data exists and values differ
|
|
424
468
|
if (existingJoinData != null) {
|
|
425
|
-
if (!
|
|
469
|
+
if (!obj.equal(existingJoinData as Record<string, unknown>, newJoinData)) {
|
|
426
470
|
throw new Error(`isSingle relationship '${localKey}' has multiple different results.`);
|
|
427
471
|
}
|
|
428
472
|
} else {
|
|
@@ -447,7 +491,7 @@ function mergeJoinData(
|
|
|
447
491
|
} else {
|
|
448
492
|
// Fallback without hashSet (legacy approach)
|
|
449
493
|
const isDuplicate = existingJoinData.some((item) =>
|
|
450
|
-
|
|
494
|
+
obj.equal(item as Record<string, unknown>, newJoinData),
|
|
451
495
|
);
|
|
452
496
|
if (!isDuplicate) {
|
|
453
497
|
existingJoinData.push(newJoinData);
|
|
@@ -50,21 +50,6 @@ describe("createDbContext", () => {
|
|
|
50
50
|
expect(postDef.as).toBe("T2");
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
it("DDL methods exist on instance", () => {
|
|
54
|
-
const db = createDbContext(TestDb, new MockExecutor(), {
|
|
55
|
-
database: "TestDb",
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// QueryDef generators
|
|
59
|
-
expect(typeof db.getCreateTableQueryDef).toBe("function");
|
|
60
|
-
expect(typeof db.getAddColumnQueryDef).toBe("function");
|
|
61
|
-
expect(typeof db.getClearSchemaQueryDef).toBe("function");
|
|
62
|
-
|
|
63
|
-
// Execution methods
|
|
64
|
-
expect(typeof db.createTable).toBe("function");
|
|
65
|
-
expect(typeof db.addColumn).toBe("function");
|
|
66
|
-
});
|
|
67
|
-
|
|
68
53
|
it("DDL QueryDef generators produce correct output", () => {
|
|
69
54
|
const db = createDbContext(TestDb, new MockExecutor(), {
|
|
70
55
|
database: "TestDb",
|
|
@@ -79,14 +64,14 @@ describe("createDbContext", () => {
|
|
|
79
64
|
});
|
|
80
65
|
});
|
|
81
66
|
|
|
82
|
-
it("connect/
|
|
67
|
+
it("connect/transaction methods exist", () => {
|
|
83
68
|
const db = createDbContext(TestDb, new MockExecutor(), {
|
|
84
69
|
database: "TestDb",
|
|
85
70
|
});
|
|
86
71
|
|
|
87
72
|
expect(typeof db.connect).toBe("function");
|
|
88
73
|
expect(typeof db.connectWithoutTransaction).toBe("function");
|
|
89
|
-
expect(typeof db.
|
|
74
|
+
expect(typeof db.transaction).toBe("function");
|
|
90
75
|
});
|
|
91
76
|
|
|
92
77
|
it("connect manages status lifecycle", async () => {
|
|
@@ -115,7 +100,7 @@ describe("createDbContext", () => {
|
|
|
115
100
|
expect(db.status).toBe("ready");
|
|
116
101
|
});
|
|
117
102
|
|
|
118
|
-
it("
|
|
103
|
+
it("transaction manages status lifecycle within connectWithoutTransaction", async () => {
|
|
119
104
|
const db = createDbContext(TestDb, new MockExecutor(), {
|
|
120
105
|
database: "TestDb",
|
|
121
106
|
});
|
|
@@ -123,7 +108,7 @@ describe("createDbContext", () => {
|
|
|
123
108
|
await db.connectWithoutTransaction(async () => {
|
|
124
109
|
await Promise.resolve();
|
|
125
110
|
expect(db.status).toBe("connect");
|
|
126
|
-
await db.
|
|
111
|
+
await db.transaction(async () => {
|
|
127
112
|
await Promise.resolve();
|
|
128
113
|
expect(db.status).toBe("transact");
|
|
129
114
|
});
|
|
@@ -181,33 +166,17 @@ describe("createDbContext", () => {
|
|
|
181
166
|
expect(db.status).toBe("ready");
|
|
182
167
|
});
|
|
183
168
|
|
|
184
|
-
it("
|
|
169
|
+
it("transaction throws when already in transaction state", async () => {
|
|
185
170
|
const db = createDbContext(TestDb, new MockExecutor(), {
|
|
186
171
|
database: "TestDb",
|
|
187
172
|
});
|
|
188
173
|
|
|
189
174
|
await db.connect(async () => {
|
|
190
175
|
// Already in transact state via connect
|
|
191
|
-
await expect(db.
|
|
176
|
+
await expect(db.transaction(async () => {})).rejects.toThrow("Already in TRANSACTION state.");
|
|
192
177
|
});
|
|
193
178
|
});
|
|
194
179
|
|
|
195
|
-
it("_migration accessor exists", () => {
|
|
196
|
-
const db = createDbContext(TestDb, new MockExecutor(), {
|
|
197
|
-
database: "TestDb",
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
expect(typeof db._migration).toBe("function");
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("initialize method exists", () => {
|
|
204
|
-
const db = createDbContext(TestDb, new MockExecutor(), {
|
|
205
|
-
database: "TestDb",
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
expect(typeof db.initialize).toBe("function");
|
|
209
|
-
});
|
|
210
|
-
|
|
211
180
|
it("getQueryDefObjectName resolves table with defaults", () => {
|
|
212
181
|
const db = createDbContext(TestDb, new MockExecutor(), {
|
|
213
182
|
database: "TestDb",
|
|
@@ -1,40 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import type { DbContextBase, DbContextDef } from "../../src/types/db-context-def";
|
|
3
2
|
import { defineDbContext } from "../../src/define-db-context";
|
|
4
3
|
import { User } from "../setup/models/User";
|
|
5
4
|
import { Post } from "../setup/models/Post";
|
|
6
|
-
import { ActiveUsers } from "../setup/views/ActiveUsers";
|
|
7
|
-
|
|
8
|
-
describe("DbContext Types", () => {
|
|
9
|
-
it("DbContextBase interface has required members", () => {
|
|
10
|
-
// Type-level test: verify the interface exists with correct shape
|
|
11
|
-
const base: DbContextBase = {
|
|
12
|
-
status: "ready",
|
|
13
|
-
database: "test",
|
|
14
|
-
schema: undefined,
|
|
15
|
-
getNextAlias: () => "T1",
|
|
16
|
-
resetAliasCounter: () => {},
|
|
17
|
-
executeDefs: () => Promise.resolve([[]]),
|
|
18
|
-
getQueryDefObjectName: () => ({ database: "test", name: "test" }),
|
|
19
|
-
switchFk: () => Promise.resolve(),
|
|
20
|
-
};
|
|
21
|
-
expect(base.status).toBe("ready");
|
|
22
|
-
expect(base.getNextAlias()).toBe("T1");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("DbContextDef has meta property", () => {
|
|
26
|
-
const def: DbContextDef<{}, {}> = {
|
|
27
|
-
meta: {
|
|
28
|
-
tables: {},
|
|
29
|
-
views: {},
|
|
30
|
-
procedures: {},
|
|
31
|
-
migrations: [],
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
expect(def.meta.tables).toEqual({});
|
|
35
|
-
expect(def.meta.migrations).toEqual([]);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
5
|
|
|
39
6
|
describe("defineDbContext", () => {
|
|
40
7
|
it("creates a DbContextDef with tables", () => {
|
|
@@ -47,22 +14,4 @@ describe("defineDbContext", () => {
|
|
|
47
14
|
expect(MyDb.meta.migrations).toEqual([]);
|
|
48
15
|
});
|
|
49
16
|
|
|
50
|
-
it("creates a DbContextDef with views", () => {
|
|
51
|
-
const MyDb = defineDbContext({
|
|
52
|
-
tables: { user: User },
|
|
53
|
-
views: { activeUsers: ActiveUsers },
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
expect(MyDb.meta.views.activeUsers).toBe(ActiveUsers);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("creates a DbContextDef with migrations", () => {
|
|
60
|
-
const migrations = [{ name: "test", up: async () => {} }];
|
|
61
|
-
const MyDb = defineDbContext({
|
|
62
|
-
tables: { user: User },
|
|
63
|
-
migrations,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
expect(MyDb.meta.migrations).toBe(migrations);
|
|
67
|
-
});
|
|
68
17
|
});
|
|
@@ -212,19 +212,19 @@ export const renameColumn: ExpectedSql = {
|
|
|
212
212
|
|
|
213
213
|
//#region ========== Primary Key ==========
|
|
214
214
|
|
|
215
|
-
export const
|
|
215
|
+
export const dropPrimaryKey: ExpectedSql = {
|
|
216
216
|
mysql: mysql`ALTER TABLE \`TestDb\`.\`User\` DROP PRIMARY KEY`,
|
|
217
217
|
mssql: tsql`ALTER TABLE [TestDb].[TestSchema].[User] DROP CONSTRAINT [PK_User]`,
|
|
218
218
|
postgresql: pgsql`ALTER TABLE "TestSchema"."User" DROP CONSTRAINT "PK_User"`,
|
|
219
219
|
};
|
|
220
220
|
|
|
221
|
-
export const
|
|
221
|
+
export const addPrimaryKey: ExpectedSql = {
|
|
222
222
|
mysql: mysql`ALTER TABLE \`TestDb\`.\`User\` ADD PRIMARY KEY (\`id\`)`,
|
|
223
223
|
mssql: tsql`ALTER TABLE [TestDb].[TestSchema].[User] ADD CONSTRAINT [PK_User] PRIMARY KEY ([id])`,
|
|
224
224
|
postgresql: pgsql`ALTER TABLE "TestSchema"."User" ADD CONSTRAINT "PK_User" PRIMARY KEY ("id")`,
|
|
225
225
|
};
|
|
226
226
|
|
|
227
|
-
export const
|
|
227
|
+
export const addPrimaryKeyComposite: ExpectedSql = {
|
|
228
228
|
mysql: mysql`ALTER TABLE \`TestDb\`.\`UserRole\` ADD PRIMARY KEY (\`userId\`, \`roleId\`)`,
|
|
229
229
|
mssql: tsql`ALTER TABLE [TestDb].[TestSchema].[UserRole] ADD CONSTRAINT [PK_UserRole] PRIMARY KEY ([userId], [roleId])`,
|
|
230
230
|
postgresql: pgsql`ALTER TABLE "TestSchema"."UserRole" ADD CONSTRAINT "PK_UserRole" PRIMARY KEY ("userId", "roleId")`,
|
|
@@ -234,7 +234,7 @@ export const addPkComposite: ExpectedSql = {
|
|
|
234
234
|
|
|
235
235
|
//#region ========== Foreign Key / Index ==========
|
|
236
236
|
|
|
237
|
-
export const
|
|
237
|
+
export const addForeignKey: ExpectedSql = {
|
|
238
238
|
mysql: mysql`
|
|
239
239
|
ALTER TABLE \`TestDb\`.\`Post\` ADD CONSTRAINT \`FK_Post_user\`
|
|
240
240
|
FOREIGN KEY (\`userId\`) REFERENCES \`TestDb\`.\`User\` (\`id\`)
|
|
@@ -251,25 +251,25 @@ CREATE INDEX "IDX_Post_Post_user" ON "TestSchema"."Post" ("userId");
|
|
|
251
251
|
`,
|
|
252
252
|
};
|
|
253
253
|
|
|
254
|
-
export const
|
|
254
|
+
export const dropForeignKey: ExpectedSql = {
|
|
255
255
|
mysql: mysql`ALTER TABLE \`TestDb\`.\`Post\` DROP FOREIGN KEY \`FK_Post_user\``,
|
|
256
256
|
mssql: tsql`ALTER TABLE [TestDb].[TestSchema].[Post] DROP CONSTRAINT [FK_Post_user]`,
|
|
257
257
|
postgresql: pgsql`ALTER TABLE "TestSchema"."Post" DROP CONSTRAINT "FK_Post_user"`,
|
|
258
258
|
};
|
|
259
259
|
|
|
260
|
-
export const
|
|
260
|
+
export const addIndex: ExpectedSql = {
|
|
261
261
|
mysql: mysql`CREATE UNIQUE INDEX \`IDX_User_email\` ON \`TestDb\`.\`User\` (\`email\` ASC)`,
|
|
262
262
|
mssql: tsql`CREATE UNIQUE INDEX [IDX_User_email] ON [TestDb].[TestSchema].[User] ([email] ASC)`,
|
|
263
263
|
postgresql: pgsql`CREATE UNIQUE INDEX "IDX_User_email" ON "TestSchema"."User" ("email" ASC)`,
|
|
264
264
|
};
|
|
265
265
|
|
|
266
|
-
export const
|
|
266
|
+
export const dropIndex: ExpectedSql = {
|
|
267
267
|
mysql: mysql`DROP INDEX \`IDX_User_email\` ON \`TestDb\`.\`User\``,
|
|
268
268
|
mssql: tsql`DROP INDEX [IDX_User_email] ON [TestDb].[TestSchema].[User]`,
|
|
269
269
|
postgresql: pgsql`DROP INDEX "TestSchema"."IDX_User_email"`,
|
|
270
270
|
};
|
|
271
271
|
|
|
272
|
-
export const
|
|
272
|
+
export const dropIndexComposite: ExpectedSql = {
|
|
273
273
|
mysql: mysql`DROP INDEX \`IDX_User_name_email\` ON \`TestDb\`.\`User\``,
|
|
274
274
|
mssql: tsql`DROP INDEX [IDX_User_name_email] ON [TestDb].[TestSchema].[User]`,
|
|
275
275
|
postgresql: pgsql`DROP INDEX "TestSchema"."IDX_User_name_email"`,
|