metal-orm 1.0.11 → 1.0.13
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 +21 -18
- package/dist/decorators/index.cjs +317 -34
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +1 -1
- package/dist/decorators/index.d.ts +1 -1
- package/dist/decorators/index.js +317 -34
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +1965 -267
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +273 -23
- package/dist/index.d.ts +273 -23
- package/dist/index.js +1947 -267
- package/dist/index.js.map +1 -1
- package/dist/{select-654m4qy8.d.cts → select-CCp1oz9p.d.cts} +254 -4
- package/dist/{select-654m4qy8.d.ts → select-CCp1oz9p.d.ts} +254 -4
- package/package.json +3 -2
- package/src/core/ast/query.ts +40 -22
- package/src/core/ddl/dialects/base-schema-dialect.ts +48 -0
- package/src/core/ddl/dialects/index.ts +5 -0
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +97 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +109 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +99 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +103 -0
- package/src/core/ddl/introspect/mssql.ts +149 -0
- package/src/core/ddl/introspect/mysql.ts +99 -0
- package/src/core/ddl/introspect/postgres.ts +154 -0
- package/src/core/ddl/introspect/sqlite.ts +66 -0
- package/src/core/ddl/introspect/types.ts +19 -0
- package/src/core/ddl/introspect/utils.ts +27 -0
- package/src/core/ddl/schema-diff.ts +179 -0
- package/src/core/ddl/schema-generator.ts +229 -0
- package/src/core/ddl/schema-introspect.ts +32 -0
- package/src/core/ddl/schema-types.ts +39 -0
- package/src/core/dialect/abstract.ts +122 -37
- package/src/core/dialect/base/sql-dialect.ts +204 -0
- package/src/core/dialect/mssql/index.ts +125 -80
- package/src/core/dialect/mysql/index.ts +18 -112
- package/src/core/dialect/postgres/index.ts +29 -126
- package/src/core/dialect/sqlite/index.ts +28 -129
- package/src/index.ts +4 -0
- package/src/orm/execute.ts +25 -16
- package/src/orm/orm-context.ts +60 -55
- package/src/orm/query-logger.ts +38 -0
- package/src/orm/relations/belongs-to.ts +42 -26
- package/src/orm/relations/has-many.ts +41 -25
- package/src/orm/relations/many-to-many.ts +43 -27
- package/src/orm/unit-of-work.ts +60 -23
- package/src/query-builder/hydration-manager.ts +229 -25
- package/src/query-builder/query-ast-service.ts +27 -12
- package/src/query-builder/select-query-state.ts +24 -12
- package/src/query-builder/select.ts +58 -14
- package/src/schema/column.ts +206 -27
- package/src/schema/table.ts +89 -32
- package/src/schema/types.ts +8 -5
package/dist/index.js
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
// src/schema/table.ts
|
|
2
|
-
var defineTable = (name, columns, relations = {}, hooks) => {
|
|
2
|
+
var defineTable = (name, columns, relations = {}, hooks, options = {}) => {
|
|
3
3
|
const colsWithNames = Object.entries(columns).reduce((acc, [key, def]) => {
|
|
4
4
|
acc[key] = { ...def, name: key, table: name };
|
|
5
5
|
return acc;
|
|
6
6
|
}, {});
|
|
7
|
-
return {
|
|
7
|
+
return {
|
|
8
|
+
name,
|
|
9
|
+
schema: options.schema,
|
|
10
|
+
columns: colsWithNames,
|
|
11
|
+
relations,
|
|
12
|
+
hooks,
|
|
13
|
+
primaryKey: options.primaryKey,
|
|
14
|
+
indexes: options.indexes,
|
|
15
|
+
checks: options.checks,
|
|
16
|
+
comment: options.comment,
|
|
17
|
+
engine: options.engine,
|
|
18
|
+
charset: options.charset,
|
|
19
|
+
collation: options.collation
|
|
20
|
+
};
|
|
8
21
|
};
|
|
9
22
|
|
|
10
23
|
// src/schema/column.ts
|
|
@@ -14,12 +27,52 @@ var col = {
|
|
|
14
27
|
* @returns ColumnDef with INT type
|
|
15
28
|
*/
|
|
16
29
|
int: () => ({ name: "", type: "INT" }),
|
|
30
|
+
/**
|
|
31
|
+
* Creates a big integer column definition
|
|
32
|
+
*/
|
|
33
|
+
bigint: () => ({ name: "", type: "BIGINT" }),
|
|
17
34
|
/**
|
|
18
35
|
* Creates a variable character column definition
|
|
19
36
|
* @param length - Maximum length of the string
|
|
20
37
|
* @returns ColumnDef with VARCHAR type
|
|
21
38
|
*/
|
|
22
39
|
varchar: (length) => ({ name: "", type: "VARCHAR", args: [length] }),
|
|
40
|
+
/**
|
|
41
|
+
* Creates a fixed precision decimal column definition
|
|
42
|
+
*/
|
|
43
|
+
decimal: (precision, scale = 0) => ({
|
|
44
|
+
name: "",
|
|
45
|
+
type: "DECIMAL",
|
|
46
|
+
args: [precision, scale]
|
|
47
|
+
}),
|
|
48
|
+
/**
|
|
49
|
+
* Creates a floating point column definition
|
|
50
|
+
*/
|
|
51
|
+
float: (precision) => ({
|
|
52
|
+
name: "",
|
|
53
|
+
type: "FLOAT",
|
|
54
|
+
args: precision !== void 0 ? [precision] : void 0
|
|
55
|
+
}),
|
|
56
|
+
/**
|
|
57
|
+
* Creates a UUID column definition
|
|
58
|
+
*/
|
|
59
|
+
uuid: () => ({ name: "", type: "UUID" }),
|
|
60
|
+
/**
|
|
61
|
+
* Creates a timestamp column definition
|
|
62
|
+
*/
|
|
63
|
+
timestamp: () => ({ name: "", type: "TIMESTAMP" }),
|
|
64
|
+
/**
|
|
65
|
+
* Creates a timestamptz column definition
|
|
66
|
+
*/
|
|
67
|
+
timestamptz: () => ({ name: "", type: "TIMESTAMPTZ" }),
|
|
68
|
+
/**
|
|
69
|
+
* Creates a date column definition
|
|
70
|
+
*/
|
|
71
|
+
date: () => ({ name: "", type: "DATE" }),
|
|
72
|
+
/**
|
|
73
|
+
* Creates a datetime column definition
|
|
74
|
+
*/
|
|
75
|
+
datetime: () => ({ name: "", type: "DATETIME" }),
|
|
23
76
|
/**
|
|
24
77
|
* Creates a JSON column definition
|
|
25
78
|
* @returns ColumnDef with JSON type
|
|
@@ -30,12 +83,64 @@ var col = {
|
|
|
30
83
|
* @returns ColumnDef with BOOLEAN type
|
|
31
84
|
*/
|
|
32
85
|
boolean: () => ({ name: "", type: "BOOLEAN" }),
|
|
86
|
+
/**
|
|
87
|
+
* Creates an enum column definition
|
|
88
|
+
* @param values - Enum values
|
|
89
|
+
*/
|
|
90
|
+
enum: (values) => ({ name: "", type: "ENUM", args: values }),
|
|
33
91
|
/**
|
|
34
92
|
* Marks a column definition as a primary key
|
|
35
93
|
* @param def - Column definition to modify
|
|
36
94
|
* @returns Modified ColumnDef with primary: true
|
|
37
95
|
*/
|
|
38
|
-
primaryKey: (def) => ({ ...def, primary: true })
|
|
96
|
+
primaryKey: (def) => ({ ...def, primary: true }),
|
|
97
|
+
/**
|
|
98
|
+
* Marks a column as NOT NULL
|
|
99
|
+
*/
|
|
100
|
+
notNull: (def) => ({ ...def, notNull: true }),
|
|
101
|
+
/**
|
|
102
|
+
* Marks a column as UNIQUE
|
|
103
|
+
*/
|
|
104
|
+
unique: (def, name) => ({
|
|
105
|
+
...def,
|
|
106
|
+
unique: name ?? true
|
|
107
|
+
}),
|
|
108
|
+
/**
|
|
109
|
+
* Sets a default value for the column
|
|
110
|
+
*/
|
|
111
|
+
default: (def, value) => ({
|
|
112
|
+
...def,
|
|
113
|
+
default: value
|
|
114
|
+
}),
|
|
115
|
+
/**
|
|
116
|
+
* Sets a raw SQL default value for the column
|
|
117
|
+
*/
|
|
118
|
+
defaultRaw: (def, expression) => ({
|
|
119
|
+
...def,
|
|
120
|
+
default: { raw: expression }
|
|
121
|
+
}),
|
|
122
|
+
/**
|
|
123
|
+
* Marks a column as auto-increment / identity
|
|
124
|
+
*/
|
|
125
|
+
autoIncrement: (def, strategy = "byDefault") => ({
|
|
126
|
+
...def,
|
|
127
|
+
autoIncrement: true,
|
|
128
|
+
generated: strategy
|
|
129
|
+
}),
|
|
130
|
+
/**
|
|
131
|
+
* Adds a foreign key reference
|
|
132
|
+
*/
|
|
133
|
+
references: (def, ref) => ({
|
|
134
|
+
...def,
|
|
135
|
+
references: ref
|
|
136
|
+
}),
|
|
137
|
+
/**
|
|
138
|
+
* Adds a check constraint to the column
|
|
139
|
+
*/
|
|
140
|
+
check: (def, expression) => ({
|
|
141
|
+
...def,
|
|
142
|
+
check: expression
|
|
143
|
+
})
|
|
39
144
|
};
|
|
40
145
|
|
|
41
146
|
// src/schema/relation.ts
|
|
@@ -445,6 +550,82 @@ var SelectQueryState = class _SelectQueryState {
|
|
|
445
550
|
ctes: [...this.ast.ctes ?? [], cte]
|
|
446
551
|
});
|
|
447
552
|
}
|
|
553
|
+
/**
|
|
554
|
+
* Adds a set operation (UNION/INTERSECT/EXCEPT) to the query
|
|
555
|
+
* @param op - Set operation node to add
|
|
556
|
+
* @returns New SelectQueryState with set operation
|
|
557
|
+
*/
|
|
558
|
+
withSetOperation(op) {
|
|
559
|
+
return this.clone({
|
|
560
|
+
...this.ast,
|
|
561
|
+
setOps: [...this.ast.setOps ?? [], op]
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// src/core/ast/join-node.ts
|
|
567
|
+
var createJoinNode = (kind, tableName, condition, relationName) => ({
|
|
568
|
+
type: "Join",
|
|
569
|
+
kind,
|
|
570
|
+
table: { type: "Table", name: tableName },
|
|
571
|
+
condition,
|
|
572
|
+
relationName
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// src/core/sql/sql.ts
|
|
576
|
+
var SQL_OPERATORS = {
|
|
577
|
+
/** Equality operator */
|
|
578
|
+
EQUALS: "=",
|
|
579
|
+
/** Not equals operator */
|
|
580
|
+
NOT_EQUALS: "!=",
|
|
581
|
+
/** Greater than operator */
|
|
582
|
+
GREATER_THAN: ">",
|
|
583
|
+
/** Greater than or equal operator */
|
|
584
|
+
GREATER_OR_EQUAL: ">=",
|
|
585
|
+
/** Less than operator */
|
|
586
|
+
LESS_THAN: "<",
|
|
587
|
+
/** Less than or equal operator */
|
|
588
|
+
LESS_OR_EQUAL: "<=",
|
|
589
|
+
/** LIKE pattern matching operator */
|
|
590
|
+
LIKE: "LIKE",
|
|
591
|
+
/** NOT LIKE pattern matching operator */
|
|
592
|
+
NOT_LIKE: "NOT LIKE",
|
|
593
|
+
/** IN membership operator */
|
|
594
|
+
IN: "IN",
|
|
595
|
+
/** NOT IN membership operator */
|
|
596
|
+
NOT_IN: "NOT IN",
|
|
597
|
+
/** BETWEEN range operator */
|
|
598
|
+
BETWEEN: "BETWEEN",
|
|
599
|
+
/** NOT BETWEEN range operator */
|
|
600
|
+
NOT_BETWEEN: "NOT BETWEEN",
|
|
601
|
+
/** IS NULL null check operator */
|
|
602
|
+
IS_NULL: "IS NULL",
|
|
603
|
+
/** IS NOT NULL null check operator */
|
|
604
|
+
IS_NOT_NULL: "IS NOT NULL",
|
|
605
|
+
/** Logical AND operator */
|
|
606
|
+
AND: "AND",
|
|
607
|
+
/** Logical OR operator */
|
|
608
|
+
OR: "OR",
|
|
609
|
+
/** EXISTS operator */
|
|
610
|
+
EXISTS: "EXISTS",
|
|
611
|
+
/** NOT EXISTS operator */
|
|
612
|
+
NOT_EXISTS: "NOT EXISTS"
|
|
613
|
+
};
|
|
614
|
+
var JOIN_KINDS = {
|
|
615
|
+
/** INNER JOIN type */
|
|
616
|
+
INNER: "INNER",
|
|
617
|
+
/** LEFT JOIN type */
|
|
618
|
+
LEFT: "LEFT",
|
|
619
|
+
/** RIGHT JOIN type */
|
|
620
|
+
RIGHT: "RIGHT",
|
|
621
|
+
/** CROSS JOIN type */
|
|
622
|
+
CROSS: "CROSS"
|
|
623
|
+
};
|
|
624
|
+
var ORDER_DIRECTIONS = {
|
|
625
|
+
/** Ascending order */
|
|
626
|
+
ASC: "ASC",
|
|
627
|
+
/** Descending order */
|
|
628
|
+
DESC: "DESC"
|
|
448
629
|
};
|
|
449
630
|
|
|
450
631
|
// src/query-builder/hydration-manager.ts
|
|
@@ -496,8 +677,26 @@ var HydrationManager = class _HydrationManager {
|
|
|
496
677
|
* @returns AST with hydration metadata
|
|
497
678
|
*/
|
|
498
679
|
applyToAst(ast) {
|
|
680
|
+
if (ast.setOps && ast.setOps.length > 0) {
|
|
681
|
+
return ast;
|
|
682
|
+
}
|
|
499
683
|
const plan = this.planner.getPlan();
|
|
500
684
|
if (!plan) return ast;
|
|
685
|
+
const needsPaginationGuard = this.requiresParentPagination(ast, plan);
|
|
686
|
+
const rewritten = needsPaginationGuard ? this.wrapForParentPagination(ast, plan) : ast;
|
|
687
|
+
return this.attachHydrationMeta(rewritten, plan);
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Gets the current hydration plan
|
|
691
|
+
* @returns Hydration plan or undefined if none exists
|
|
692
|
+
*/
|
|
693
|
+
getPlan() {
|
|
694
|
+
return this.planner.getPlan();
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Attaches hydration metadata to a query AST node.
|
|
698
|
+
*/
|
|
699
|
+
attachHydrationMeta(ast, plan) {
|
|
501
700
|
return {
|
|
502
701
|
...ast,
|
|
503
702
|
meta: {
|
|
@@ -507,11 +706,154 @@ var HydrationManager = class _HydrationManager {
|
|
|
507
706
|
};
|
|
508
707
|
}
|
|
509
708
|
/**
|
|
510
|
-
*
|
|
511
|
-
*
|
|
709
|
+
* Determines whether the query needs pagination rewriting to keep LIMIT/OFFSET
|
|
710
|
+
* applied to parent rows when eager-loading multiplicative relations.
|
|
512
711
|
*/
|
|
513
|
-
|
|
514
|
-
|
|
712
|
+
requiresParentPagination(ast, plan) {
|
|
713
|
+
const hasPagination = ast.limit !== void 0 || ast.offset !== void 0;
|
|
714
|
+
return hasPagination && this.hasMultiplyingRelations(plan);
|
|
715
|
+
}
|
|
716
|
+
hasMultiplyingRelations(plan) {
|
|
717
|
+
return plan.relations.some(
|
|
718
|
+
(rel) => rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Rewrites the query using CTEs so LIMIT/OFFSET target distinct parent rows
|
|
723
|
+
* instead of the joined result set.
|
|
724
|
+
*
|
|
725
|
+
* The strategy:
|
|
726
|
+
* - Hoist the original query (minus limit/offset) into a base CTE.
|
|
727
|
+
* - Select distinct parent ids from that base CTE with the original ordering and pagination.
|
|
728
|
+
* - Join the base CTE against the paged ids to retrieve the joined rows for just that page.
|
|
729
|
+
*/
|
|
730
|
+
wrapForParentPagination(ast, plan) {
|
|
731
|
+
const projectionNames = this.getProjectionNames(ast.columns);
|
|
732
|
+
if (!projectionNames) {
|
|
733
|
+
return ast;
|
|
734
|
+
}
|
|
735
|
+
const projectionAliases = this.buildProjectionAliasMap(ast.columns);
|
|
736
|
+
const projectionSet = new Set(projectionNames);
|
|
737
|
+
const rootPkAlias = projectionAliases.get(`${plan.rootTable}.${plan.rootPrimaryKey}`) ?? plan.rootPrimaryKey;
|
|
738
|
+
const baseCteName = this.nextCteName(ast.ctes, "__metal_pagination_base");
|
|
739
|
+
const baseQuery = {
|
|
740
|
+
...ast,
|
|
741
|
+
ctes: void 0,
|
|
742
|
+
limit: void 0,
|
|
743
|
+
offset: void 0,
|
|
744
|
+
orderBy: void 0,
|
|
745
|
+
meta: void 0
|
|
746
|
+
};
|
|
747
|
+
const baseCte = {
|
|
748
|
+
type: "CommonTableExpression",
|
|
749
|
+
name: baseCteName,
|
|
750
|
+
query: baseQuery,
|
|
751
|
+
recursive: false
|
|
752
|
+
};
|
|
753
|
+
const orderBy = this.mapOrderBy(ast.orderBy, plan, projectionAliases, baseCteName, projectionSet);
|
|
754
|
+
if (orderBy === null) {
|
|
755
|
+
return ast;
|
|
756
|
+
}
|
|
757
|
+
const pageCteName = this.nextCteName([...ast.ctes ?? [], baseCte], "__metal_pagination_page");
|
|
758
|
+
const pagingColumns = this.buildPagingColumns(rootPkAlias, orderBy, baseCteName);
|
|
759
|
+
const pageCte = {
|
|
760
|
+
type: "CommonTableExpression",
|
|
761
|
+
name: pageCteName,
|
|
762
|
+
query: {
|
|
763
|
+
type: "SelectQuery",
|
|
764
|
+
from: { type: "Table", name: baseCteName },
|
|
765
|
+
columns: pagingColumns,
|
|
766
|
+
joins: [],
|
|
767
|
+
distinct: [{ type: "Column", table: baseCteName, name: rootPkAlias }],
|
|
768
|
+
orderBy,
|
|
769
|
+
limit: ast.limit,
|
|
770
|
+
offset: ast.offset
|
|
771
|
+
},
|
|
772
|
+
recursive: false
|
|
773
|
+
};
|
|
774
|
+
const joinCondition = eq(
|
|
775
|
+
{ type: "Column", table: baseCteName, name: rootPkAlias },
|
|
776
|
+
{ type: "Column", table: pageCteName, name: rootPkAlias }
|
|
777
|
+
);
|
|
778
|
+
const outerColumns = projectionNames.map((name) => ({
|
|
779
|
+
type: "Column",
|
|
780
|
+
table: baseCteName,
|
|
781
|
+
name,
|
|
782
|
+
alias: name
|
|
783
|
+
}));
|
|
784
|
+
return {
|
|
785
|
+
type: "SelectQuery",
|
|
786
|
+
from: { type: "Table", name: baseCteName },
|
|
787
|
+
columns: outerColumns,
|
|
788
|
+
joins: [createJoinNode(JOIN_KINDS.INNER, pageCteName, joinCondition)],
|
|
789
|
+
orderBy,
|
|
790
|
+
ctes: [...ast.ctes ?? [], baseCte, pageCte]
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
nextCteName(existing, baseName) {
|
|
794
|
+
const names = new Set((existing ?? []).map((cte) => cte.name));
|
|
795
|
+
let candidate = baseName;
|
|
796
|
+
let suffix = 1;
|
|
797
|
+
while (names.has(candidate)) {
|
|
798
|
+
suffix += 1;
|
|
799
|
+
candidate = `${baseName}_${suffix}`;
|
|
800
|
+
}
|
|
801
|
+
return candidate;
|
|
802
|
+
}
|
|
803
|
+
getProjectionNames(columns) {
|
|
804
|
+
const names = [];
|
|
805
|
+
for (const col2 of columns) {
|
|
806
|
+
const alias = col2.alias ?? col2.name;
|
|
807
|
+
if (!alias) return void 0;
|
|
808
|
+
names.push(alias);
|
|
809
|
+
}
|
|
810
|
+
return names;
|
|
811
|
+
}
|
|
812
|
+
buildProjectionAliasMap(columns) {
|
|
813
|
+
const map = /* @__PURE__ */ new Map();
|
|
814
|
+
for (const col2 of columns) {
|
|
815
|
+
if (col2.type !== "Column") continue;
|
|
816
|
+
const node = col2;
|
|
817
|
+
const key = `${node.table}.${node.name}`;
|
|
818
|
+
map.set(key, node.alias ?? node.name);
|
|
819
|
+
}
|
|
820
|
+
return map;
|
|
821
|
+
}
|
|
822
|
+
mapOrderBy(orderBy, plan, projectionAliases, baseAlias, availableColumns) {
|
|
823
|
+
if (!orderBy || orderBy.length === 0) {
|
|
824
|
+
return void 0;
|
|
825
|
+
}
|
|
826
|
+
const mapped = [];
|
|
827
|
+
for (const ob of orderBy) {
|
|
828
|
+
if (ob.column.table !== plan.rootTable) {
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
831
|
+
const alias = projectionAliases.get(`${ob.column.table}.${ob.column.name}`) ?? ob.column.name;
|
|
832
|
+
if (!availableColumns.has(alias)) {
|
|
833
|
+
return null;
|
|
834
|
+
}
|
|
835
|
+
mapped.push({
|
|
836
|
+
type: "OrderBy",
|
|
837
|
+
column: { type: "Column", table: baseAlias, name: alias },
|
|
838
|
+
direction: ob.direction
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
return mapped;
|
|
842
|
+
}
|
|
843
|
+
buildPagingColumns(primaryKey, orderBy, tableAlias) {
|
|
844
|
+
const columns = [{ type: "Column", table: tableAlias, name: primaryKey, alias: primaryKey }];
|
|
845
|
+
if (!orderBy) return columns;
|
|
846
|
+
for (const ob of orderBy) {
|
|
847
|
+
if (!columns.some((col2) => col2.name === ob.column.name)) {
|
|
848
|
+
columns.push({
|
|
849
|
+
type: "Column",
|
|
850
|
+
table: tableAlias,
|
|
851
|
+
name: ob.column.name,
|
|
852
|
+
alias: ob.column.name
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
return columns;
|
|
515
857
|
}
|
|
516
858
|
};
|
|
517
859
|
|
|
@@ -774,6 +1116,20 @@ var QueryAstService = class {
|
|
|
774
1116
|
};
|
|
775
1117
|
return this.state.withCte(cte);
|
|
776
1118
|
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Adds a set operation (UNION/UNION ALL/INTERSECT/EXCEPT) to the query
|
|
1121
|
+
* @param operator - Set operator
|
|
1122
|
+
* @param query - Right-hand side query
|
|
1123
|
+
* @returns Updated query state with set operation
|
|
1124
|
+
*/
|
|
1125
|
+
withSetOperation(operator, query) {
|
|
1126
|
+
const op = {
|
|
1127
|
+
type: "SetOperation",
|
|
1128
|
+
operator,
|
|
1129
|
+
query
|
|
1130
|
+
};
|
|
1131
|
+
return this.state.withSetOperation(op);
|
|
1132
|
+
}
|
|
777
1133
|
/**
|
|
778
1134
|
* Selects a subquery as a column
|
|
779
1135
|
* @param alias - Alias for the subquery
|
|
@@ -926,15 +1282,6 @@ var RelationProjectionHelper = class {
|
|
|
926
1282
|
}
|
|
927
1283
|
};
|
|
928
1284
|
|
|
929
|
-
// src/core/ast/join-node.ts
|
|
930
|
-
var createJoinNode = (kind, tableName, condition, relationName) => ({
|
|
931
|
-
type: "Join",
|
|
932
|
-
kind,
|
|
933
|
-
table: { type: "Table", name: tableName },
|
|
934
|
-
condition,
|
|
935
|
-
relationName
|
|
936
|
-
});
|
|
937
|
-
|
|
938
1285
|
// src/query-builder/relation-conditions.ts
|
|
939
1286
|
var assertNever = (value) => {
|
|
940
1287
|
throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
|
|
@@ -990,62 +1337,6 @@ var buildRelationCorrelation = (root, relation) => {
|
|
|
990
1337
|
return baseRelationCondition(root, relation);
|
|
991
1338
|
};
|
|
992
1339
|
|
|
993
|
-
// src/core/sql/sql.ts
|
|
994
|
-
var SQL_OPERATORS = {
|
|
995
|
-
/** Equality operator */
|
|
996
|
-
EQUALS: "=",
|
|
997
|
-
/** Not equals operator */
|
|
998
|
-
NOT_EQUALS: "!=",
|
|
999
|
-
/** Greater than operator */
|
|
1000
|
-
GREATER_THAN: ">",
|
|
1001
|
-
/** Greater than or equal operator */
|
|
1002
|
-
GREATER_OR_EQUAL: ">=",
|
|
1003
|
-
/** Less than operator */
|
|
1004
|
-
LESS_THAN: "<",
|
|
1005
|
-
/** Less than or equal operator */
|
|
1006
|
-
LESS_OR_EQUAL: "<=",
|
|
1007
|
-
/** LIKE pattern matching operator */
|
|
1008
|
-
LIKE: "LIKE",
|
|
1009
|
-
/** NOT LIKE pattern matching operator */
|
|
1010
|
-
NOT_LIKE: "NOT LIKE",
|
|
1011
|
-
/** IN membership operator */
|
|
1012
|
-
IN: "IN",
|
|
1013
|
-
/** NOT IN membership operator */
|
|
1014
|
-
NOT_IN: "NOT IN",
|
|
1015
|
-
/** BETWEEN range operator */
|
|
1016
|
-
BETWEEN: "BETWEEN",
|
|
1017
|
-
/** NOT BETWEEN range operator */
|
|
1018
|
-
NOT_BETWEEN: "NOT BETWEEN",
|
|
1019
|
-
/** IS NULL null check operator */
|
|
1020
|
-
IS_NULL: "IS NULL",
|
|
1021
|
-
/** IS NOT NULL null check operator */
|
|
1022
|
-
IS_NOT_NULL: "IS NOT NULL",
|
|
1023
|
-
/** Logical AND operator */
|
|
1024
|
-
AND: "AND",
|
|
1025
|
-
/** Logical OR operator */
|
|
1026
|
-
OR: "OR",
|
|
1027
|
-
/** EXISTS operator */
|
|
1028
|
-
EXISTS: "EXISTS",
|
|
1029
|
-
/** NOT EXISTS operator */
|
|
1030
|
-
NOT_EXISTS: "NOT EXISTS"
|
|
1031
|
-
};
|
|
1032
|
-
var JOIN_KINDS = {
|
|
1033
|
-
/** INNER JOIN type */
|
|
1034
|
-
INNER: "INNER",
|
|
1035
|
-
/** LEFT JOIN type */
|
|
1036
|
-
LEFT: "LEFT",
|
|
1037
|
-
/** RIGHT JOIN type */
|
|
1038
|
-
RIGHT: "RIGHT",
|
|
1039
|
-
/** CROSS JOIN type */
|
|
1040
|
-
CROSS: "CROSS"
|
|
1041
|
-
};
|
|
1042
|
-
var ORDER_DIRECTIONS = {
|
|
1043
|
-
/** Ascending order */
|
|
1044
|
-
ASC: "ASC",
|
|
1045
|
-
/** Descending order */
|
|
1046
|
-
DESC: "DESC"
|
|
1047
|
-
};
|
|
1048
|
-
|
|
1049
1340
|
// src/query-builder/relation-service.ts
|
|
1050
1341
|
var RelationService = class {
|
|
1051
1342
|
/**
|
|
@@ -1491,6 +1782,16 @@ var hasEntityMeta = (entity) => {
|
|
|
1491
1782
|
|
|
1492
1783
|
// src/orm/relations/has-many.ts
|
|
1493
1784
|
var toKey2 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
1785
|
+
var hideInternal = (obj, keys) => {
|
|
1786
|
+
for (const key of keys) {
|
|
1787
|
+
Object.defineProperty(obj, key, {
|
|
1788
|
+
value: obj[key],
|
|
1789
|
+
writable: false,
|
|
1790
|
+
configurable: false,
|
|
1791
|
+
enumerable: false
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1494
1795
|
var DefaultHasManyCollection = class {
|
|
1495
1796
|
constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
|
|
1496
1797
|
this.ctx = ctx;
|
|
@@ -1506,6 +1807,7 @@ var DefaultHasManyCollection = class {
|
|
|
1506
1807
|
this.items = [];
|
|
1507
1808
|
this.added = /* @__PURE__ */ new Set();
|
|
1508
1809
|
this.removed = /* @__PURE__ */ new Set();
|
|
1810
|
+
hideInternal(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
|
|
1509
1811
|
this.hydrateFromCache();
|
|
1510
1812
|
}
|
|
1511
1813
|
async load() {
|
|
@@ -1581,10 +1883,23 @@ var DefaultHasManyCollection = class {
|
|
|
1581
1883
|
this.items = rows.map((row) => this.createEntity(row));
|
|
1582
1884
|
this.loaded = true;
|
|
1583
1885
|
}
|
|
1886
|
+
toJSON() {
|
|
1887
|
+
return this.items;
|
|
1888
|
+
}
|
|
1584
1889
|
};
|
|
1585
1890
|
|
|
1586
1891
|
// src/orm/relations/belongs-to.ts
|
|
1587
1892
|
var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
1893
|
+
var hideInternal2 = (obj, keys) => {
|
|
1894
|
+
for (const key of keys) {
|
|
1895
|
+
Object.defineProperty(obj, key, {
|
|
1896
|
+
value: obj[key],
|
|
1897
|
+
writable: false,
|
|
1898
|
+
configurable: false,
|
|
1899
|
+
enumerable: false
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
};
|
|
1588
1903
|
var DefaultBelongsToReference = class {
|
|
1589
1904
|
constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, targetKey) {
|
|
1590
1905
|
this.ctx = ctx;
|
|
@@ -1598,6 +1913,7 @@ var DefaultBelongsToReference = class {
|
|
|
1598
1913
|
this.targetKey = targetKey;
|
|
1599
1914
|
this.loaded = false;
|
|
1600
1915
|
this.current = null;
|
|
1916
|
+
hideInternal2(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
|
|
1601
1917
|
this.populateFromHydrationCache();
|
|
1602
1918
|
}
|
|
1603
1919
|
async load() {
|
|
@@ -1658,10 +1974,23 @@ var DefaultBelongsToReference = class {
|
|
|
1658
1974
|
this.current = this.createEntity(row);
|
|
1659
1975
|
this.loaded = true;
|
|
1660
1976
|
}
|
|
1977
|
+
toJSON() {
|
|
1978
|
+
return this.current;
|
|
1979
|
+
}
|
|
1661
1980
|
};
|
|
1662
1981
|
|
|
1663
1982
|
// src/orm/relations/many-to-many.ts
|
|
1664
1983
|
var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
1984
|
+
var hideInternal3 = (obj, keys) => {
|
|
1985
|
+
for (const key of keys) {
|
|
1986
|
+
Object.defineProperty(obj, key, {
|
|
1987
|
+
value: obj[key],
|
|
1988
|
+
writable: false,
|
|
1989
|
+
configurable: false,
|
|
1990
|
+
enumerable: false
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
};
|
|
1665
1994
|
var DefaultManyToManyCollection = class {
|
|
1666
1995
|
constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
|
|
1667
1996
|
this.ctx = ctx;
|
|
@@ -1675,6 +2004,7 @@ var DefaultManyToManyCollection = class {
|
|
|
1675
2004
|
this.localKey = localKey;
|
|
1676
2005
|
this.loaded = false;
|
|
1677
2006
|
this.items = [];
|
|
2007
|
+
hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
|
|
1678
2008
|
this.hydrateFromCache();
|
|
1679
2009
|
}
|
|
1680
2010
|
async load() {
|
|
@@ -1780,6 +2110,9 @@ var DefaultManyToManyCollection = class {
|
|
|
1780
2110
|
});
|
|
1781
2111
|
this.loaded = true;
|
|
1782
2112
|
}
|
|
2113
|
+
toJSON() {
|
|
2114
|
+
return this.items;
|
|
2115
|
+
}
|
|
1783
2116
|
};
|
|
1784
2117
|
|
|
1785
2118
|
// src/orm/lazy-batch.ts
|
|
@@ -2140,9 +2473,15 @@ var flattenResults = (results) => {
|
|
|
2140
2473
|
return rows;
|
|
2141
2474
|
};
|
|
2142
2475
|
async function executeHydrated(ctx, qb) {
|
|
2143
|
-
const
|
|
2476
|
+
const ast = qb.getAST();
|
|
2477
|
+
const compiled = ctx.dialect.compileSelect(ast);
|
|
2144
2478
|
const executed = await ctx.executor.executeSql(compiled.sql, compiled.params);
|
|
2145
2479
|
const rows = flattenResults(executed);
|
|
2480
|
+
if (ast.setOps && ast.setOps.length > 0) {
|
|
2481
|
+
return rows.map(
|
|
2482
|
+
(row) => createEntityProxy(ctx, qb.getTable(), row, qb.getLazyRelations())
|
|
2483
|
+
);
|
|
2484
|
+
}
|
|
2146
2485
|
const hydrated = hydrateRows(rows, qb.getHydrationPlan());
|
|
2147
2486
|
return hydrated.map(
|
|
2148
2487
|
(row) => createEntityFromRow(ctx, qb.getTable(), row, qb.getLazyRelations())
|
|
@@ -2189,6 +2528,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
2189
2528
|
const joinNode = createJoinNode(kind, table.name, condition);
|
|
2190
2529
|
return this.applyAst(context, (service) => service.withJoin(joinNode));
|
|
2191
2530
|
}
|
|
2531
|
+
applySetOperation(operator, query) {
|
|
2532
|
+
const subAst = this.resolveQueryNode(query);
|
|
2533
|
+
return this.applyAst(this.context, (service) => service.withSetOperation(operator, subAst));
|
|
2534
|
+
}
|
|
2192
2535
|
/**
|
|
2193
2536
|
* Selects specific columns for the query
|
|
2194
2537
|
* @param columns - Record of column definitions, function nodes, case expressions, or window functions
|
|
@@ -2377,6 +2720,38 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
2377
2720
|
const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
|
|
2378
2721
|
return this.clone(nextContext);
|
|
2379
2722
|
}
|
|
2723
|
+
/**
|
|
2724
|
+
* Combines this query with another using UNION
|
|
2725
|
+
* @param query - Query to union with
|
|
2726
|
+
* @returns New query builder instance with the set operation
|
|
2727
|
+
*/
|
|
2728
|
+
union(query) {
|
|
2729
|
+
return this.clone(this.applySetOperation("UNION", query));
|
|
2730
|
+
}
|
|
2731
|
+
/**
|
|
2732
|
+
* Combines this query with another using UNION ALL
|
|
2733
|
+
* @param query - Query to union with
|
|
2734
|
+
* @returns New query builder instance with the set operation
|
|
2735
|
+
*/
|
|
2736
|
+
unionAll(query) {
|
|
2737
|
+
return this.clone(this.applySetOperation("UNION ALL", query));
|
|
2738
|
+
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Combines this query with another using INTERSECT
|
|
2741
|
+
* @param query - Query to intersect with
|
|
2742
|
+
* @returns New query builder instance with the set operation
|
|
2743
|
+
*/
|
|
2744
|
+
intersect(query) {
|
|
2745
|
+
return this.clone(this.applySetOperation("INTERSECT", query));
|
|
2746
|
+
}
|
|
2747
|
+
/**
|
|
2748
|
+
* Combines this query with another using EXCEPT
|
|
2749
|
+
* @param query - Query to subtract
|
|
2750
|
+
* @returns New query builder instance with the set operation
|
|
2751
|
+
*/
|
|
2752
|
+
except(query) {
|
|
2753
|
+
return this.clone(this.applySetOperation("EXCEPT", query));
|
|
2754
|
+
}
|
|
2380
2755
|
/**
|
|
2381
2756
|
* Adds a WHERE EXISTS condition to the query
|
|
2382
2757
|
* @param subquery - Subquery to check for existence
|
|
@@ -2666,7 +3041,8 @@ var Dialect = class {
|
|
|
2666
3041
|
*/
|
|
2667
3042
|
compileSelect(ast) {
|
|
2668
3043
|
const ctx = this.createCompilerContext();
|
|
2669
|
-
const
|
|
3044
|
+
const normalized = this.normalizeSelectAst(ast);
|
|
3045
|
+
const rawSql = this.compileSelectAst(normalized, ctx).trim();
|
|
2670
3046
|
const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
|
|
2671
3047
|
return {
|
|
2672
3048
|
sql,
|
|
@@ -2700,6 +3076,9 @@ var Dialect = class {
|
|
|
2700
3076
|
params: [...ctx.params]
|
|
2701
3077
|
};
|
|
2702
3078
|
}
|
|
3079
|
+
supportsReturning() {
|
|
3080
|
+
return false;
|
|
3081
|
+
}
|
|
2703
3082
|
/**
|
|
2704
3083
|
* Compiles a WHERE clause
|
|
2705
3084
|
* @param where - WHERE expression
|
|
@@ -2724,7 +3103,11 @@ var Dialect = class {
|
|
|
2724
3103
|
* @returns SQL for EXISTS subquery
|
|
2725
3104
|
*/
|
|
2726
3105
|
compileSelectForExists(ast, ctx) {
|
|
2727
|
-
const
|
|
3106
|
+
const normalized = this.normalizeSelectAst(ast);
|
|
3107
|
+
const full = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, "");
|
|
3108
|
+
if (normalized.setOps && normalized.setOps.length > 0) {
|
|
3109
|
+
return `SELECT 1 FROM (${full}) AS _exists`;
|
|
3110
|
+
}
|
|
2728
3111
|
const upper = full.toUpperCase();
|
|
2729
3112
|
const fromIndex = upper.indexOf(" FROM ");
|
|
2730
3113
|
if (fromIndex === -1) {
|
|
@@ -2757,6 +3140,65 @@ var Dialect = class {
|
|
|
2757
3140
|
formatPlaceholder(index) {
|
|
2758
3141
|
return "?";
|
|
2759
3142
|
}
|
|
3143
|
+
/**
|
|
3144
|
+
* Whether the current dialect supports a given set operation.
|
|
3145
|
+
* Override in concrete dialects to restrict support.
|
|
3146
|
+
*/
|
|
3147
|
+
supportsSetOperation(kind) {
|
|
3148
|
+
return true;
|
|
3149
|
+
}
|
|
3150
|
+
/**
|
|
3151
|
+
* Validates set-operation semantics:
|
|
3152
|
+
* - Ensures the dialect supports requested operators.
|
|
3153
|
+
* - Enforces that only the outermost compound query may have ORDER/LIMIT/OFFSET.
|
|
3154
|
+
* @param ast - Query to validate
|
|
3155
|
+
* @param isOutermost - Whether this node is the outermost compound query
|
|
3156
|
+
*/
|
|
3157
|
+
validateSetOperations(ast, isOutermost = true) {
|
|
3158
|
+
const hasSetOps = !!(ast.setOps && ast.setOps.length);
|
|
3159
|
+
if (!isOutermost && (ast.orderBy || ast.limit !== void 0 || ast.offset !== void 0)) {
|
|
3160
|
+
throw new Error("ORDER BY / LIMIT / OFFSET are only allowed on the outermost compound query.");
|
|
3161
|
+
}
|
|
3162
|
+
if (hasSetOps) {
|
|
3163
|
+
for (const op of ast.setOps) {
|
|
3164
|
+
if (!this.supportsSetOperation(op.operator)) {
|
|
3165
|
+
throw new Error(`Set operation ${op.operator} is not supported by this dialect.`);
|
|
3166
|
+
}
|
|
3167
|
+
this.validateSetOperations(op.query, false);
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
/**
|
|
3172
|
+
* Hoists CTEs from set-operation operands to the outermost query so WITH appears once.
|
|
3173
|
+
* @param ast - Query AST
|
|
3174
|
+
* @returns Normalized AST without inner CTEs and a list of hoisted CTEs
|
|
3175
|
+
*/
|
|
3176
|
+
hoistCtes(ast) {
|
|
3177
|
+
let hoisted = [];
|
|
3178
|
+
const normalizedSetOps = ast.setOps?.map((op) => {
|
|
3179
|
+
const { normalized: child, hoistedCtes: childHoisted } = this.hoistCtes(op.query);
|
|
3180
|
+
const childCtes = child.ctes ?? [];
|
|
3181
|
+
if (childCtes.length) {
|
|
3182
|
+
hoisted = hoisted.concat(childCtes);
|
|
3183
|
+
}
|
|
3184
|
+
hoisted = hoisted.concat(childHoisted);
|
|
3185
|
+
const queryWithoutCtes = childCtes.length ? { ...child, ctes: void 0 } : child;
|
|
3186
|
+
return { ...op, query: queryWithoutCtes };
|
|
3187
|
+
});
|
|
3188
|
+
const normalized = normalizedSetOps ? { ...ast, setOps: normalizedSetOps } : ast;
|
|
3189
|
+
return { normalized, hoistedCtes: hoisted };
|
|
3190
|
+
}
|
|
3191
|
+
/**
|
|
3192
|
+
* Normalizes a SELECT AST before compilation (validation + CTE hoisting).
|
|
3193
|
+
* @param ast - Query AST
|
|
3194
|
+
* @returns Normalized query AST
|
|
3195
|
+
*/
|
|
3196
|
+
normalizeSelectAst(ast) {
|
|
3197
|
+
this.validateSetOperations(ast, true);
|
|
3198
|
+
const { normalized, hoistedCtes } = this.hoistCtes(ast);
|
|
3199
|
+
const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
|
|
3200
|
+
return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
|
|
3201
|
+
}
|
|
2760
3202
|
constructor() {
|
|
2761
3203
|
this.expressionCompilers = /* @__PURE__ */ new Map();
|
|
2762
3204
|
this.operandCompilers = /* @__PURE__ */ new Map();
|
|
@@ -2899,102 +3341,181 @@ var Dialect = class {
|
|
|
2899
3341
|
}
|
|
2900
3342
|
};
|
|
2901
3343
|
|
|
2902
|
-
// src/core/dialect/
|
|
2903
|
-
var
|
|
3344
|
+
// src/core/dialect/base/sql-dialect.ts
|
|
3345
|
+
var SqlDialectBase = class extends Dialect {
|
|
2904
3346
|
/**
|
|
2905
|
-
*
|
|
3347
|
+
* Compiles SELECT query AST to SQL using common rules.
|
|
2906
3348
|
*/
|
|
2907
|
-
|
|
2908
|
-
|
|
3349
|
+
compileSelectAst(ast, ctx) {
|
|
3350
|
+
const hasSetOps = !!(ast.setOps && ast.setOps.length);
|
|
3351
|
+
const ctes = this.compileCtes(ast, ctx);
|
|
3352
|
+
const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
|
|
3353
|
+
const baseSelect = this.compileSelectCore(baseAst, ctx);
|
|
3354
|
+
if (!hasSetOps) {
|
|
3355
|
+
return `${ctes}${baseSelect}`;
|
|
3356
|
+
}
|
|
3357
|
+
const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
|
|
3358
|
+
const orderBy = this.compileOrderBy(ast);
|
|
3359
|
+
const pagination = this.compilePagination(ast, orderBy);
|
|
3360
|
+
const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
|
|
3361
|
+
return `${ctes}${combined}${orderBy}${pagination}`;
|
|
3362
|
+
}
|
|
3363
|
+
compileInsertAst(ast, ctx) {
|
|
3364
|
+
const table = this.compileTableName(ast.into);
|
|
3365
|
+
const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
|
|
3366
|
+
const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
3367
|
+
const returning = this.compileReturning(ast.returning, ctx);
|
|
3368
|
+
return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning}`;
|
|
2909
3369
|
}
|
|
2910
3370
|
/**
|
|
2911
|
-
*
|
|
2912
|
-
* @param id - Identifier to quote
|
|
2913
|
-
* @returns Quoted identifier
|
|
3371
|
+
* Compiles a single SELECT (no set operations, no CTE prefix).
|
|
2914
3372
|
*/
|
|
2915
|
-
|
|
2916
|
-
|
|
3373
|
+
compileSelectCore(ast, ctx) {
|
|
3374
|
+
const columns = this.compileSelectColumns(ast, ctx);
|
|
3375
|
+
const from = this.compileFrom(ast.from);
|
|
3376
|
+
const joins = this.compileJoins(ast, ctx);
|
|
3377
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3378
|
+
const groupBy = this.compileGroupBy(ast);
|
|
3379
|
+
const having = this.compileHaving(ast, ctx);
|
|
3380
|
+
const orderBy = this.compileOrderBy(ast);
|
|
3381
|
+
const pagination = this.compilePagination(ast, orderBy);
|
|
3382
|
+
return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
|
|
3383
|
+
}
|
|
3384
|
+
compileUpdateAst(ast, ctx) {
|
|
3385
|
+
const table = this.compileTableName(ast.table);
|
|
3386
|
+
const assignments = ast.set.map((assignment) => {
|
|
3387
|
+
const col2 = assignment.column;
|
|
3388
|
+
const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
|
|
3389
|
+
const value = this.compileOperand(assignment.value, ctx);
|
|
3390
|
+
return `${target} = ${value}`;
|
|
3391
|
+
}).join(", ");
|
|
3392
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3393
|
+
const returning = this.compileReturning(ast.returning, ctx);
|
|
3394
|
+
return `UPDATE ${table} SET ${assignments}${whereClause}${returning}`;
|
|
3395
|
+
}
|
|
3396
|
+
compileDeleteAst(ast, ctx) {
|
|
3397
|
+
const table = this.compileTableName(ast.from);
|
|
3398
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3399
|
+
const returning = this.compileReturning(ast.returning, ctx);
|
|
3400
|
+
return `DELETE FROM ${table}${whereClause}${returning}`;
|
|
2917
3401
|
}
|
|
2918
3402
|
/**
|
|
2919
|
-
*
|
|
2920
|
-
* @param node - JSON path node
|
|
2921
|
-
* @returns MySQL JSON path expression
|
|
3403
|
+
* Default RETURNING compilation: no support.
|
|
2922
3404
|
*/
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
3405
|
+
compileReturning(returning, _ctx) {
|
|
3406
|
+
if (!returning || returning.length === 0) return "";
|
|
3407
|
+
throw new Error("RETURNING is not supported by this dialect.");
|
|
3408
|
+
}
|
|
3409
|
+
formatReturningColumns(returning) {
|
|
3410
|
+
return returning.map((column) => {
|
|
3411
|
+
const tablePart = column.table ? `${this.quoteIdentifier(column.table)}.` : "";
|
|
3412
|
+
const aliasPart = column.alias ? ` AS ${this.quoteIdentifier(column.alias)}` : "";
|
|
3413
|
+
return `${tablePart}${this.quoteIdentifier(column.name)}${aliasPart}`;
|
|
3414
|
+
}).join(", ");
|
|
2926
3415
|
}
|
|
2927
3416
|
/**
|
|
2928
|
-
*
|
|
2929
|
-
* @param ast - Query AST
|
|
2930
|
-
* @param ctx - Compiler context
|
|
2931
|
-
* @returns MySQL SQL string
|
|
3417
|
+
* DISTINCT clause. Override for DISTINCT ON support.
|
|
2932
3418
|
*/
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
|
|
2940
|
-
} else if (c.type === "ScalarSubquery") {
|
|
2941
|
-
expr = this.compileOperand(c, ctx);
|
|
2942
|
-
} else if (c.type === "WindowFunction") {
|
|
2943
|
-
expr = this.compileOperand(c, ctx);
|
|
2944
|
-
}
|
|
3419
|
+
compileDistinct(ast) {
|
|
3420
|
+
return ast.distinct ? "DISTINCT " : "";
|
|
3421
|
+
}
|
|
3422
|
+
compileSelectColumns(ast, ctx) {
|
|
3423
|
+
return ast.columns.map((c) => {
|
|
3424
|
+
const expr = this.compileOperand(c, ctx);
|
|
2945
3425
|
if (c.alias) {
|
|
2946
3426
|
if (c.alias.includes("(")) return c.alias;
|
|
2947
3427
|
return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
|
|
2948
3428
|
}
|
|
2949
3429
|
return expr;
|
|
2950
3430
|
}).join(", ");
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
const
|
|
2954
|
-
|
|
3431
|
+
}
|
|
3432
|
+
compileFrom(ast) {
|
|
3433
|
+
const base = this.compileTableName(ast);
|
|
3434
|
+
return ast.alias ? `${base} AS ${this.quoteIdentifier(ast.alias)}` : base;
|
|
3435
|
+
}
|
|
3436
|
+
compileTableName(table) {
|
|
3437
|
+
if (table.schema) {
|
|
3438
|
+
return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
|
|
3439
|
+
}
|
|
3440
|
+
return this.quoteIdentifier(table.name);
|
|
3441
|
+
}
|
|
3442
|
+
compileJoins(ast, ctx) {
|
|
3443
|
+
if (!ast.joins || ast.joins.length === 0) return "";
|
|
3444
|
+
const parts = ast.joins.map((j) => {
|
|
3445
|
+
const table = this.compileFrom(j.table);
|
|
2955
3446
|
const cond = this.compileExpression(j.condition, ctx);
|
|
2956
3447
|
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
2957
|
-
})
|
|
2958
|
-
|
|
2959
|
-
const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
|
|
2960
|
-
const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
|
|
2961
|
-
const orderBy = ast.orderBy && ast.orderBy.length > 0 ? " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ") : "";
|
|
2962
|
-
const limit = ast.limit ? ` LIMIT ${ast.limit}` : "";
|
|
2963
|
-
const offset = ast.offset ? ` OFFSET ${ast.offset}` : "";
|
|
2964
|
-
const ctes = ast.ctes && ast.ctes.length > 0 ? (() => {
|
|
2965
|
-
const hasRecursive = ast.ctes.some((cte) => cte.recursive);
|
|
2966
|
-
const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
|
|
2967
|
-
const cteDefs = ast.ctes.map((cte) => {
|
|
2968
|
-
const name = this.quoteIdentifier(cte.name);
|
|
2969
|
-
const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
2970
|
-
const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, "");
|
|
2971
|
-
return `${name}${cols} AS (${query})`;
|
|
2972
|
-
}).join(", ");
|
|
2973
|
-
return prefix + cteDefs + " ";
|
|
2974
|
-
})() : "";
|
|
2975
|
-
return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
|
|
3448
|
+
});
|
|
3449
|
+
return ` ${parts.join(" ")}`;
|
|
2976
3450
|
}
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
const
|
|
2980
|
-
|
|
2981
|
-
return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
|
|
3451
|
+
compileGroupBy(ast) {
|
|
3452
|
+
if (!ast.groupBy || ast.groupBy.length === 0) return "";
|
|
3453
|
+
const cols = ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ");
|
|
3454
|
+
return ` GROUP BY ${cols}`;
|
|
2982
3455
|
}
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3456
|
+
compileHaving(ast, ctx) {
|
|
3457
|
+
if (!ast.having) return "";
|
|
3458
|
+
return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
|
|
3459
|
+
}
|
|
3460
|
+
compileOrderBy(ast) {
|
|
3461
|
+
if (!ast.orderBy || ast.orderBy.length === 0) return "";
|
|
3462
|
+
const parts = ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
|
|
3463
|
+
return ` ORDER BY ${parts}`;
|
|
3464
|
+
}
|
|
3465
|
+
/**
|
|
3466
|
+
* Default LIMIT/OFFSET pagination clause.
|
|
3467
|
+
*/
|
|
3468
|
+
compilePagination(ast, _orderByClause) {
|
|
3469
|
+
const parts = [];
|
|
3470
|
+
if (ast.limit !== void 0) parts.push(`LIMIT ${ast.limit}`);
|
|
3471
|
+
if (ast.offset !== void 0) parts.push(`OFFSET ${ast.offset}`);
|
|
3472
|
+
return parts.length ? ` ${parts.join(" ")}` : "";
|
|
3473
|
+
}
|
|
3474
|
+
compileCtes(ast, ctx) {
|
|
3475
|
+
if (!ast.ctes || ast.ctes.length === 0) return "";
|
|
3476
|
+
const hasRecursive = ast.ctes.some((cte) => cte.recursive);
|
|
3477
|
+
const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
|
|
3478
|
+
const cteDefs = ast.ctes.map((cte) => {
|
|
3479
|
+
const name = this.quoteIdentifier(cte.name);
|
|
3480
|
+
const cols = cte.columns && cte.columns.length ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
3481
|
+
const query = this.stripTrailingSemicolon(this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx));
|
|
3482
|
+
return `${name}${cols} AS (${query})`;
|
|
2990
3483
|
}).join(", ");
|
|
2991
|
-
|
|
2992
|
-
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
3484
|
+
return `${prefix}${cteDefs} `;
|
|
2993
3485
|
}
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
3486
|
+
stripTrailingSemicolon(sql) {
|
|
3487
|
+
return sql.trim().replace(/;$/, "");
|
|
3488
|
+
}
|
|
3489
|
+
wrapSetOperand(sql) {
|
|
3490
|
+
const trimmed = this.stripTrailingSemicolon(sql);
|
|
3491
|
+
return `(${trimmed})`;
|
|
3492
|
+
}
|
|
3493
|
+
};
|
|
3494
|
+
|
|
3495
|
+
// src/core/dialect/mysql/index.ts
|
|
3496
|
+
var MySqlDialect = class extends SqlDialectBase {
|
|
3497
|
+
/**
|
|
3498
|
+
* Creates a new MySqlDialect instance
|
|
3499
|
+
*/
|
|
3500
|
+
constructor() {
|
|
3501
|
+
super();
|
|
3502
|
+
}
|
|
3503
|
+
/**
|
|
3504
|
+
* Quotes an identifier using MySQL backtick syntax
|
|
3505
|
+
* @param id - Identifier to quote
|
|
3506
|
+
* @returns Quoted identifier
|
|
3507
|
+
*/
|
|
3508
|
+
quoteIdentifier(id) {
|
|
3509
|
+
return `\`${id}\``;
|
|
3510
|
+
}
|
|
3511
|
+
/**
|
|
3512
|
+
* Compiles JSON path expression using MySQL syntax
|
|
3513
|
+
* @param node - JSON path node
|
|
3514
|
+
* @returns MySQL JSON path expression
|
|
3515
|
+
*/
|
|
3516
|
+
compileJsonPath(node) {
|
|
3517
|
+
const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
|
|
3518
|
+
return `${col2}->'${node.path}'`;
|
|
2998
3519
|
}
|
|
2999
3520
|
};
|
|
3000
3521
|
|
|
@@ -3038,6 +3559,43 @@ var SqlServerDialect = class extends Dialect {
|
|
|
3038
3559
|
* @returns SQL Server SQL string
|
|
3039
3560
|
*/
|
|
3040
3561
|
compileSelectAst(ast, ctx) {
|
|
3562
|
+
const hasSetOps = !!(ast.setOps && ast.setOps.length);
|
|
3563
|
+
const ctes = this.compileCtes(ast, ctx);
|
|
3564
|
+
const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
|
|
3565
|
+
const baseSelect = this.compileSelectCore(baseAst, ctx);
|
|
3566
|
+
if (!hasSetOps) {
|
|
3567
|
+
return `${ctes}${baseSelect}`;
|
|
3568
|
+
}
|
|
3569
|
+
const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
|
|
3570
|
+
const orderBy = this.compileOrderBy(ast);
|
|
3571
|
+
const pagination = this.compilePagination(ast, orderBy);
|
|
3572
|
+
const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
|
|
3573
|
+
const tail = pagination || orderBy;
|
|
3574
|
+
return `${ctes}${combined}${tail}`;
|
|
3575
|
+
}
|
|
3576
|
+
compileInsertAst(ast, ctx) {
|
|
3577
|
+
const table = this.quoteIdentifier(ast.into.name);
|
|
3578
|
+
const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
|
|
3579
|
+
const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
3580
|
+
return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
|
|
3581
|
+
}
|
|
3582
|
+
compileUpdateAst(ast, ctx) {
|
|
3583
|
+
const table = this.quoteIdentifier(ast.table.name);
|
|
3584
|
+
const assignments = ast.set.map((assignment) => {
|
|
3585
|
+
const col2 = assignment.column;
|
|
3586
|
+
const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
|
|
3587
|
+
const value = this.compileOperand(assignment.value, ctx);
|
|
3588
|
+
return `${target} = ${value}`;
|
|
3589
|
+
}).join(", ");
|
|
3590
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3591
|
+
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
3592
|
+
}
|
|
3593
|
+
compileDeleteAst(ast, ctx) {
|
|
3594
|
+
const table = this.quoteIdentifier(ast.from.name);
|
|
3595
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3596
|
+
return `DELETE FROM ${table}${whereClause};`;
|
|
3597
|
+
}
|
|
3598
|
+
compileSelectCore(ast, ctx) {
|
|
3041
3599
|
const columns = ast.columns.map((c) => {
|
|
3042
3600
|
let expr = "";
|
|
3043
3601
|
if (c.type === "Function") {
|
|
@@ -3065,51 +3623,47 @@ var SqlServerDialect = class extends Dialect {
|
|
|
3065
3623
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3066
3624
|
const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
|
|
3067
3625
|
const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
|
|
3068
|
-
const orderBy =
|
|
3069
|
-
|
|
3070
|
-
if (
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3626
|
+
const orderBy = this.compileOrderBy(ast);
|
|
3627
|
+
const pagination = this.compilePagination(ast, orderBy);
|
|
3628
|
+
if (pagination) {
|
|
3629
|
+
return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${pagination}`;
|
|
3630
|
+
}
|
|
3631
|
+
return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}`;
|
|
3632
|
+
}
|
|
3633
|
+
compileOrderBy(ast) {
|
|
3634
|
+
if (!ast.orderBy || ast.orderBy.length === 0) return "";
|
|
3635
|
+
return " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ");
|
|
3636
|
+
}
|
|
3637
|
+
compilePagination(ast, orderBy) {
|
|
3638
|
+
const hasLimit = ast.limit !== void 0;
|
|
3639
|
+
const hasOffset = ast.offset !== void 0;
|
|
3640
|
+
if (!hasLimit && !hasOffset) return "";
|
|
3641
|
+
const off = ast.offset ?? 0;
|
|
3642
|
+
const orderClause = orderBy || " ORDER BY (SELECT NULL)";
|
|
3643
|
+
let pagination = `${orderClause} OFFSET ${off} ROWS`;
|
|
3644
|
+
if (hasLimit) {
|
|
3645
|
+
pagination += ` FETCH NEXT ${ast.limit} ROWS ONLY`;
|
|
3078
3646
|
}
|
|
3079
|
-
|
|
3647
|
+
return pagination;
|
|
3648
|
+
}
|
|
3649
|
+
compileCtes(ast, ctx) {
|
|
3650
|
+
if (!ast.ctes || ast.ctes.length === 0) return "";
|
|
3651
|
+
const defs = ast.ctes.map((cte) => {
|
|
3080
3652
|
const name = this.quoteIdentifier(cte.name);
|
|
3081
3653
|
const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
3082
|
-
const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, "");
|
|
3654
|
+
const query = this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx).trim().replace(/;$/, "");
|
|
3083
3655
|
return `${name}${cols} AS (${query})`;
|
|
3084
|
-
}).join(", ") + " " : "";
|
|
3085
|
-
return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy};`;
|
|
3086
|
-
}
|
|
3087
|
-
compileInsertAst(ast, ctx) {
|
|
3088
|
-
const table = this.quoteIdentifier(ast.into.name);
|
|
3089
|
-
const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
|
|
3090
|
-
const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
3091
|
-
return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
|
|
3092
|
-
}
|
|
3093
|
-
compileUpdateAst(ast, ctx) {
|
|
3094
|
-
const table = this.quoteIdentifier(ast.table.name);
|
|
3095
|
-
const assignments = ast.set.map((assignment) => {
|
|
3096
|
-
const col2 = assignment.column;
|
|
3097
|
-
const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
|
|
3098
|
-
const value = this.compileOperand(assignment.value, ctx);
|
|
3099
|
-
return `${target} = ${value}`;
|
|
3100
3656
|
}).join(", ");
|
|
3101
|
-
|
|
3102
|
-
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
3657
|
+
return `WITH ${defs} `;
|
|
3103
3658
|
}
|
|
3104
|
-
|
|
3105
|
-
const
|
|
3106
|
-
|
|
3107
|
-
return `DELETE FROM ${table}${whereClause};`;
|
|
3659
|
+
wrapSetOperand(sql) {
|
|
3660
|
+
const trimmed = sql.trim().replace(/;$/, "");
|
|
3661
|
+
return `(${trimmed})`;
|
|
3108
3662
|
}
|
|
3109
3663
|
};
|
|
3110
3664
|
|
|
3111
3665
|
// src/core/dialect/sqlite/index.ts
|
|
3112
|
-
var SqliteDialect = class extends
|
|
3666
|
+
var SqliteDialect = class extends SqlDialectBase {
|
|
3113
3667
|
/**
|
|
3114
3668
|
* Creates a new SqliteDialect instance
|
|
3115
3669
|
*/
|
|
@@ -3133,91 +3687,1143 @@ var SqliteDialect = class extends Dialect {
|
|
|
3133
3687
|
const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
|
|
3134
3688
|
return `json_extract(${col2}, '${node.path}')`;
|
|
3135
3689
|
}
|
|
3690
|
+
compileReturning(returning, ctx) {
|
|
3691
|
+
if (!returning || returning.length === 0) return "";
|
|
3692
|
+
const columns = this.formatReturningColumns(returning);
|
|
3693
|
+
return ` RETURNING ${columns}`;
|
|
3694
|
+
}
|
|
3695
|
+
supportsReturning() {
|
|
3696
|
+
return true;
|
|
3697
|
+
}
|
|
3698
|
+
};
|
|
3699
|
+
|
|
3700
|
+
// src/core/dialect/postgres/index.ts
|
|
3701
|
+
var PostgresDialect = class extends SqlDialectBase {
|
|
3136
3702
|
/**
|
|
3137
|
-
*
|
|
3138
|
-
* @param ast - Query AST
|
|
3139
|
-
* @param ctx - Compiler context
|
|
3140
|
-
* @returns SQLite SQL string
|
|
3703
|
+
* Creates a new PostgresDialect instance
|
|
3141
3704
|
*/
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
let expr = "";
|
|
3145
|
-
if (c.type === "Function") {
|
|
3146
|
-
expr = this.compileOperand(c, ctx);
|
|
3147
|
-
} else if (c.type === "Column") {
|
|
3148
|
-
expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
|
|
3149
|
-
} else if (c.type === "ScalarSubquery") {
|
|
3150
|
-
expr = this.compileOperand(c, ctx);
|
|
3151
|
-
} else if (c.type === "CaseExpression") {
|
|
3152
|
-
expr = this.compileOperand(c, ctx);
|
|
3153
|
-
} else if (c.type === "WindowFunction") {
|
|
3154
|
-
expr = this.compileOperand(c, ctx);
|
|
3155
|
-
}
|
|
3156
|
-
if (c.alias) {
|
|
3157
|
-
if (c.alias.includes("(")) return c.alias;
|
|
3158
|
-
return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
|
|
3159
|
-
}
|
|
3160
|
-
return expr;
|
|
3161
|
-
}).join(", ");
|
|
3162
|
-
const distinct = ast.distinct ? "DISTINCT " : "";
|
|
3163
|
-
const from = `${this.quoteIdentifier(ast.from.name)}`;
|
|
3164
|
-
const joins = ast.joins.map((j) => {
|
|
3165
|
-
const table = this.quoteIdentifier(j.table.name);
|
|
3166
|
-
const cond = this.compileExpression(j.condition, ctx);
|
|
3167
|
-
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
3168
|
-
}).join(" ");
|
|
3169
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3170
|
-
const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((c) => `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`).join(", ") : "";
|
|
3171
|
-
const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
|
|
3172
|
-
const orderBy = ast.orderBy && ast.orderBy.length > 0 ? " ORDER BY " + ast.orderBy.map((o) => `${this.quoteIdentifier(o.column.table)}.${this.quoteIdentifier(o.column.name)} ${o.direction}`).join(", ") : "";
|
|
3173
|
-
const limit = ast.limit ? ` LIMIT ${ast.limit}` : "";
|
|
3174
|
-
const offset = ast.offset ? ` OFFSET ${ast.offset}` : "";
|
|
3175
|
-
const ctes = ast.ctes && ast.ctes.length > 0 ? (() => {
|
|
3176
|
-
const hasRecursive = ast.ctes.some((cte) => cte.recursive);
|
|
3177
|
-
const prefix = hasRecursive ? "WITH RECURSIVE " : "WITH ";
|
|
3178
|
-
const cteDefs = ast.ctes.map((cte) => {
|
|
3179
|
-
const name = this.quoteIdentifier(cte.name);
|
|
3180
|
-
const cols = cte.columns ? `(${cte.columns.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
3181
|
-
const query = this.compileSelectAst(cte.query, ctx).trim().replace(/;$/, "");
|
|
3182
|
-
return `${name}${cols} AS (${query})`;
|
|
3183
|
-
}).join(", ");
|
|
3184
|
-
return prefix + cteDefs + " ";
|
|
3185
|
-
})() : "";
|
|
3186
|
-
return `${ctes}SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}${limit}${offset};`;
|
|
3187
|
-
}
|
|
3188
|
-
compileInsertAst(ast, ctx) {
|
|
3189
|
-
const table = this.quoteIdentifier(ast.into.name);
|
|
3190
|
-
const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
|
|
3191
|
-
const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
3192
|
-
const returning = this.compileReturning(ast.returning, ctx);
|
|
3193
|
-
return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning};`;
|
|
3705
|
+
constructor() {
|
|
3706
|
+
super();
|
|
3194
3707
|
}
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
}).join(", ");
|
|
3203
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
3204
|
-
const returning = this.compileReturning(ast.returning, ctx);
|
|
3205
|
-
return `UPDATE ${table} SET ${assignments}${whereClause}${returning};`;
|
|
3708
|
+
/**
|
|
3709
|
+
* Quotes an identifier using PostgreSQL double-quote syntax
|
|
3710
|
+
* @param id - Identifier to quote
|
|
3711
|
+
* @returns Quoted identifier
|
|
3712
|
+
*/
|
|
3713
|
+
quoteIdentifier(id) {
|
|
3714
|
+
return `"${id}"`;
|
|
3206
3715
|
}
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3716
|
+
/**
|
|
3717
|
+
* Compiles JSON path expression using PostgreSQL syntax
|
|
3718
|
+
* @param node - JSON path node
|
|
3719
|
+
* @returns PostgreSQL JSON path expression
|
|
3720
|
+
*/
|
|
3721
|
+
compileJsonPath(node) {
|
|
3722
|
+
const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
|
|
3723
|
+
return `${col2}->>'${node.path}'`;
|
|
3212
3724
|
}
|
|
3213
3725
|
compileReturning(returning, ctx) {
|
|
3214
3726
|
if (!returning || returning.length === 0) return "";
|
|
3215
|
-
const columns =
|
|
3216
|
-
const tablePart = column.table ? `${this.quoteIdentifier(column.table)}.` : "";
|
|
3217
|
-
return `${tablePart}${this.quoteIdentifier(column.name)}`;
|
|
3218
|
-
}).join(", ");
|
|
3727
|
+
const columns = this.formatReturningColumns(returning);
|
|
3219
3728
|
return ` RETURNING ${columns}`;
|
|
3220
3729
|
}
|
|
3730
|
+
supportsReturning() {
|
|
3731
|
+
return true;
|
|
3732
|
+
}
|
|
3733
|
+
};
|
|
3734
|
+
|
|
3735
|
+
// src/core/ddl/dialects/base-schema-dialect.ts
|
|
3736
|
+
var BaseSchemaDialect = class {
|
|
3737
|
+
supportsPartialIndexes() {
|
|
3738
|
+
return false;
|
|
3739
|
+
}
|
|
3740
|
+
formatTableName(table) {
|
|
3741
|
+
if (table.schema) {
|
|
3742
|
+
return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
|
|
3743
|
+
}
|
|
3744
|
+
return this.quoteIdentifier(table.name);
|
|
3745
|
+
}
|
|
3746
|
+
renderDefault(value, _column) {
|
|
3747
|
+
return formatLiteral(value, this.name);
|
|
3748
|
+
}
|
|
3749
|
+
renderReference(ref, _table) {
|
|
3750
|
+
const parts = ["REFERENCES", quoteQualified(this, ref.table), `(${this.quoteIdentifier(ref.column)})`];
|
|
3751
|
+
if (ref.onDelete) parts.push("ON DELETE", ref.onDelete);
|
|
3752
|
+
if (ref.onUpdate) parts.push("ON UPDATE", ref.onUpdate);
|
|
3753
|
+
if (ref.deferrable && this.name === "postgres") parts.push("DEFERRABLE INITIALLY DEFERRED");
|
|
3754
|
+
return parts.join(" ");
|
|
3755
|
+
}
|
|
3756
|
+
renderTableOptions(_table) {
|
|
3757
|
+
return void 0;
|
|
3758
|
+
}
|
|
3759
|
+
dropTableSql(table) {
|
|
3760
|
+
return [`DROP TABLE IF EXISTS ${this.formatTableName(table)};`];
|
|
3761
|
+
}
|
|
3762
|
+
warnDropColumn(_table, _column) {
|
|
3763
|
+
return void 0;
|
|
3764
|
+
}
|
|
3765
|
+
};
|
|
3766
|
+
|
|
3767
|
+
// src/core/ddl/dialects/postgres-schema-dialect.ts
|
|
3768
|
+
var PostgresSchemaDialect = class extends BaseSchemaDialect {
|
|
3769
|
+
constructor() {
|
|
3770
|
+
super(...arguments);
|
|
3771
|
+
this.name = "postgres";
|
|
3772
|
+
}
|
|
3773
|
+
quoteIdentifier(id) {
|
|
3774
|
+
return `"${id}"`;
|
|
3775
|
+
}
|
|
3776
|
+
renderColumnType(column) {
|
|
3777
|
+
switch (column.type) {
|
|
3778
|
+
case "INT":
|
|
3779
|
+
case "INTEGER":
|
|
3780
|
+
case "int":
|
|
3781
|
+
case "integer":
|
|
3782
|
+
return "integer";
|
|
3783
|
+
case "BIGINT":
|
|
3784
|
+
case "bigint":
|
|
3785
|
+
return "bigint";
|
|
3786
|
+
case "UUID":
|
|
3787
|
+
case "uuid":
|
|
3788
|
+
return "uuid";
|
|
3789
|
+
case "BOOLEAN":
|
|
3790
|
+
case "boolean":
|
|
3791
|
+
return "boolean";
|
|
3792
|
+
case "JSON":
|
|
3793
|
+
case "json":
|
|
3794
|
+
return "jsonb";
|
|
3795
|
+
case "DECIMAL":
|
|
3796
|
+
case "decimal":
|
|
3797
|
+
return column.args?.length ? `numeric(${column.args[0]}, ${column.args[1] ?? 0})` : "numeric";
|
|
3798
|
+
case "FLOAT":
|
|
3799
|
+
case "float":
|
|
3800
|
+
case "DOUBLE":
|
|
3801
|
+
case "double":
|
|
3802
|
+
return "double precision";
|
|
3803
|
+
case "TIMESTAMPTZ":
|
|
3804
|
+
case "timestamptz":
|
|
3805
|
+
return "timestamptz";
|
|
3806
|
+
case "TIMESTAMP":
|
|
3807
|
+
case "timestamp":
|
|
3808
|
+
return "timestamp";
|
|
3809
|
+
case "DATE":
|
|
3810
|
+
case "date":
|
|
3811
|
+
return "date";
|
|
3812
|
+
case "DATETIME":
|
|
3813
|
+
case "datetime":
|
|
3814
|
+
return "timestamp";
|
|
3815
|
+
case "VARCHAR":
|
|
3816
|
+
case "varchar":
|
|
3817
|
+
return column.args?.length ? `varchar(${column.args[0]})` : "varchar";
|
|
3818
|
+
case "TEXT":
|
|
3819
|
+
case "text":
|
|
3820
|
+
return "text";
|
|
3821
|
+
case "ENUM":
|
|
3822
|
+
case "enum":
|
|
3823
|
+
return "text";
|
|
3824
|
+
default:
|
|
3825
|
+
return String(column.type).toLowerCase();
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
renderAutoIncrement(column) {
|
|
3829
|
+
if (!column.autoIncrement) return void 0;
|
|
3830
|
+
const strategy = column.generated === "always" ? "GENERATED ALWAYS" : "GENERATED BY DEFAULT";
|
|
3831
|
+
return `${strategy} AS IDENTITY`;
|
|
3832
|
+
}
|
|
3833
|
+
renderIndex(table, index) {
|
|
3834
|
+
const name = index.name || deriveIndexName(table, index);
|
|
3835
|
+
const cols = renderIndexColumns(this, index.columns);
|
|
3836
|
+
const unique = index.unique ? "UNIQUE " : "";
|
|
3837
|
+
const where = index.where ? ` WHERE ${index.where}` : "";
|
|
3838
|
+
return `CREATE ${unique}INDEX IF NOT EXISTS ${this.quoteIdentifier(name)} ON ${this.formatTableName(table)} (${cols})${where};`;
|
|
3839
|
+
}
|
|
3840
|
+
supportsPartialIndexes() {
|
|
3841
|
+
return true;
|
|
3842
|
+
}
|
|
3843
|
+
dropColumnSql(table, column) {
|
|
3844
|
+
return [`ALTER TABLE ${this.formatTableName(table)} DROP COLUMN ${this.quoteIdentifier(column)};`];
|
|
3845
|
+
}
|
|
3846
|
+
dropIndexSql(table, index) {
|
|
3847
|
+
const qualified = table.schema ? `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(index)}` : this.quoteIdentifier(index);
|
|
3848
|
+
return [`DROP INDEX IF EXISTS ${qualified};`];
|
|
3849
|
+
}
|
|
3850
|
+
};
|
|
3851
|
+
|
|
3852
|
+
// src/core/ddl/dialects/mysql-schema-dialect.ts
|
|
3853
|
+
var MySqlSchemaDialect = class extends BaseSchemaDialect {
|
|
3854
|
+
constructor() {
|
|
3855
|
+
super(...arguments);
|
|
3856
|
+
this.name = "mysql";
|
|
3857
|
+
}
|
|
3858
|
+
quoteIdentifier(id) {
|
|
3859
|
+
return `\`${id}\``;
|
|
3860
|
+
}
|
|
3861
|
+
renderColumnType(column) {
|
|
3862
|
+
switch (column.type) {
|
|
3863
|
+
case "INT":
|
|
3864
|
+
case "INTEGER":
|
|
3865
|
+
case "int":
|
|
3866
|
+
case "integer":
|
|
3867
|
+
return "INT";
|
|
3868
|
+
case "BIGINT":
|
|
3869
|
+
case "bigint":
|
|
3870
|
+
return "BIGINT";
|
|
3871
|
+
case "UUID":
|
|
3872
|
+
case "uuid":
|
|
3873
|
+
return "CHAR(36)";
|
|
3874
|
+
case "BOOLEAN":
|
|
3875
|
+
case "boolean":
|
|
3876
|
+
return "TINYINT(1)";
|
|
3877
|
+
case "JSON":
|
|
3878
|
+
case "json":
|
|
3879
|
+
return "JSON";
|
|
3880
|
+
case "DECIMAL":
|
|
3881
|
+
case "decimal":
|
|
3882
|
+
return column.args?.length ? `DECIMAL(${column.args[0]},${column.args[1] ?? 0})` : "DECIMAL";
|
|
3883
|
+
case "FLOAT":
|
|
3884
|
+
case "float":
|
|
3885
|
+
return column.args?.length ? `FLOAT(${column.args[0]})` : "FLOAT";
|
|
3886
|
+
case "DOUBLE":
|
|
3887
|
+
case "double":
|
|
3888
|
+
return "DOUBLE";
|
|
3889
|
+
case "TIMESTAMPTZ":
|
|
3890
|
+
case "timestamptz":
|
|
3891
|
+
return "TIMESTAMP";
|
|
3892
|
+
case "TIMESTAMP":
|
|
3893
|
+
case "timestamp":
|
|
3894
|
+
return "TIMESTAMP";
|
|
3895
|
+
case "DATETIME":
|
|
3896
|
+
case "datetime":
|
|
3897
|
+
return "DATETIME";
|
|
3898
|
+
case "DATE":
|
|
3899
|
+
case "date":
|
|
3900
|
+
return "DATE";
|
|
3901
|
+
case "VARCHAR":
|
|
3902
|
+
case "varchar":
|
|
3903
|
+
return column.args?.length ? `VARCHAR(${column.args[0]})` : "VARCHAR(255)";
|
|
3904
|
+
case "TEXT":
|
|
3905
|
+
case "text":
|
|
3906
|
+
return "TEXT";
|
|
3907
|
+
case "ENUM":
|
|
3908
|
+
case "enum":
|
|
3909
|
+
return column.args && Array.isArray(column.args) && column.args.length ? `ENUM(${column.args.map((v) => `'${escapeLiteral(v)}'`).join(",")})` : "ENUM";
|
|
3910
|
+
default:
|
|
3911
|
+
return String(column.type).toUpperCase();
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
renderDefault(value) {
|
|
3915
|
+
return formatLiteral(value, this.name);
|
|
3916
|
+
}
|
|
3917
|
+
renderAutoIncrement(column) {
|
|
3918
|
+
return column.autoIncrement ? "AUTO_INCREMENT" : void 0;
|
|
3919
|
+
}
|
|
3920
|
+
renderIndex(table, index) {
|
|
3921
|
+
if (index.where) {
|
|
3922
|
+
throw new Error("MySQL does not support partial/filtered indexes");
|
|
3923
|
+
}
|
|
3924
|
+
const name = index.name || deriveIndexName(table, index);
|
|
3925
|
+
const cols = renderIndexColumns(this, index.columns);
|
|
3926
|
+
const unique = index.unique ? "UNIQUE " : "";
|
|
3927
|
+
return `CREATE ${unique}INDEX ${this.quoteIdentifier(name)} ON ${this.formatTableName(table)} (${cols});`;
|
|
3928
|
+
}
|
|
3929
|
+
renderTableOptions(table) {
|
|
3930
|
+
const parts = [];
|
|
3931
|
+
if (table.engine) parts.push(`ENGINE=${table.engine}`);
|
|
3932
|
+
if (table.charset) parts.push(`DEFAULT CHARSET=${table.charset}`);
|
|
3933
|
+
if (table.collation) parts.push(`COLLATE=${table.collation}`);
|
|
3934
|
+
return parts.length ? parts.join(" ") : void 0;
|
|
3935
|
+
}
|
|
3936
|
+
dropColumnSql(table, column) {
|
|
3937
|
+
return [`ALTER TABLE ${this.formatTableName(table)} DROP COLUMN ${this.quoteIdentifier(column)};`];
|
|
3938
|
+
}
|
|
3939
|
+
dropIndexSql(table, index) {
|
|
3940
|
+
return [`DROP INDEX ${this.quoteIdentifier(index)} ON ${this.formatTableName(table)};`];
|
|
3941
|
+
}
|
|
3942
|
+
};
|
|
3943
|
+
|
|
3944
|
+
// src/core/ddl/dialects/sqlite-schema-dialect.ts
|
|
3945
|
+
var SQLiteSchemaDialect = class extends BaseSchemaDialect {
|
|
3946
|
+
constructor() {
|
|
3947
|
+
super(...arguments);
|
|
3948
|
+
this.name = "sqlite";
|
|
3949
|
+
}
|
|
3950
|
+
quoteIdentifier(id) {
|
|
3951
|
+
return `"${id}"`;
|
|
3952
|
+
}
|
|
3953
|
+
renderColumnType(column) {
|
|
3954
|
+
switch (column.type) {
|
|
3955
|
+
case "INT":
|
|
3956
|
+
case "INTEGER":
|
|
3957
|
+
case "int":
|
|
3958
|
+
case "integer":
|
|
3959
|
+
case "BIGINT":
|
|
3960
|
+
case "bigint":
|
|
3961
|
+
return "INTEGER";
|
|
3962
|
+
case "BOOLEAN":
|
|
3963
|
+
case "boolean":
|
|
3964
|
+
return "INTEGER";
|
|
3965
|
+
case "DECIMAL":
|
|
3966
|
+
case "decimal":
|
|
3967
|
+
case "FLOAT":
|
|
3968
|
+
case "float":
|
|
3969
|
+
case "DOUBLE":
|
|
3970
|
+
case "double":
|
|
3971
|
+
return "REAL";
|
|
3972
|
+
case "DATE":
|
|
3973
|
+
case "date":
|
|
3974
|
+
case "DATETIME":
|
|
3975
|
+
case "datetime":
|
|
3976
|
+
case "TIMESTAMP":
|
|
3977
|
+
case "timestamp":
|
|
3978
|
+
case "TIMESTAMPTZ":
|
|
3979
|
+
case "timestamptz":
|
|
3980
|
+
return "TEXT";
|
|
3981
|
+
case "VARCHAR":
|
|
3982
|
+
case "varchar":
|
|
3983
|
+
case "TEXT":
|
|
3984
|
+
case "text":
|
|
3985
|
+
case "JSON":
|
|
3986
|
+
case "json":
|
|
3987
|
+
case "UUID":
|
|
3988
|
+
case "uuid":
|
|
3989
|
+
return "TEXT";
|
|
3990
|
+
case "ENUM":
|
|
3991
|
+
case "enum":
|
|
3992
|
+
return "TEXT";
|
|
3993
|
+
default:
|
|
3994
|
+
return "TEXT";
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
renderAutoIncrement(column, table) {
|
|
3998
|
+
const pk = resolvePrimaryKey(table);
|
|
3999
|
+
if (column.autoIncrement && pk.length === 1 && pk[0] === column.name) {
|
|
4000
|
+
return "PRIMARY KEY AUTOINCREMENT";
|
|
4001
|
+
}
|
|
4002
|
+
return void 0;
|
|
4003
|
+
}
|
|
4004
|
+
preferInlinePkAutoincrement(column, table, pk) {
|
|
4005
|
+
return !!(column.autoIncrement && pk.length === 1 && pk[0] === column.name);
|
|
4006
|
+
}
|
|
4007
|
+
renderDefault(value) {
|
|
4008
|
+
return formatLiteral(value, this.name);
|
|
4009
|
+
}
|
|
4010
|
+
renderIndex(table, index) {
|
|
4011
|
+
if (index.where) {
|
|
4012
|
+
throw new Error("SQLite does not support partial/filtered indexes");
|
|
4013
|
+
}
|
|
4014
|
+
const name = index.name || deriveIndexName(table, index);
|
|
4015
|
+
const cols = renderIndexColumns(this, index.columns);
|
|
4016
|
+
const unique = index.unique ? "UNIQUE " : "";
|
|
4017
|
+
return `CREATE ${unique}INDEX IF NOT EXISTS ${this.quoteIdentifier(name)} ON ${this.formatTableName(table)} (${cols});`;
|
|
4018
|
+
}
|
|
4019
|
+
dropColumnSql(_table, _column) {
|
|
4020
|
+
return [];
|
|
4021
|
+
}
|
|
4022
|
+
dropIndexSql(_table, index) {
|
|
4023
|
+
return [`DROP INDEX IF EXISTS ${this.quoteIdentifier(index)};`];
|
|
4024
|
+
}
|
|
4025
|
+
warnDropColumn(table, column) {
|
|
4026
|
+
const key = table.schema ? `${table.schema}.${table.name}` : table.name;
|
|
4027
|
+
return `Dropping columns on SQLite requires table rebuild (column ${column} on ${key}).`;
|
|
4028
|
+
}
|
|
4029
|
+
};
|
|
4030
|
+
|
|
4031
|
+
// src/core/ddl/dialects/mssql-schema-dialect.ts
|
|
4032
|
+
var MSSqlSchemaDialect = class extends BaseSchemaDialect {
|
|
4033
|
+
constructor() {
|
|
4034
|
+
super(...arguments);
|
|
4035
|
+
this.name = "mssql";
|
|
4036
|
+
}
|
|
4037
|
+
quoteIdentifier(id) {
|
|
4038
|
+
return `[${id.replace(/]/g, "]]")}]`;
|
|
4039
|
+
}
|
|
4040
|
+
renderColumnType(column) {
|
|
4041
|
+
switch (column.type) {
|
|
4042
|
+
case "INT":
|
|
4043
|
+
case "INTEGER":
|
|
4044
|
+
case "int":
|
|
4045
|
+
case "integer":
|
|
4046
|
+
return "INT";
|
|
4047
|
+
case "BIGINT":
|
|
4048
|
+
case "bigint":
|
|
4049
|
+
return "BIGINT";
|
|
4050
|
+
case "UUID":
|
|
4051
|
+
case "uuid":
|
|
4052
|
+
return "UNIQUEIDENTIFIER";
|
|
4053
|
+
case "BOOLEAN":
|
|
4054
|
+
case "boolean":
|
|
4055
|
+
return "BIT";
|
|
4056
|
+
case "JSON":
|
|
4057
|
+
case "json":
|
|
4058
|
+
return "NVARCHAR(MAX)";
|
|
4059
|
+
case "DECIMAL":
|
|
4060
|
+
case "decimal":
|
|
4061
|
+
return column.args?.length ? `DECIMAL(${column.args[0]},${column.args[1] ?? 0})` : "DECIMAL(18,0)";
|
|
4062
|
+
case "FLOAT":
|
|
4063
|
+
case "float":
|
|
4064
|
+
case "DOUBLE":
|
|
4065
|
+
case "double":
|
|
4066
|
+
return "FLOAT";
|
|
4067
|
+
case "TIMESTAMPTZ":
|
|
4068
|
+
case "timestamptz":
|
|
4069
|
+
case "TIMESTAMP":
|
|
4070
|
+
case "timestamp":
|
|
4071
|
+
case "DATETIME":
|
|
4072
|
+
case "datetime":
|
|
4073
|
+
return "DATETIME2";
|
|
4074
|
+
case "DATE":
|
|
4075
|
+
case "date":
|
|
4076
|
+
return "DATE";
|
|
4077
|
+
case "VARCHAR":
|
|
4078
|
+
case "varchar":
|
|
4079
|
+
return column.args?.length ? `NVARCHAR(${column.args[0]})` : "NVARCHAR(255)";
|
|
4080
|
+
case "TEXT":
|
|
4081
|
+
case "text":
|
|
4082
|
+
return "NVARCHAR(MAX)";
|
|
4083
|
+
case "ENUM":
|
|
4084
|
+
case "enum":
|
|
4085
|
+
return "NVARCHAR(255)";
|
|
4086
|
+
default:
|
|
4087
|
+
return String(column.type).toUpperCase();
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
renderDefault(value) {
|
|
4091
|
+
return formatLiteral(value, this.name);
|
|
4092
|
+
}
|
|
4093
|
+
renderAutoIncrement(column) {
|
|
4094
|
+
return column.autoIncrement ? "IDENTITY(1,1)" : void 0;
|
|
4095
|
+
}
|
|
4096
|
+
renderIndex(table, index) {
|
|
4097
|
+
const name = index.name || deriveIndexName(table, index);
|
|
4098
|
+
const cols = renderIndexColumns(this, index.columns);
|
|
4099
|
+
const unique = index.unique ? "UNIQUE " : "";
|
|
4100
|
+
const where = index.where ? ` WHERE ${index.where}` : "";
|
|
4101
|
+
return `CREATE ${unique}INDEX ${this.quoteIdentifier(name)} ON ${this.formatTableName(table)} (${cols})${where};`;
|
|
4102
|
+
}
|
|
4103
|
+
supportsPartialIndexes() {
|
|
4104
|
+
return true;
|
|
4105
|
+
}
|
|
4106
|
+
dropColumnSql(table, column) {
|
|
4107
|
+
return [`ALTER TABLE ${this.formatTableName(table)} DROP COLUMN ${this.quoteIdentifier(column)};`];
|
|
4108
|
+
}
|
|
4109
|
+
dropIndexSql(table, index) {
|
|
4110
|
+
return [`DROP INDEX ${this.quoteIdentifier(index)} ON ${this.formatTableName(table)};`];
|
|
4111
|
+
}
|
|
4112
|
+
};
|
|
4113
|
+
|
|
4114
|
+
// src/core/ddl/schema-generator.ts
|
|
4115
|
+
var escapeLiteral = (value) => value.replace(/'/g, "''");
|
|
4116
|
+
var isRawDefault = (value) => {
|
|
4117
|
+
return !!value && typeof value === "object" && "raw" in value && typeof value.raw === "string";
|
|
4118
|
+
};
|
|
4119
|
+
var formatLiteral = (value, dialect) => {
|
|
4120
|
+
if (isRawDefault(value)) return value.raw;
|
|
4121
|
+
if (value === null) return "NULL";
|
|
4122
|
+
if (typeof value === "number") return Number.isFinite(value) ? String(value) : "NULL";
|
|
4123
|
+
if (typeof value === "boolean") {
|
|
4124
|
+
if (dialect === "mysql" || dialect === "sqlite" || dialect === "mssql") {
|
|
4125
|
+
return value ? "1" : "0";
|
|
4126
|
+
}
|
|
4127
|
+
return value ? "TRUE" : "FALSE";
|
|
4128
|
+
}
|
|
4129
|
+
if (value instanceof Date) return `'${escapeLiteral(value.toISOString())}'`;
|
|
4130
|
+
if (typeof value === "string") return `'${escapeLiteral(value)}'`;
|
|
4131
|
+
return `'${escapeLiteral(JSON.stringify(value))}'`;
|
|
4132
|
+
};
|
|
4133
|
+
var resolvePrimaryKey = (table) => {
|
|
4134
|
+
if (table.primaryKey && table.primaryKey.length > 0) {
|
|
4135
|
+
return table.primaryKey;
|
|
4136
|
+
}
|
|
4137
|
+
const cols = Object.values(table.columns);
|
|
4138
|
+
return cols.filter((c) => c.primary).map((c) => c.name);
|
|
4139
|
+
};
|
|
4140
|
+
var quoteQualified = (dialect, identifier) => {
|
|
4141
|
+
if (identifier.includes(".")) {
|
|
4142
|
+
return identifier.split(".").map((part) => dialect.quoteIdentifier(part)).join(".");
|
|
4143
|
+
}
|
|
4144
|
+
return dialect.quoteIdentifier(identifier);
|
|
4145
|
+
};
|
|
4146
|
+
var renderIndexColumns = (dialect, columns) => {
|
|
4147
|
+
return columns.map((col2) => {
|
|
4148
|
+
if (typeof col2 === "string") return dialect.quoteIdentifier(col2);
|
|
4149
|
+
const parts = [dialect.quoteIdentifier(col2.column)];
|
|
4150
|
+
if (col2.order) parts.push(col2.order);
|
|
4151
|
+
if (col2.nulls) parts.push(`NULLS ${col2.nulls}`);
|
|
4152
|
+
return parts.join(" ");
|
|
4153
|
+
}).join(", ");
|
|
4154
|
+
};
|
|
4155
|
+
var deriveIndexName = (table, index) => {
|
|
4156
|
+
const base = (index.columns || []).map((col2) => typeof col2 === "string" ? col2 : col2.column).join("_");
|
|
4157
|
+
const suffix = index.unique ? "uniq" : "idx";
|
|
4158
|
+
return `${table.name}_${base}_${suffix}`;
|
|
4159
|
+
};
|
|
4160
|
+
var renderColumnDefinition = (table, col2, dialect, options = {}) => {
|
|
4161
|
+
const parts = [];
|
|
4162
|
+
parts.push(dialect.quoteIdentifier(col2.name));
|
|
4163
|
+
parts.push(dialect.renderColumnType(col2));
|
|
4164
|
+
const autoInc = dialect.renderAutoIncrement(col2, table);
|
|
4165
|
+
if (autoInc) parts.push(autoInc);
|
|
4166
|
+
if (col2.notNull) parts.push("NOT NULL");
|
|
4167
|
+
if (col2.unique) parts.push("UNIQUE");
|
|
4168
|
+
if (col2.default !== void 0) {
|
|
4169
|
+
parts.push(`DEFAULT ${dialect.renderDefault(col2.default, col2)}`);
|
|
4170
|
+
}
|
|
4171
|
+
if (options.includePrimary && col2.primary) {
|
|
4172
|
+
parts.push("PRIMARY KEY");
|
|
4173
|
+
}
|
|
4174
|
+
if (col2.check) {
|
|
4175
|
+
parts.push(`CHECK (${col2.check})`);
|
|
4176
|
+
}
|
|
4177
|
+
if (col2.references) {
|
|
4178
|
+
parts.push(dialect.renderReference(col2.references, table));
|
|
4179
|
+
}
|
|
4180
|
+
return { sql: parts.join(" "), inlinePrimary: !!(options.includePrimary && col2.primary) };
|
|
4181
|
+
};
|
|
4182
|
+
var generateCreateTableSql = (table, dialect) => {
|
|
4183
|
+
const pk = resolvePrimaryKey(table);
|
|
4184
|
+
const inlinePkColumns = /* @__PURE__ */ new Set();
|
|
4185
|
+
const columnLines = Object.values(table.columns).map((col2) => {
|
|
4186
|
+
const includePk = dialect.preferInlinePkAutoincrement?.(col2, table, pk) && pk.includes(col2.name);
|
|
4187
|
+
if (includePk) {
|
|
4188
|
+
inlinePkColumns.add(col2.name);
|
|
4189
|
+
}
|
|
4190
|
+
return renderColumnDefinition(table, col2, dialect, { includePrimary: includePk }).sql;
|
|
4191
|
+
});
|
|
4192
|
+
const constraintLines = [];
|
|
4193
|
+
if (pk.length > 0 && !(pk.length === 1 && inlinePkColumns.has(pk[0]))) {
|
|
4194
|
+
const cols = pk.map((c) => dialect.quoteIdentifier(c)).join(", ");
|
|
4195
|
+
constraintLines.push(`PRIMARY KEY (${cols})`);
|
|
4196
|
+
}
|
|
4197
|
+
if (table.checks) {
|
|
4198
|
+
table.checks.forEach((check) => {
|
|
4199
|
+
const name = check.name ? `${dialect.quoteIdentifier(check.name)} ` : "";
|
|
4200
|
+
constraintLines.push(`CONSTRAINT ${name}CHECK (${check.expression})`);
|
|
4201
|
+
});
|
|
4202
|
+
}
|
|
4203
|
+
const allLines = [...columnLines, ...constraintLines];
|
|
4204
|
+
const body = allLines.map((line) => ` ${line}`).join(",\n");
|
|
4205
|
+
const tableOptions = dialect.renderTableOptions(table);
|
|
4206
|
+
const tableSql = `CREATE TABLE ${dialect.formatTableName(table)} (
|
|
4207
|
+
${body}
|
|
4208
|
+
)${tableOptions ? " " + tableOptions : ""};`;
|
|
4209
|
+
const indexSql = [];
|
|
4210
|
+
if (table.indexes && table.indexes.length > 0) {
|
|
4211
|
+
for (const idx of table.indexes) {
|
|
4212
|
+
if (idx.where && !dialect.supportsPartialIndexes()) {
|
|
4213
|
+
throw new Error(`Dialect ${dialect.name} does not support partial/filtered indexes (${idx.name || idx.columns.join("_")}).`);
|
|
4214
|
+
}
|
|
4215
|
+
indexSql.push(dialect.renderIndex(table, idx));
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
return { tableSql, indexSql };
|
|
4219
|
+
};
|
|
4220
|
+
var generateSchemaSql = (tables, dialect) => {
|
|
4221
|
+
const ordered = orderTablesByDependencies(tables);
|
|
4222
|
+
const statements = [];
|
|
4223
|
+
ordered.forEach((table) => {
|
|
4224
|
+
const { tableSql, indexSql } = generateCreateTableSql(table, dialect);
|
|
4225
|
+
statements.push(tableSql, ...indexSql);
|
|
4226
|
+
});
|
|
4227
|
+
return statements;
|
|
4228
|
+
};
|
|
4229
|
+
var orderTablesByDependencies = (tables) => {
|
|
4230
|
+
const map = /* @__PURE__ */ new Map();
|
|
4231
|
+
tables.forEach((t) => map.set(t.name, t));
|
|
4232
|
+
const deps = /* @__PURE__ */ new Map();
|
|
4233
|
+
for (const table of tables) {
|
|
4234
|
+
const refTables = /* @__PURE__ */ new Set();
|
|
4235
|
+
Object.values(table.columns).forEach((col2) => {
|
|
4236
|
+
if (col2.references?.table) {
|
|
4237
|
+
refTables.add(col2.references.table);
|
|
4238
|
+
}
|
|
4239
|
+
});
|
|
4240
|
+
deps.set(table.name, refTables);
|
|
4241
|
+
}
|
|
4242
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4243
|
+
const ordered = [];
|
|
4244
|
+
const visit = (name, stack) => {
|
|
4245
|
+
if (visited.has(name)) return;
|
|
4246
|
+
const table = map.get(name);
|
|
4247
|
+
if (!table) return;
|
|
4248
|
+
if (stack.has(name)) {
|
|
4249
|
+
ordered.push(table);
|
|
4250
|
+
visited.add(name);
|
|
4251
|
+
return;
|
|
4252
|
+
}
|
|
4253
|
+
stack.add(name);
|
|
4254
|
+
for (const dep of deps.get(name) || []) {
|
|
4255
|
+
visit(dep, stack);
|
|
4256
|
+
}
|
|
4257
|
+
stack.delete(name);
|
|
4258
|
+
visited.add(name);
|
|
4259
|
+
ordered.push(table);
|
|
4260
|
+
};
|
|
4261
|
+
tables.forEach((t) => visit(t.name, /* @__PURE__ */ new Set()));
|
|
4262
|
+
return ordered;
|
|
4263
|
+
};
|
|
4264
|
+
|
|
4265
|
+
// src/core/ddl/schema-diff.ts
|
|
4266
|
+
var tableKey = (name, schema) => schema ? `${schema}.${name}` : name;
|
|
4267
|
+
var mapTables = (schema) => {
|
|
4268
|
+
const map = /* @__PURE__ */ new Map();
|
|
4269
|
+
for (const table of schema.tables) {
|
|
4270
|
+
map.set(tableKey(table.name, table.schema), table);
|
|
4271
|
+
}
|
|
4272
|
+
return map;
|
|
4273
|
+
};
|
|
4274
|
+
var buildAddColumnSql = (table, colName, dialect) => {
|
|
4275
|
+
const column = table.columns[colName];
|
|
4276
|
+
const rendered = renderColumnDefinition(table, column, dialect);
|
|
4277
|
+
return `ALTER TABLE ${dialect.formatTableName(table)} ADD ${rendered.sql};`;
|
|
4278
|
+
};
|
|
4279
|
+
var diffSchema = (expectedTables, actualSchema, dialect, options = {}) => {
|
|
4280
|
+
const allowDestructive = options.allowDestructive ?? false;
|
|
4281
|
+
const plan = { changes: [], warnings: [] };
|
|
4282
|
+
const actualMap = mapTables(actualSchema);
|
|
4283
|
+
for (const table of expectedTables) {
|
|
4284
|
+
const key = tableKey(table.name, table.schema);
|
|
4285
|
+
const actual = actualMap.get(key);
|
|
4286
|
+
if (!actual) {
|
|
4287
|
+
const { tableSql, indexSql } = generateCreateTableSql(table, dialect);
|
|
4288
|
+
plan.changes.push({
|
|
4289
|
+
kind: "createTable",
|
|
4290
|
+
table: key,
|
|
4291
|
+
description: `Create table ${key}`,
|
|
4292
|
+
statements: [tableSql, ...indexSql],
|
|
4293
|
+
safe: true
|
|
4294
|
+
});
|
|
4295
|
+
continue;
|
|
4296
|
+
}
|
|
4297
|
+
const actualCols = new Map(actual.columns.map((c) => [c.name, c]));
|
|
4298
|
+
for (const colName of Object.keys(table.columns)) {
|
|
4299
|
+
if (!actualCols.has(colName)) {
|
|
4300
|
+
plan.changes.push({
|
|
4301
|
+
kind: "addColumn",
|
|
4302
|
+
table: key,
|
|
4303
|
+
description: `Add column ${colName} to ${key}`,
|
|
4304
|
+
statements: [buildAddColumnSql(table, colName, dialect)],
|
|
4305
|
+
safe: true
|
|
4306
|
+
});
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
for (const colName of actualCols.keys()) {
|
|
4310
|
+
if (!table.columns[colName]) {
|
|
4311
|
+
plan.changes.push({
|
|
4312
|
+
kind: "dropColumn",
|
|
4313
|
+
table: key,
|
|
4314
|
+
description: `Drop column ${colName} from ${key}`,
|
|
4315
|
+
statements: allowDestructive ? dialect.dropColumnSql(actual, colName) : [],
|
|
4316
|
+
safe: false
|
|
4317
|
+
});
|
|
4318
|
+
const warning = dialect.warnDropColumn?.(actual, colName);
|
|
4319
|
+
if (warning) plan.warnings.push(warning);
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
const expectedIndexes = table.indexes ?? [];
|
|
4323
|
+
const actualIndexes = actual.indexes ?? [];
|
|
4324
|
+
const actualIndexMap = new Map(actualIndexes.map((idx) => [idx.name, idx]));
|
|
4325
|
+
for (const idx of expectedIndexes) {
|
|
4326
|
+
const name = idx.name || deriveIndexName(table, idx);
|
|
4327
|
+
if (!actualIndexMap.has(name)) {
|
|
4328
|
+
plan.changes.push({
|
|
4329
|
+
kind: "addIndex",
|
|
4330
|
+
table: key,
|
|
4331
|
+
description: `Create index ${name} on ${key}`,
|
|
4332
|
+
statements: [dialect.renderIndex(table, { ...idx, name })],
|
|
4333
|
+
safe: true
|
|
4334
|
+
});
|
|
4335
|
+
}
|
|
4336
|
+
}
|
|
4337
|
+
for (const idx of actualIndexes) {
|
|
4338
|
+
if (idx.name && !expectedIndexes.find((expected) => (expected.name || deriveIndexName(table, expected)) === idx.name)) {
|
|
4339
|
+
plan.changes.push({
|
|
4340
|
+
kind: "dropIndex",
|
|
4341
|
+
table: key,
|
|
4342
|
+
description: `Drop index ${idx.name} on ${key}`,
|
|
4343
|
+
statements: allowDestructive ? dialect.dropIndexSql(actual, idx.name) : [],
|
|
4344
|
+
safe: false
|
|
4345
|
+
});
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
}
|
|
4349
|
+
for (const actual of actualSchema.tables) {
|
|
4350
|
+
const key = tableKey(actual.name, actual.schema);
|
|
4351
|
+
if (!expectedTables.find((t) => tableKey(t.name, t.schema) === key)) {
|
|
4352
|
+
plan.changes.push({
|
|
4353
|
+
kind: "dropTable",
|
|
4354
|
+
table: key,
|
|
4355
|
+
description: `Drop table ${key}`,
|
|
4356
|
+
statements: allowDestructive ? dialect.dropTableSql(actual) : [],
|
|
4357
|
+
safe: false
|
|
4358
|
+
});
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
return plan;
|
|
4362
|
+
};
|
|
4363
|
+
var synchronizeSchema = async (expectedTables, actualSchema, dialect, executor, options = {}) => {
|
|
4364
|
+
const plan = diffSchema(expectedTables, actualSchema, dialect, options);
|
|
4365
|
+
if (options.dryRun) return plan;
|
|
4366
|
+
for (const change of plan.changes) {
|
|
4367
|
+
if (!change.statements.length) continue;
|
|
4368
|
+
if (!change.safe && !options.allowDestructive) continue;
|
|
4369
|
+
for (const stmt of change.statements) {
|
|
4370
|
+
if (!stmt.trim()) continue;
|
|
4371
|
+
await executor.executeSql(stmt);
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
return plan;
|
|
4375
|
+
};
|
|
4376
|
+
|
|
4377
|
+
// src/core/ddl/introspect/utils.ts
|
|
4378
|
+
var toRows = (result) => {
|
|
4379
|
+
if (!result) return [];
|
|
4380
|
+
return result.values.map(
|
|
4381
|
+
(row) => result.columns.reduce((acc, col2, idx) => {
|
|
4382
|
+
acc[col2] = row[idx];
|
|
4383
|
+
return acc;
|
|
4384
|
+
}, {})
|
|
4385
|
+
);
|
|
4386
|
+
};
|
|
4387
|
+
var queryRows = async (executor, sql, params = []) => {
|
|
4388
|
+
const [first] = await executor.executeSql(sql, params);
|
|
4389
|
+
return toRows(first);
|
|
4390
|
+
};
|
|
4391
|
+
var shouldIncludeTable = (name, options) => {
|
|
4392
|
+
if (options.includeTables && !options.includeTables.includes(name)) return false;
|
|
4393
|
+
if (options.excludeTables && options.excludeTables.includes(name)) return false;
|
|
4394
|
+
return true;
|
|
4395
|
+
};
|
|
4396
|
+
|
|
4397
|
+
// src/core/ddl/introspect/postgres.ts
|
|
4398
|
+
var postgresIntrospector = {
|
|
4399
|
+
async introspect(executor, options) {
|
|
4400
|
+
const schema = options.schema || "public";
|
|
4401
|
+
const tables = [];
|
|
4402
|
+
const columnRows = await queryRows(
|
|
4403
|
+
executor,
|
|
4404
|
+
`
|
|
4405
|
+
SELECT table_schema, table_name, column_name, data_type, is_nullable, column_default
|
|
4406
|
+
FROM information_schema.columns
|
|
4407
|
+
WHERE table_schema = $1
|
|
4408
|
+
ORDER BY table_name, ordinal_position
|
|
4409
|
+
`,
|
|
4410
|
+
[schema]
|
|
4411
|
+
);
|
|
4412
|
+
const pkRows = await queryRows(
|
|
4413
|
+
executor,
|
|
4414
|
+
`
|
|
4415
|
+
SELECT
|
|
4416
|
+
ns.nspname AS table_schema,
|
|
4417
|
+
tbl.relname AS table_name,
|
|
4418
|
+
array_agg(att.attname ORDER BY arr.idx) AS pk_columns
|
|
4419
|
+
FROM pg_index i
|
|
4420
|
+
JOIN pg_class tbl ON tbl.oid = i.indrelid
|
|
4421
|
+
JOIN pg_namespace ns ON ns.oid = tbl.relnamespace
|
|
4422
|
+
JOIN LATERAL unnest(i.indkey) WITH ORDINALITY AS arr(attnum, idx) ON TRUE
|
|
4423
|
+
LEFT JOIN pg_attribute att ON att.attrelid = tbl.oid AND att.attnum = arr.attnum
|
|
4424
|
+
WHERE i.indisprimary AND ns.nspname = $1
|
|
4425
|
+
GROUP BY ns.nspname, tbl.relname
|
|
4426
|
+
`,
|
|
4427
|
+
[schema]
|
|
4428
|
+
);
|
|
4429
|
+
const pkMap = /* @__PURE__ */ new Map();
|
|
4430
|
+
pkRows.forEach((r) => {
|
|
4431
|
+
pkMap.set(`${r.table_schema}.${r.table_name}`, r.pk_columns || []);
|
|
4432
|
+
});
|
|
4433
|
+
const fkRows = await queryRows(
|
|
4434
|
+
executor,
|
|
4435
|
+
`
|
|
4436
|
+
SELECT
|
|
4437
|
+
tc.table_schema,
|
|
4438
|
+
tc.table_name,
|
|
4439
|
+
kcu.column_name,
|
|
4440
|
+
ccu.table_schema AS foreign_table_schema,
|
|
4441
|
+
ccu.table_name AS foreign_table_name,
|
|
4442
|
+
ccu.column_name AS foreign_column_name,
|
|
4443
|
+
rc.update_rule AS on_update,
|
|
4444
|
+
rc.delete_rule AS on_delete
|
|
4445
|
+
FROM information_schema.table_constraints AS tc
|
|
4446
|
+
JOIN information_schema.key_column_usage AS kcu
|
|
4447
|
+
ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
|
|
4448
|
+
JOIN information_schema.constraint_column_usage AS ccu
|
|
4449
|
+
ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema
|
|
4450
|
+
JOIN information_schema.referential_constraints rc
|
|
4451
|
+
ON rc.constraint_name = tc.constraint_name AND rc.constraint_schema = tc.table_schema
|
|
4452
|
+
WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = $1
|
|
4453
|
+
`,
|
|
4454
|
+
[schema]
|
|
4455
|
+
);
|
|
4456
|
+
const fkMap = /* @__PURE__ */ new Map();
|
|
4457
|
+
fkRows.forEach((r) => {
|
|
4458
|
+
const key = `${r.table_schema}.${r.table_name}.${r.column_name}`;
|
|
4459
|
+
fkMap.set(key, [{
|
|
4460
|
+
table: `${r.foreign_table_schema}.${r.foreign_table_name}`,
|
|
4461
|
+
column: r.foreign_column_name,
|
|
4462
|
+
onDelete: r.on_delete?.toUpperCase(),
|
|
4463
|
+
onUpdate: r.on_update?.toUpperCase()
|
|
4464
|
+
}]);
|
|
4465
|
+
});
|
|
4466
|
+
const indexRows = await queryRows(
|
|
4467
|
+
executor,
|
|
4468
|
+
`
|
|
4469
|
+
SELECT
|
|
4470
|
+
ns.nspname AS table_schema,
|
|
4471
|
+
tbl.relname AS table_name,
|
|
4472
|
+
idx.relname AS index_name,
|
|
4473
|
+
i.indisunique AS is_unique,
|
|
4474
|
+
pg_get_expr(i.indpred, i.indrelid) AS predicate,
|
|
4475
|
+
array_agg(att.attname ORDER BY arr.idx) AS column_names
|
|
4476
|
+
FROM pg_index i
|
|
4477
|
+
JOIN pg_class tbl ON tbl.oid = i.indrelid
|
|
4478
|
+
JOIN pg_namespace ns ON ns.oid = tbl.relnamespace
|
|
4479
|
+
JOIN pg_class idx ON idx.oid = i.indexrelid
|
|
4480
|
+
JOIN LATERAL unnest(i.indkey) WITH ORDINALITY AS arr(attnum, idx) ON TRUE
|
|
4481
|
+
LEFT JOIN pg_attribute att ON att.attrelid = tbl.oid AND att.attnum = arr.attnum
|
|
4482
|
+
WHERE ns.nspname = $1 AND NOT i.indisprimary
|
|
4483
|
+
GROUP BY ns.nspname, tbl.relname, idx.relname, i.indisunique, i.indpred
|
|
4484
|
+
`,
|
|
4485
|
+
[schema]
|
|
4486
|
+
);
|
|
4487
|
+
const tablesByKey = /* @__PURE__ */ new Map();
|
|
4488
|
+
columnRows.forEach((r) => {
|
|
4489
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4490
|
+
if (!shouldIncludeTable(r.table_name, options)) {
|
|
4491
|
+
return;
|
|
4492
|
+
}
|
|
4493
|
+
if (!tablesByKey.has(key)) {
|
|
4494
|
+
tablesByKey.set(key, {
|
|
4495
|
+
name: r.table_name,
|
|
4496
|
+
schema: r.table_schema,
|
|
4497
|
+
columns: [],
|
|
4498
|
+
primaryKey: pkMap.get(key) || [],
|
|
4499
|
+
indexes: []
|
|
4500
|
+
});
|
|
4501
|
+
}
|
|
4502
|
+
const cols = tablesByKey.get(key);
|
|
4503
|
+
const fk = fkMap.get(`${r.table_schema}.${r.table_name}.${r.column_name}`)?.[0];
|
|
4504
|
+
const column = {
|
|
4505
|
+
name: r.column_name,
|
|
4506
|
+
type: r.data_type,
|
|
4507
|
+
notNull: r.is_nullable === "NO",
|
|
4508
|
+
default: r.column_default ?? void 0,
|
|
4509
|
+
references: fk ? {
|
|
4510
|
+
table: fk.table,
|
|
4511
|
+
column: fk.column,
|
|
4512
|
+
onDelete: fk.onDelete,
|
|
4513
|
+
onUpdate: fk.onUpdate
|
|
4514
|
+
} : void 0
|
|
4515
|
+
};
|
|
4516
|
+
cols.columns.push(column);
|
|
4517
|
+
});
|
|
4518
|
+
indexRows.forEach((r) => {
|
|
4519
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4520
|
+
const table = tablesByKey.get(key);
|
|
4521
|
+
if (!table) return;
|
|
4522
|
+
const idx = {
|
|
4523
|
+
name: r.index_name,
|
|
4524
|
+
columns: (r.column_names || []).map((c) => ({ column: c })),
|
|
4525
|
+
unique: !!r.is_unique,
|
|
4526
|
+
where: r.predicate || void 0
|
|
4527
|
+
};
|
|
4528
|
+
table.indexes = table.indexes || [];
|
|
4529
|
+
table.indexes.push(idx);
|
|
4530
|
+
});
|
|
4531
|
+
tables.push(...tablesByKey.values());
|
|
4532
|
+
return { tables };
|
|
4533
|
+
}
|
|
4534
|
+
};
|
|
4535
|
+
|
|
4536
|
+
// src/core/ddl/introspect/mysql.ts
|
|
4537
|
+
var mysqlIntrospector = {
|
|
4538
|
+
async introspect(executor, options) {
|
|
4539
|
+
const schema = options.schema;
|
|
4540
|
+
const filterClause = schema ? "table_schema = ?" : "table_schema = database()";
|
|
4541
|
+
const params = schema ? [schema] : [];
|
|
4542
|
+
const columnRows = await queryRows(
|
|
4543
|
+
executor,
|
|
4544
|
+
`
|
|
4545
|
+
SELECT table_schema, table_name, column_name, data_type, is_nullable, column_default, extra
|
|
4546
|
+
FROM information_schema.columns
|
|
4547
|
+
WHERE ${filterClause}
|
|
4548
|
+
ORDER BY table_name, ordinal_position
|
|
4549
|
+
`,
|
|
4550
|
+
params
|
|
4551
|
+
);
|
|
4552
|
+
const pkRows = await queryRows(
|
|
4553
|
+
executor,
|
|
4554
|
+
`
|
|
4555
|
+
SELECT table_schema, table_name, column_name
|
|
4556
|
+
FROM information_schema.key_column_usage
|
|
4557
|
+
WHERE constraint_name = 'PRIMARY' AND ${filterClause}
|
|
4558
|
+
ORDER BY ordinal_position
|
|
4559
|
+
`,
|
|
4560
|
+
params
|
|
4561
|
+
);
|
|
4562
|
+
const pkMap = /* @__PURE__ */ new Map();
|
|
4563
|
+
pkRows.forEach((r) => {
|
|
4564
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4565
|
+
const list = pkMap.get(key) || [];
|
|
4566
|
+
list.push(r.column_name);
|
|
4567
|
+
pkMap.set(key, list);
|
|
4568
|
+
});
|
|
4569
|
+
const indexRows = await queryRows(
|
|
4570
|
+
executor,
|
|
4571
|
+
`
|
|
4572
|
+
SELECT
|
|
4573
|
+
table_schema,
|
|
4574
|
+
table_name,
|
|
4575
|
+
index_name,
|
|
4576
|
+
non_unique,
|
|
4577
|
+
GROUP_CONCAT(column_name ORDER BY seq_in_index) AS cols
|
|
4578
|
+
FROM information_schema.statistics
|
|
4579
|
+
WHERE ${filterClause} AND index_name <> 'PRIMARY'
|
|
4580
|
+
GROUP BY table_schema, table_name, index_name, non_unique
|
|
4581
|
+
`,
|
|
4582
|
+
params
|
|
4583
|
+
);
|
|
4584
|
+
const tablesByKey = /* @__PURE__ */ new Map();
|
|
4585
|
+
columnRows.forEach((r) => {
|
|
4586
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4587
|
+
if (!shouldIncludeTable(r.table_name, options)) return;
|
|
4588
|
+
if (!tablesByKey.has(key)) {
|
|
4589
|
+
tablesByKey.set(key, {
|
|
4590
|
+
name: r.table_name,
|
|
4591
|
+
schema: r.table_schema,
|
|
4592
|
+
columns: [],
|
|
4593
|
+
primaryKey: pkMap.get(key) || [],
|
|
4594
|
+
indexes: []
|
|
4595
|
+
});
|
|
4596
|
+
}
|
|
4597
|
+
const cols = tablesByKey.get(key);
|
|
4598
|
+
const column = {
|
|
4599
|
+
name: r.column_name,
|
|
4600
|
+
type: r.data_type,
|
|
4601
|
+
notNull: r.is_nullable === "NO",
|
|
4602
|
+
default: r.column_default ?? void 0,
|
|
4603
|
+
autoIncrement: typeof r.extra === "string" && r.extra.includes("auto_increment")
|
|
4604
|
+
};
|
|
4605
|
+
cols.columns.push(column);
|
|
4606
|
+
});
|
|
4607
|
+
indexRows.forEach((r) => {
|
|
4608
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4609
|
+
const table = tablesByKey.get(key);
|
|
4610
|
+
if (!table) return;
|
|
4611
|
+
const cols = (typeof r.cols === "string" ? r.cols.split(",") : []).map((c) => ({ column: c.trim() }));
|
|
4612
|
+
const idx = {
|
|
4613
|
+
name: r.index_name,
|
|
4614
|
+
columns: cols,
|
|
4615
|
+
unique: r.non_unique === 0
|
|
4616
|
+
};
|
|
4617
|
+
table.indexes = table.indexes || [];
|
|
4618
|
+
table.indexes.push(idx);
|
|
4619
|
+
});
|
|
4620
|
+
return { tables: Array.from(tablesByKey.values()) };
|
|
4621
|
+
}
|
|
4622
|
+
};
|
|
4623
|
+
|
|
4624
|
+
// src/core/ddl/introspect/sqlite.ts
|
|
4625
|
+
var escapeSingleQuotes = (name) => name.replace(/'/g, "''");
|
|
4626
|
+
var sqliteIntrospector = {
|
|
4627
|
+
async introspect(executor, options) {
|
|
4628
|
+
const tables = [];
|
|
4629
|
+
const tableRows = await queryRows(
|
|
4630
|
+
executor,
|
|
4631
|
+
`SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%';`
|
|
4632
|
+
);
|
|
4633
|
+
for (const row of tableRows) {
|
|
4634
|
+
const name = row.name;
|
|
4635
|
+
if (!shouldIncludeTable(name, options)) continue;
|
|
4636
|
+
const table = { name, columns: [], primaryKey: [], indexes: [] };
|
|
4637
|
+
const cols = await queryRows(executor, `PRAGMA table_info('${escapeSingleQuotes(name)}');`);
|
|
4638
|
+
cols.forEach((c) => {
|
|
4639
|
+
table.columns.push({
|
|
4640
|
+
name: c.name,
|
|
4641
|
+
type: c.type,
|
|
4642
|
+
notNull: c.notnull === 1,
|
|
4643
|
+
default: c.dflt_value ?? void 0,
|
|
4644
|
+
autoIncrement: false
|
|
4645
|
+
});
|
|
4646
|
+
if (c.pk && c.pk > 0) {
|
|
4647
|
+
table.primaryKey = table.primaryKey || [];
|
|
4648
|
+
table.primaryKey.push(c.name);
|
|
4649
|
+
}
|
|
4650
|
+
});
|
|
4651
|
+
const fkRows = await queryRows(executor, `PRAGMA foreign_key_list('${escapeSingleQuotes(name)}');`);
|
|
4652
|
+
fkRows.forEach((fk) => {
|
|
4653
|
+
const col2 = table.columns.find((c) => c.name === fk.from);
|
|
4654
|
+
if (col2) {
|
|
4655
|
+
col2.references = {
|
|
4656
|
+
table: fk.table,
|
|
4657
|
+
column: fk.to,
|
|
4658
|
+
onDelete: fk.on_delete?.toUpperCase(),
|
|
4659
|
+
onUpdate: fk.on_update?.toUpperCase()
|
|
4660
|
+
};
|
|
4661
|
+
}
|
|
4662
|
+
});
|
|
4663
|
+
const idxList = await queryRows(executor, `PRAGMA index_list('${escapeSingleQuotes(name)}');`);
|
|
4664
|
+
for (const idx of idxList) {
|
|
4665
|
+
const idxName = idx.name;
|
|
4666
|
+
const columnsInfo = await queryRows(executor, `PRAGMA index_info('${escapeSingleQuotes(idxName)}');`);
|
|
4667
|
+
const idxEntry = {
|
|
4668
|
+
name: idxName,
|
|
4669
|
+
columns: columnsInfo.map((ci) => ({ column: ci.name })),
|
|
4670
|
+
unique: idx.unique === 1
|
|
4671
|
+
};
|
|
4672
|
+
table.indexes.push(idxEntry);
|
|
4673
|
+
}
|
|
4674
|
+
tables.push(table);
|
|
4675
|
+
}
|
|
4676
|
+
return { tables };
|
|
4677
|
+
}
|
|
4678
|
+
};
|
|
4679
|
+
|
|
4680
|
+
// src/core/ddl/introspect/mssql.ts
|
|
4681
|
+
var mssqlIntrospector = {
|
|
4682
|
+
async introspect(executor, options) {
|
|
4683
|
+
const schema = options.schema;
|
|
4684
|
+
const filterSchema = schema ? "sch.name = @p1" : "1=1";
|
|
4685
|
+
const params = schema ? [schema] : [];
|
|
4686
|
+
const columnRows = await queryRows(
|
|
4687
|
+
executor,
|
|
4688
|
+
`
|
|
4689
|
+
SELECT
|
|
4690
|
+
sch.name AS table_schema,
|
|
4691
|
+
t.name AS table_name,
|
|
4692
|
+
c.name AS column_name,
|
|
4693
|
+
ty.name AS data_type,
|
|
4694
|
+
c.is_nullable,
|
|
4695
|
+
c.is_identity,
|
|
4696
|
+
object_definition(c.default_object_id) AS column_default
|
|
4697
|
+
FROM sys.columns c
|
|
4698
|
+
JOIN sys.tables t ON t.object_id = c.object_id
|
|
4699
|
+
JOIN sys.schemas sch ON sch.schema_id = t.schema_id
|
|
4700
|
+
JOIN sys.types ty ON ty.user_type_id = c.user_type_id
|
|
4701
|
+
WHERE t.is_ms_shipped = 0 AND ${filterSchema}
|
|
4702
|
+
`,
|
|
4703
|
+
params
|
|
4704
|
+
);
|
|
4705
|
+
const pkRows = await queryRows(
|
|
4706
|
+
executor,
|
|
4707
|
+
`
|
|
4708
|
+
SELECT
|
|
4709
|
+
sch.name AS table_schema,
|
|
4710
|
+
t.name AS table_name,
|
|
4711
|
+
c.name AS column_name,
|
|
4712
|
+
ic.key_ordinal
|
|
4713
|
+
FROM sys.indexes i
|
|
4714
|
+
JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
|
|
4715
|
+
JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
|
|
4716
|
+
JOIN sys.tables t ON t.object_id = i.object_id
|
|
4717
|
+
JOIN sys.schemas sch ON sch.schema_id = t.schema_id
|
|
4718
|
+
WHERE i.is_primary_key = 1 AND ${filterSchema}
|
|
4719
|
+
ORDER BY ic.key_ordinal
|
|
4720
|
+
`,
|
|
4721
|
+
params
|
|
4722
|
+
);
|
|
4723
|
+
const pkMap = /* @__PURE__ */ new Map();
|
|
4724
|
+
pkRows.forEach((r) => {
|
|
4725
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4726
|
+
const list = pkMap.get(key) || [];
|
|
4727
|
+
list.push(r.column_name);
|
|
4728
|
+
pkMap.set(key, list);
|
|
4729
|
+
});
|
|
4730
|
+
const indexRows = await queryRows(
|
|
4731
|
+
executor,
|
|
4732
|
+
`
|
|
4733
|
+
SELECT
|
|
4734
|
+
sch.name AS table_schema,
|
|
4735
|
+
t.name AS table_name,
|
|
4736
|
+
i.name AS index_name,
|
|
4737
|
+
i.is_unique,
|
|
4738
|
+
i.has_filter,
|
|
4739
|
+
i.filter_definition
|
|
4740
|
+
FROM sys.indexes i
|
|
4741
|
+
JOIN sys.tables t ON t.object_id = i.object_id
|
|
4742
|
+
JOIN sys.schemas sch ON sch.schema_id = t.schema_id
|
|
4743
|
+
WHERE i.is_primary_key = 0 AND i.is_hypothetical = 0 AND ${filterSchema}
|
|
4744
|
+
`,
|
|
4745
|
+
params
|
|
4746
|
+
);
|
|
4747
|
+
const indexColsRows = await queryRows(
|
|
4748
|
+
executor,
|
|
4749
|
+
`
|
|
4750
|
+
SELECT
|
|
4751
|
+
sch.name AS table_schema,
|
|
4752
|
+
t.name AS table_name,
|
|
4753
|
+
i.name AS index_name,
|
|
4754
|
+
c.name AS column_name,
|
|
4755
|
+
ic.key_ordinal
|
|
4756
|
+
FROM sys.index_columns ic
|
|
4757
|
+
JOIN sys.indexes i ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
|
4758
|
+
JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
|
|
4759
|
+
JOIN sys.tables t ON t.object_id = i.object_id
|
|
4760
|
+
JOIN sys.schemas sch ON sch.schema_id = t.schema_id
|
|
4761
|
+
WHERE i.is_primary_key = 0 AND ${filterSchema}
|
|
4762
|
+
ORDER BY ic.key_ordinal
|
|
4763
|
+
`,
|
|
4764
|
+
params
|
|
4765
|
+
);
|
|
4766
|
+
const indexColumnsMap = /* @__PURE__ */ new Map();
|
|
4767
|
+
indexColsRows.forEach((r) => {
|
|
4768
|
+
const key = `${r.table_schema}.${r.table_name}.${r.index_name}`;
|
|
4769
|
+
const list = indexColumnsMap.get(key) || [];
|
|
4770
|
+
list.push({ column: r.column_name, order: r.key_ordinal });
|
|
4771
|
+
indexColumnsMap.set(key, list);
|
|
4772
|
+
});
|
|
4773
|
+
const tablesByKey = /* @__PURE__ */ new Map();
|
|
4774
|
+
columnRows.forEach((r) => {
|
|
4775
|
+
if (!shouldIncludeTable(r.table_name, options)) return;
|
|
4776
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4777
|
+
if (!tablesByKey.has(key)) {
|
|
4778
|
+
tablesByKey.set(key, {
|
|
4779
|
+
name: r.table_name,
|
|
4780
|
+
schema: r.table_schema,
|
|
4781
|
+
columns: [],
|
|
4782
|
+
primaryKey: pkMap.get(key) || [],
|
|
4783
|
+
indexes: []
|
|
4784
|
+
});
|
|
4785
|
+
}
|
|
4786
|
+
const t = tablesByKey.get(key);
|
|
4787
|
+
const column = {
|
|
4788
|
+
name: r.column_name,
|
|
4789
|
+
type: r.data_type,
|
|
4790
|
+
notNull: r.is_nullable === false || r.is_nullable === 0,
|
|
4791
|
+
default: r.column_default ?? void 0,
|
|
4792
|
+
autoIncrement: !!r.is_identity
|
|
4793
|
+
};
|
|
4794
|
+
t.columns.push(column);
|
|
4795
|
+
});
|
|
4796
|
+
indexRows.forEach((r) => {
|
|
4797
|
+
const key = `${r.table_schema}.${r.table_name}`;
|
|
4798
|
+
const table = tablesByKey.get(key);
|
|
4799
|
+
if (!table) return;
|
|
4800
|
+
const cols = (indexColumnsMap.get(`${r.table_schema}.${r.table_name}.${r.index_name}`) || []).sort((a, b) => a.order - b.order).map((c) => ({ column: c.column }));
|
|
4801
|
+
const idx = {
|
|
4802
|
+
name: r.index_name,
|
|
4803
|
+
columns: cols,
|
|
4804
|
+
unique: !!r.is_unique,
|
|
4805
|
+
where: r.has_filter ? r.filter_definition : void 0
|
|
4806
|
+
};
|
|
4807
|
+
table.indexes = table.indexes || [];
|
|
4808
|
+
table.indexes.push(idx);
|
|
4809
|
+
});
|
|
4810
|
+
return { tables: Array.from(tablesByKey.values()) };
|
|
4811
|
+
}
|
|
4812
|
+
};
|
|
4813
|
+
|
|
4814
|
+
// src/core/ddl/schema-introspect.ts
|
|
4815
|
+
var INTROSPECTORS = {
|
|
4816
|
+
postgres: postgresIntrospector,
|
|
4817
|
+
mysql: mysqlIntrospector,
|
|
4818
|
+
sqlite: sqliteIntrospector,
|
|
4819
|
+
mssql: mssqlIntrospector
|
|
4820
|
+
};
|
|
4821
|
+
var introspectSchema = async (executor, dialect, options = {}) => {
|
|
4822
|
+
const handler = INTROSPECTORS[dialect];
|
|
4823
|
+
if (!handler) {
|
|
4824
|
+
throw new Error(`Unsupported dialect for introspection: ${dialect}`);
|
|
4825
|
+
}
|
|
4826
|
+
return handler.introspect(executor, options);
|
|
3221
4827
|
};
|
|
3222
4828
|
|
|
3223
4829
|
// src/orm/als.ts
|
|
@@ -3887,9 +5493,13 @@ var UnitOfWork = class {
|
|
|
3887
5493
|
async flushInsert(tracked) {
|
|
3888
5494
|
await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
|
|
3889
5495
|
const payload = this.extractColumns(tracked.table, tracked.entity);
|
|
3890
|
-
|
|
5496
|
+
let builder = new InsertQueryBuilder(tracked.table).values(payload);
|
|
5497
|
+
if (this.dialect.supportsReturning()) {
|
|
5498
|
+
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
5499
|
+
}
|
|
3891
5500
|
const compiled = builder.compile(this.dialect);
|
|
3892
|
-
await this.executeCompiled(compiled);
|
|
5501
|
+
const results = await this.executeCompiled(compiled);
|
|
5502
|
+
this.applyReturningResults(tracked, results);
|
|
3893
5503
|
tracked.status = "managed" /* Managed */;
|
|
3894
5504
|
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
3895
5505
|
tracked.pk = this.getPrimaryKeyValue(tracked);
|
|
@@ -3906,9 +5516,13 @@ var UnitOfWork = class {
|
|
|
3906
5516
|
await this.runHook(tracked.table.hooks?.beforeUpdate, tracked);
|
|
3907
5517
|
const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
|
|
3908
5518
|
if (!pkColumn) return;
|
|
3909
|
-
|
|
5519
|
+
let builder = new UpdateQueryBuilder(tracked.table).set(changes).where(eq(pkColumn, tracked.pk));
|
|
5520
|
+
if (this.dialect.supportsReturning()) {
|
|
5521
|
+
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
5522
|
+
}
|
|
3910
5523
|
const compiled = builder.compile(this.dialect);
|
|
3911
|
-
await this.executeCompiled(compiled);
|
|
5524
|
+
const results = await this.executeCompiled(compiled);
|
|
5525
|
+
this.applyReturningResults(tracked, results);
|
|
3912
5526
|
tracked.status = "managed" /* Managed */;
|
|
3913
5527
|
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
3914
5528
|
this.registerIdentity(tracked);
|
|
@@ -3950,7 +5564,31 @@ var UnitOfWork = class {
|
|
|
3950
5564
|
return payload;
|
|
3951
5565
|
}
|
|
3952
5566
|
async executeCompiled(compiled) {
|
|
3953
|
-
|
|
5567
|
+
return this.executor.executeSql(compiled.sql, compiled.params);
|
|
5568
|
+
}
|
|
5569
|
+
getReturningColumns(table) {
|
|
5570
|
+
return Object.values(table.columns).map((column) => ({
|
|
5571
|
+
type: "Column",
|
|
5572
|
+
table: table.name,
|
|
5573
|
+
name: column.name,
|
|
5574
|
+
alias: column.name
|
|
5575
|
+
}));
|
|
5576
|
+
}
|
|
5577
|
+
applyReturningResults(tracked, results) {
|
|
5578
|
+
if (!this.dialect.supportsReturning()) return;
|
|
5579
|
+
const first = results[0];
|
|
5580
|
+
if (!first || first.values.length === 0) return;
|
|
5581
|
+
const row = first.values[0];
|
|
5582
|
+
for (let i = 0; i < first.columns.length; i++) {
|
|
5583
|
+
const columnName = this.normalizeColumnName(first.columns[i]);
|
|
5584
|
+
if (!(columnName in tracked.table.columns)) continue;
|
|
5585
|
+
tracked.entity[columnName] = row[i];
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5588
|
+
normalizeColumnName(column) {
|
|
5589
|
+
const parts = column.split(".");
|
|
5590
|
+
const candidate = parts[parts.length - 1];
|
|
5591
|
+
return candidate.replace(/^["`[\]]+|["`[\]]+$/g, "");
|
|
3954
5592
|
}
|
|
3955
5593
|
registerIdentity(tracked) {
|
|
3956
5594
|
if (tracked.pk == null) return;
|
|
@@ -3971,22 +5609,46 @@ var UnitOfWork = class {
|
|
|
3971
5609
|
}
|
|
3972
5610
|
};
|
|
3973
5611
|
|
|
5612
|
+
// src/orm/query-logger.ts
|
|
5613
|
+
var createQueryLoggingExecutor = (executor, logger) => {
|
|
5614
|
+
if (!logger) {
|
|
5615
|
+
return executor;
|
|
5616
|
+
}
|
|
5617
|
+
const wrapped = {
|
|
5618
|
+
async executeSql(sql, params) {
|
|
5619
|
+
logger({ sql, params });
|
|
5620
|
+
return executor.executeSql(sql, params);
|
|
5621
|
+
}
|
|
5622
|
+
};
|
|
5623
|
+
if (executor.beginTransaction) {
|
|
5624
|
+
wrapped.beginTransaction = executor.beginTransaction.bind(executor);
|
|
5625
|
+
}
|
|
5626
|
+
if (executor.commitTransaction) {
|
|
5627
|
+
wrapped.commitTransaction = executor.commitTransaction.bind(executor);
|
|
5628
|
+
}
|
|
5629
|
+
if (executor.rollbackTransaction) {
|
|
5630
|
+
wrapped.rollbackTransaction = executor.rollbackTransaction.bind(executor);
|
|
5631
|
+
}
|
|
5632
|
+
return wrapped;
|
|
5633
|
+
};
|
|
5634
|
+
|
|
3974
5635
|
// src/orm/orm-context.ts
|
|
3975
5636
|
var OrmContext = class {
|
|
3976
5637
|
constructor(options) {
|
|
3977
5638
|
this.options = options;
|
|
3978
5639
|
this.identityMap = new IdentityMap();
|
|
3979
5640
|
this.interceptors = [...options.interceptors ?? []];
|
|
5641
|
+
this.executorWithLogging = createQueryLoggingExecutor(options.executor, options.queryLogger);
|
|
3980
5642
|
this.unitOfWork = new UnitOfWork(
|
|
3981
5643
|
options.dialect,
|
|
3982
|
-
|
|
5644
|
+
this.executorWithLogging,
|
|
3983
5645
|
this.identityMap,
|
|
3984
5646
|
() => this
|
|
3985
5647
|
);
|
|
3986
5648
|
this.relationChanges = new RelationChangeProcessor(
|
|
3987
5649
|
this.unitOfWork,
|
|
3988
5650
|
options.dialect,
|
|
3989
|
-
|
|
5651
|
+
this.executorWithLogging
|
|
3990
5652
|
);
|
|
3991
5653
|
this.domainEvents = new DomainEventBus(options.domainEventHandlers);
|
|
3992
5654
|
}
|
|
@@ -3994,7 +5656,7 @@ var OrmContext = class {
|
|
|
3994
5656
|
return this.options.dialect;
|
|
3995
5657
|
}
|
|
3996
5658
|
get executor() {
|
|
3997
|
-
return this.
|
|
5659
|
+
return this.executorWithLogging;
|
|
3998
5660
|
}
|
|
3999
5661
|
get identityBuckets() {
|
|
4000
5662
|
return this.unitOfWork.identityBuckets;
|
|
@@ -4057,15 +5719,21 @@ var OrmContext = class {
|
|
|
4057
5719
|
};
|
|
4058
5720
|
export {
|
|
4059
5721
|
AsyncLocalStorage,
|
|
5722
|
+
BaseSchemaDialect,
|
|
4060
5723
|
DefaultBelongsToReference,
|
|
4061
5724
|
DefaultHasManyCollection,
|
|
4062
5725
|
DefaultManyToManyCollection,
|
|
4063
5726
|
DeleteQueryBuilder,
|
|
4064
5727
|
EntityStatus,
|
|
4065
5728
|
InsertQueryBuilder,
|
|
5729
|
+
MSSqlSchemaDialect,
|
|
4066
5730
|
MySqlDialect,
|
|
5731
|
+
MySqlSchemaDialect,
|
|
4067
5732
|
OrmContext,
|
|
5733
|
+
PostgresDialect,
|
|
5734
|
+
PostgresSchemaDialect,
|
|
4068
5735
|
RelationKinds,
|
|
5736
|
+
SQLiteSchemaDialect,
|
|
4069
5737
|
SelectQueryBuilder,
|
|
4070
5738
|
SqlServerDialect,
|
|
4071
5739
|
SqliteDialect,
|
|
@@ -4087,15 +5755,22 @@ export {
|
|
|
4087
5755
|
createLiteral,
|
|
4088
5756
|
defineTable,
|
|
4089
5757
|
denseRank,
|
|
5758
|
+
deriveIndexName,
|
|
5759
|
+
diffSchema,
|
|
4090
5760
|
eq,
|
|
5761
|
+
escapeLiteral,
|
|
4091
5762
|
executeHydrated,
|
|
4092
5763
|
exists,
|
|
4093
5764
|
firstValue,
|
|
5765
|
+
formatLiteral,
|
|
5766
|
+
generateCreateTableSql,
|
|
5767
|
+
generateSchemaSql,
|
|
4094
5768
|
gt,
|
|
4095
5769
|
gte,
|
|
4096
5770
|
hasMany,
|
|
4097
5771
|
hydrateRows,
|
|
4098
5772
|
inList,
|
|
5773
|
+
introspectSchema,
|
|
4099
5774
|
isCaseExpressionNode,
|
|
4100
5775
|
isExpressionSelectionNode,
|
|
4101
5776
|
isFunctionNode,
|
|
@@ -4120,9 +5795,14 @@ export {
|
|
|
4120
5795
|
notLike,
|
|
4121
5796
|
ntile,
|
|
4122
5797
|
or,
|
|
5798
|
+
quoteQualified,
|
|
4123
5799
|
rank,
|
|
5800
|
+
renderColumnDefinition,
|
|
5801
|
+
renderIndexColumns,
|
|
5802
|
+
resolvePrimaryKey,
|
|
4124
5803
|
rowNumber,
|
|
4125
5804
|
sum,
|
|
5805
|
+
synchronizeSchema,
|
|
4126
5806
|
valueToOperand,
|
|
4127
5807
|
visitExpression,
|
|
4128
5808
|
visitOperand,
|